feat(opencode): remplace le provider Ollama par llama.cpp
Le tool-calling local ne fonctionnait jamais via Ollama. Refonte du support local d'OpenCode autour de llama.cpp: profil, catalogue, matérialisation de la config OpenCode et surface first-run alignés sur llama-server (backend + frontend). QA vert (commandes réelles): domain 244, application 81+64, infra 263 (10 échecs = bind-port sandbox identiques sur develop, non-régression), frontend 574, tsc propre. Réserve E2E live non bloquante faute de llama-server joignable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -2292,6 +2292,9 @@ impl LaunchAgent {
|
||||
if profile.structured_adapter != Some(StructuredAdapter::OpenCode) {
|
||||
return;
|
||||
}
|
||||
let Some(opencode) = profile.opencode.as_ref() else {
|
||||
return;
|
||||
};
|
||||
let config_path = join(run_dir, target);
|
||||
let opencode_home = join(run_dir, ".opencode");
|
||||
let xdg_config = format!("{opencode_home}/config");
|
||||
@ -2307,7 +2310,7 @@ impl LaunchAgent {
|
||||
.await;
|
||||
}
|
||||
let body =
|
||||
opencode_config_json(profile, project_root.as_str(), runtime).to_string();
|
||||
opencode_config_json(opencode, project_root.as_str(), runtime).to_string();
|
||||
let _ = self
|
||||
.fs
|
||||
.write(&RemotePath::new(config_path.clone()), body.as_bytes())
|
||||
@ -2474,43 +2477,52 @@ fn mcp_server_wiring(
|
||||
/// The MCP server is named `idea`; OpenCode therefore exposes IdeA tools as
|
||||
/// `idea_idea_*` (`<serverName>_<toolName>`), matching the observed contract.
|
||||
fn opencode_config_json(
|
||||
profile: &AgentProfile,
|
||||
config: &domain::profile::OpenCodeConfig,
|
||||
project_root: &str,
|
||||
runtime: Option<&McpRuntime>,
|
||||
) -> serde_json::Value {
|
||||
let model = profile
|
||||
.opencode
|
||||
.as_ref()
|
||||
.and_then(|config| config.model.as_deref());
|
||||
let model = config.model.as_str();
|
||||
let opencode_model = format!("llamacpp/{model}");
|
||||
let mut root = serde_json::Map::new();
|
||||
root.insert(
|
||||
"$schema".to_owned(),
|
||||
serde_json::Value::String("https://opencode.ai/config.json".to_owned()),
|
||||
);
|
||||
if let Some(model) = model {
|
||||
root.insert(
|
||||
"model".to_owned(),
|
||||
serde_json::Value::String(model.to_owned()),
|
||||
root.insert(
|
||||
"model".to_owned(),
|
||||
serde_json::Value::String(opencode_model.clone()),
|
||||
);
|
||||
|
||||
let mut options = serde_json::Map::new();
|
||||
options.insert(
|
||||
"baseURL".to_owned(),
|
||||
serde_json::Value::String(config.base_url.clone()),
|
||||
);
|
||||
if let Some(api_key) = config.api_key.as_ref() {
|
||||
options.insert(
|
||||
"apiKey".to_owned(),
|
||||
serde_json::Value::String(api_key.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
let mut models = serde_json::Map::new();
|
||||
if let Some(model) = model {
|
||||
models.insert(
|
||||
model.trim_start_matches("ollama/").to_owned(),
|
||||
serde_json::json!({ "name": model }),
|
||||
);
|
||||
}
|
||||
models.insert(
|
||||
model.to_owned(),
|
||||
serde_json::json!({
|
||||
"name": opencode_model,
|
||||
"tool_call": true,
|
||||
"reasoning": config.reasoning_enabled(),
|
||||
"attachment": config.attachment_enabled()
|
||||
}),
|
||||
);
|
||||
|
||||
root.insert(
|
||||
"provider".to_owned(),
|
||||
serde_json::json!({
|
||||
"ollama": {
|
||||
"llamacpp": {
|
||||
"npm": "@ai-sdk/openai-compatible",
|
||||
"name": "Ollama (local)",
|
||||
"options": {
|
||||
"baseURL": "http://localhost:11434/v1"
|
||||
},
|
||||
"name": "llama.cpp",
|
||||
"options": options,
|
||||
"models": models
|
||||
}
|
||||
}),
|
||||
@ -2556,7 +2568,7 @@ fn opencode_config_json(
|
||||
);
|
||||
root.insert(
|
||||
"disabled_providers".to_owned(),
|
||||
serde_json::json!(["anthropic", "openai", "gemini"]),
|
||||
serde_json::json!(["anthropic", "openai", "gemini", "ollama"]),
|
||||
);
|
||||
serde_json::Value::Object(root)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user