fix(claw): carry a kind's stored route into construction
This commit is contained in:
1 parent
ddb26bf34d
commit
05045e4ee5
1 file changed
+63
-4
@@ -355,7 +355,8 @@ pub struct ProviderParams {
|
|||||||
/// The API key (or GitHub token for copilot).
|
/// The API key (or GitHub token for copilot).
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub api_key: Option<String>,
|
pub api_key: Option<String>,
|
||||||
/// The server address (llama.cpp).
|
/// The server address (llama.cpp) or stored route override
|
||||||
|
/// (OpenAI-compatible, Z.ai, OpenCode).
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
}
|
}
|
||||||
@@ -390,7 +391,8 @@ pub struct ParamField {
|
|||||||
/// shape under `addable` the UI builds its inputs from. `required`
|
/// shape under `addable` the UI builds its inputs from. `required`
|
||||||
/// states what the daemon demands, not what the form shows: llama.cpp
|
/// states what the daemon demands, not what the form shows: llama.cpp
|
||||||
/// serves keyless on a default url, so neither of its fields gates the
|
/// serves keyless on a default url, so neither of its fields gates the
|
||||||
/// login; the six keyed kinds cannot construct without an `api_key`.
|
/// login; the keyed kinds cannot construct without an `api_key`.
|
||||||
|
/// OpenCode's optional url chooses its pay-as-you-go or Go route.
|
||||||
pub fn param_fields_for(kind: &str) -> &'static [ParamField] {
|
pub fn param_fields_for(kind: &str) -> &'static [ParamField] {
|
||||||
match kind {
|
match kind {
|
||||||
"llama-cpp" => &[
|
"llama-cpp" => &[
|
||||||
@@ -403,6 +405,16 @@ pub fn param_fields_for(kind: &str) -> &'static [ParamField] {
|
|||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"opencode" => &[
|
||||||
|
ParamField {
|
||||||
|
name: "url",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
ParamField {
|
||||||
|
name: "api_key",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
_ => &[ParamField {
|
_ => &[ParamField {
|
||||||
name: "api_key",
|
name: "api_key",
|
||||||
required: true,
|
required: true,
|
||||||
@@ -425,6 +437,14 @@ pub fn construct(kind: &str, params: &ProviderParams) -> Result<Backend, Backend
|
|||||||
.unwrap_or_else(|| rhema_config::Profile::default().llama_cpp_url());
|
.unwrap_or_else(|| rhema_config::Profile::default().llama_cpp_url());
|
||||||
let built = match kind {
|
let built = match kind {
|
||||||
"llama-cpp" => build(kind, params.api_key.as_deref(), Some(&llama_url), None),
|
"llama-cpp" => build(kind, params.api_key.as_deref(), Some(&llama_url), None),
|
||||||
|
// The route-bearing kinds take an optional stored override; the
|
||||||
|
// rest keep their adapter's default.
|
||||||
|
"openai" | "zai" | "opencode" => build(
|
||||||
|
kind,
|
||||||
|
Some(require_key(kind, params)?.as_str()),
|
||||||
|
None,
|
||||||
|
params.url.as_deref(),
|
||||||
|
),
|
||||||
_ => build(kind, Some(require_key(kind, params)?.as_str()), None, None),
|
_ => build(kind, Some(require_key(kind, params)?.as_str()), None, None),
|
||||||
}
|
}
|
||||||
.map_err(|err| match err {
|
.map_err(|err| match err {
|
||||||
@@ -488,6 +508,16 @@ pub fn discover(
|
|||||||
.filter(|key| !key.trim().is_empty())
|
.filter(|key| !key.trim().is_empty())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The stored route override a kind's profile instance carries —
|
||||||
|
// OpenCode's Go/pay-as-you-go choice, Z.ai's proxy, an
|
||||||
|
// OpenAI-compatible server. `None` lets the adapter keep its own
|
||||||
|
// default.
|
||||||
|
let stored_route = |kind: &str| {
|
||||||
|
stored
|
||||||
|
.instance_for_provider(kind)
|
||||||
|
.and_then(|(_, instance)| stored.instance_url(instance))
|
||||||
|
};
|
||||||
|
|
||||||
// Stored instances first, under the id their row names. An
|
// Stored instances first, under the id their row names. An
|
||||||
// env-backed kind takes the id back from a stored twin of the same
|
// env-backed kind takes the id back from a stored twin of the same
|
||||||
// id below — env wins the per-kind slot, the row stays persisted.
|
// id below — env wins the per-kind slot, the row stays persisted.
|
||||||
@@ -531,7 +561,7 @@ pub fn discover(
|
|||||||
ProviderParams {
|
ProviderParams {
|
||||||
kind: kind.to_owned(),
|
kind: kind.to_owned(),
|
||||||
api_key: Some(api_key),
|
api_key: Some(api_key),
|
||||||
url: None,
|
url: stored_route(kind),
|
||||||
}
|
}
|
||||||
} else if stored_of_kind {
|
} else if stored_of_kind {
|
||||||
// The stored instance of the kind wins over the profile
|
// The stored instance of the kind wins over the profile
|
||||||
@@ -548,7 +578,7 @@ pub fn discover(
|
|||||||
ProviderParams {
|
ProviderParams {
|
||||||
kind: kind.to_owned(),
|
kind: kind.to_owned(),
|
||||||
api_key,
|
api_key,
|
||||||
url: None,
|
url: stored_route(kind),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let has_creds = match kind {
|
let has_creds = match kind {
|
||||||
@@ -736,6 +766,18 @@ mod tests {
|
|||||||
assert!(construct("google", ¶ms("google", Some("k"), None)).is_ok());
|
assert!(construct("google", ¶ms("google", Some("k"), None)).is_ok());
|
||||||
assert!(construct("deepseek", ¶ms("deepseek", Some("k"), None)).is_ok());
|
assert!(construct("deepseek", ¶ms("deepseek", Some("k"), None)).is_ok());
|
||||||
assert!(construct("zai", ¶ms("zai", Some("k"), None)).is_ok());
|
assert!(construct("zai", ¶ms("zai", Some("k"), None)).is_ok());
|
||||||
|
assert!(construct("opencode", ¶ms("opencode", Some("k"), None)).is_ok());
|
||||||
|
assert!(
|
||||||
|
construct(
|
||||||
|
"opencode",
|
||||||
|
¶ms(
|
||||||
|
"opencode",
|
||||||
|
Some("k"),
|
||||||
|
Some("https://opencode.ai/inference/go/openai/v1")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
assert!(construct("copilot", ¶ms("copilot", Some("t"), None)).is_ok());
|
assert!(construct("copilot", ¶ms("copilot", Some("t"), None)).is_ok());
|
||||||
assert!(construct("nope", ¶ms("nope", Some("k"), None)).is_err());
|
assert!(construct("nope", ¶ms("nope", Some("k"), None)).is_err());
|
||||||
// llama.cpp serves keyless on its default url.
|
// llama.cpp serves keyless on its default url.
|
||||||
@@ -743,6 +785,23 @@ mod tests {
|
|||||||
assert!(construct("llama-cpp", ¶ms("llama-cpp", None, Some("http://h:1"))).is_ok());
|
assert!(construct("llama-cpp", ¶ms("llama-cpp", None, Some("http://h:1"))).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn opencode_offers_an_optional_url_beside_its_key() {
|
||||||
|
assert_eq!(
|
||||||
|
param_fields_for("opencode"),
|
||||||
|
&[
|
||||||
|
ParamField {
|
||||||
|
name: "url",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
ParamField {
|
||||||
|
name: "api_key",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn discover_builds_exactly_the_kinds_with_a_source() {
|
fn discover_builds_exactly_the_kinds_with_a_source() {
|
||||||
let config = test_config();
|
let config = test_config();
|
||||||
|
|||||||
Reference in new issue
Block a user