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:
@ -1587,7 +1587,16 @@ fn profile_with_mcp(id: ProfileId, config: McpConfigStrategy) -> AgentProfile {
|
||||
fn opencode_profile(id: ProfileId) -> AgentProfile {
|
||||
profile(id, ContextInjection::convention_file("AGENTS.md").unwrap())
|
||||
.with_structured_adapter(StructuredAdapter::OpenCode)
|
||||
.with_opencode(OpenCodeConfig::new(Some("ollama/qwen3-coder:30b".to_owned())).unwrap())
|
||||
.with_opencode(
|
||||
OpenCodeConfig::new(
|
||||
"http://localhost:8080/v1",
|
||||
Some("sk-no-key".to_owned()),
|
||||
"qwen3-coder-30b",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.with_mcp(McpCapability::new(
|
||||
McpConfigStrategy::open_code_config("opencode.json").unwrap(),
|
||||
McpTransport::Stdio,
|
||||
@ -1730,10 +1739,34 @@ async fn launch_opencode_writes_isolated_config_and_env() {
|
||||
assert_eq!(opencode.len(), 1, "one OpenCode config is written");
|
||||
assert_eq!(opencode[0].0, format!("{run_dir}/opencode.json"));
|
||||
let body: serde_json::Value = serde_json::from_slice(&opencode[0].1).unwrap();
|
||||
assert_eq!(body["model"], "ollama/qwen3-coder:30b");
|
||||
assert_eq!(body["model"], "llamacpp/qwen3-coder-30b");
|
||||
assert_eq!(
|
||||
body["provider"]["ollama"]["options"]["baseURL"],
|
||||
"http://localhost:11434/v1"
|
||||
body["provider"]["llamacpp"]["options"]["baseURL"],
|
||||
"http://localhost:8080/v1"
|
||||
);
|
||||
assert_eq!(
|
||||
body["provider"]["llamacpp"]["options"]["apiKey"],
|
||||
"sk-no-key"
|
||||
);
|
||||
assert_eq!(
|
||||
body["provider"]["llamacpp"]["models"]["qwen3-coder-30b"]["name"],
|
||||
"llamacpp/qwen3-coder-30b"
|
||||
);
|
||||
assert_eq!(
|
||||
body["provider"]["llamacpp"]["models"]["qwen3-coder-30b"]["tool_call"],
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
body["provider"]["llamacpp"]["models"]["qwen3-coder-30b"]["reasoning"],
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
body["provider"]["llamacpp"]["models"]["qwen3-coder-30b"]["attachment"],
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
body["disabled_providers"],
|
||||
serde_json::json!(["anthropic", "openai", "gemini", "ollama"])
|
||||
);
|
||||
assert_eq!(body["mcp"]["idea"]["type"], "local");
|
||||
assert_eq!(body["mcp"]["idea"]["command"][0], "idea");
|
||||
@ -2496,6 +2529,50 @@ async fn launch_with_invalid_uuid_conversation_id_omits_resume_section() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn launch_opencode_omits_api_key_when_profile_has_none() {
|
||||
let profile = profile(pid(91), ContextInjection::convention_file("AGENTS.md").unwrap())
|
||||
.with_structured_adapter(StructuredAdapter::OpenCode)
|
||||
.with_opencode(
|
||||
OpenCodeConfig::new(
|
||||
"http://localhost:8080/v1",
|
||||
None,
|
||||
"qwen3-coder-30b",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.with_mcp(McpCapability::new(
|
||||
McpConfigStrategy::open_code_config("opencode.json").unwrap(),
|
||||
McpTransport::Stdio,
|
||||
));
|
||||
let (launch, agent, fs, _pty, _bus, _sessions, _tr, _session) = launch_fixture_with_profile(
|
||||
profile,
|
||||
Some(ContextInjectionPlan::File {
|
||||
target: "AGENTS.md".to_owned(),
|
||||
}),
|
||||
);
|
||||
|
||||
launch.execute(launch_input(agent.id)).await.unwrap();
|
||||
|
||||
let opencode = writes_ending_with(&fs, "/opencode.json");
|
||||
assert_eq!(opencode.len(), 1, "one OpenCode config is written");
|
||||
let body: serde_json::Value = serde_json::from_slice(&opencode[0].1).unwrap();
|
||||
assert_eq!(
|
||||
body["provider"]["llamacpp"]["options"]["baseURL"],
|
||||
"http://localhost:8080/v1"
|
||||
);
|
||||
assert!(
|
||||
body["provider"]["llamacpp"]["options"]
|
||||
.as_object()
|
||||
.expect("llamacpp options are an object")
|
||||
.get("apiKey")
|
||||
.is_none(),
|
||||
"apiKey must be omitted, not serialized as null"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn launch_with_store_without_handoff_omits_resume_section() {
|
||||
// Provider wired, valid conversation_id, but the store holds NO handoff for it
|
||||
|
||||
Reference in New Issue
Block a user