feat(arr): make the OpenAI endpoint a live setting
The base URL and model move into a cell the backend re-reads per request, so an operator can repoint it without a restart. Migration 0028 adds the two columns; DESIGN.md §15 calls both database rows. Construction never depends on the API key — llama.cpp serves without one.
This commit is contained in:
@@ -16,7 +16,7 @@ use std::time::Duration;
|
||||
|
||||
use arr_core::Language;
|
||||
use arr_subs::translate::{translate, Backend, Error};
|
||||
use arr_subs::{OpenAi, OpenAiConfig};
|
||||
use arr_subs::{OpenAi, OpenAiConfig, OpenAiEndpoint};
|
||||
use wiremock::matchers::{header, method, path};
|
||||
use wiremock::{Mock, MockServer, ResponseTemplate};
|
||||
|
||||
@@ -319,9 +319,60 @@ async fn an_unsupported_target_is_refused_before_any_request() {
|
||||
assert!(matches!(error, Error::UnsupportedTarget { .. }));
|
||||
}
|
||||
|
||||
/// #220: an edit of `openai_base_url` / `openai_model` reaches the running
|
||||
/// backend without a restart — the next batch goes to the new endpoint and
|
||||
/// names the new model, with no reconstruction in between.
|
||||
#[tokio::test]
|
||||
async fn repointing_the_endpoint_moves_the_next_batch() {
|
||||
let first = MockServer::start().await;
|
||||
let second = MockServer::start().await;
|
||||
mount_reply(
|
||||
&first,
|
||||
ResponseTemplate::new(200).set_body_string(chat_response(
|
||||
r#""[{\"number\": 1, \"text\": \"do primeiro\"}]""#,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
mount_reply(
|
||||
&second,
|
||||
ResponseTemplate::new(200).set_body_string(chat_response(
|
||||
r#""[{\"number\": 1, \"text\": \"do segundo\"}]""#,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
let endpoint = OpenAiEndpoint::new(Some(&format!("{}/v1/", first.uri())), Some("first-model"))
|
||||
.expect("endpoint resolves");
|
||||
let backend =
|
||||
OpenAi::with_endpoint(OpenAiConfig { api_key: None }, endpoint.clone()).expect("builds");
|
||||
|
||||
let cues = vec![arr_subs::translate::BatchCue {
|
||||
number: 1,
|
||||
text: "from the first".to_owned(),
|
||||
}];
|
||||
let batch = arr_subs::translate::Batch {
|
||||
source: Language::Other("en".to_owned()),
|
||||
target: Language::PortuguesePortugal,
|
||||
cues: cues.clone(),
|
||||
};
|
||||
let out = backend.translate(&batch).await.expect("first batch");
|
||||
assert_eq!(out[0].text, "do primeiro");
|
||||
|
||||
endpoint
|
||||
.set(Some(&format!("{}/v1/", second.uri())), Some("second-model"))
|
||||
.expect("repoint");
|
||||
let out = backend.translate(&batch).await.expect("second batch");
|
||||
assert_eq!(out[0].text, "do segundo");
|
||||
|
||||
let sent = second.received_requests().await.expect("requests recorded");
|
||||
let body: serde_json::Value =
|
||||
serde_json::from_slice(&sent.last().expect("one request").body).expect("json body");
|
||||
assert_eq!(body["model"], "second-model");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_backend_answers_to_openai() {
|
||||
let id = OpenAi::new("gpt-4o-mini", OpenAiConfig { api_key: None })
|
||||
let id = OpenAi::new(OpenAiConfig { api_key: None })
|
||||
.expect("client builds")
|
||||
.id();
|
||||
assert_eq!(id.as_str(), "openai");
|
||||
|
||||
Reference in New Issue
Block a user