feat(session): adapter HTTP OpenAI-compatible pour profils locaux/LAN (#14)

Ajoute un adapter de session HTTP OpenAI-compatible, purement additif,
permettant d'intégrer des modèles locaux/LAN comme profils IdeA canoniques
avec parité tool-calling/MCP.

- domain: extension du profil et des ports pour l'adapter OpenAI-compatible
- infrastructure: adapter openai_compat + routage factory
- app-tauri: mapping des outils OpenAI (openai_tools) + wiring state/lib
- application: catalogue d'agents + tests de use-cases profils

Validé QA (backend GO): round-trip byte-identique Claude/Codex, mapping
erreurs, dégradation tools, conversation_id None, conformance un seul Final,
routage factory. Suites vertes domain 467 / application 530 /
infrastructure 523 / app-tauri 247, 0 échec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 22:09:16 +02:00
parent cb20fabdf4
commit aab4bcafb6
14 changed files with 1794 additions and 25 deletions

View File

@ -20,7 +20,7 @@
use domain::ids::ProfileId;
use domain::permission::ProjectorKey;
use domain::profile::{
AgentProfile, ContextInjection, McpCapability, McpConfigStrategy, McpTransport,
AgentProfile, ContextInjection, HttpChatConfig, McpCapability, McpConfigStrategy, McpTransport,
StructuredAdapter,
};
@ -96,6 +96,30 @@ pub fn reference_profiles() -> Vec<AgentProfile> {
.expect(".codex/config.toml + CODEX_HOME is a valid MCP config target"),
McpTransport::Stdio,
)),
AgentProfile::new(
reference_id("ollama-openai-compatible"),
"Ollama / OpenAI-compatible local model",
"openai-compatible",
Vec::new(),
ContextInjection::convention_file("AGENTS.md")
.expect("AGENTS.md is a valid convention target"),
None,
"{agentRunDir}",
None,
)
.expect("OpenAI-compatible reference profile is valid")
.with_structured_adapter(StructuredAdapter::OpenAiCompatible)
.with_chat_http(
HttpChatConfig::new(
"http://localhost:11434/v1",
"qwen2.5-coder",
None,
Some(120_000),
Some(5_000),
Some(HttpChatConfig::DEFAULT_MAX_TOOL_ITERATIONS),
)
.expect("OpenAI-compatible HTTP config is valid"),
),
AgentProfile::new(
reference_id("gemini"),
"Gemini CLI",
@ -129,8 +153,9 @@ pub fn reference_profiles() -> Vec<AgentProfile> {
///
/// A profile is selectable iff it can be driven in **structured** mode
/// ([`AgentProfile::is_selectable`] = it carries a `structured_adapter`). Today
/// that is Claude + Codex; Gemini/Aider stay in [`reference_profiles`] (the data
/// catalogue is untouched) but are **not** proposed for selection. There is no
/// that is Claude + Codex + OpenAI-compatible; Gemini/Aider stay in
/// [`reference_profiles`] (the data catalogue is untouched) but are **not**
/// proposed for selection. There is no
/// custom-profile entry here either: the selection path offers only profiles we
/// know how to pilot.
///
@ -201,6 +226,20 @@ mod mcp_tests {
);
}
#[test]
fn openai_compatible_seed_is_selectable_http_profile_without_mcp() {
let profile = profile("ollama-openai-compatible");
assert_eq!(
profile.structured_adapter,
Some(StructuredAdapter::OpenAiCompatible)
);
let chat = profile.chat_http.as_ref().expect("chatHttp present");
assert_eq!(chat.endpoint, "http://localhost:11434/v1");
assert_eq!(chat.model, "qwen2.5-coder");
assert!(profile.mcp.is_none());
assert!(profile.is_selectable());
}
#[test]
fn gemini_and_aider_have_no_mcp_capability() {
for slug in ["gemini", "aider"] {