feat(13-10): Ollama tool-calling backend, first in the D-04 chain

backends/ollama.rs implements the Backend trait against Ollama's
POST /api/chat (messages + tools arrays, message.tool_calls response) --
never mesh/listener/assist.rs::call_ollama's older single-shot prompt
endpoint, which has no tool-calling support at all. Ollama's per-call
tool-call ids (absent on the wire) are synthesized; its already-parsed
function.arguments object is passed through without a second string-parse
(the OpenAI-shape normalization would be wrong here). Every request sets
an explicit generation-length cap and runs non-streaming.

model_supports_tools queries Ollama's /api/show and caches the answer for
the process lifetime, turning AI-SPEC's [ASSUMED] note about
qwen2.5-coder's tool capability into a runtime fact: a non-tool-capable or
unreachable Ollama falls through to Claude with a logged reason, never a
silent tools-free degrade.

select_backend (backends/mod.rs) is now async and reuses the existing
detect_ollama() probe (mesh::assistant, bumped to pub(crate) for this
reuse) rather than re-probing. A new FallbackChain wraps the Ollama leg so
a transport error mid-turn falls through to Claude for that same call
instead of failing the turn outright.

13 new tests under assistant::backends::{ollama,}::tests::, exercised
against a local hyper-based HTTP stub (no mock-HTTP crate exists in this
workspace). Full assistant:: suite: 42/42 (29 baseline + 13 new).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-05 17:42:24 -04:00
co-authored by Claude Fable 5
parent a245e5d29f
commit 821d8700ce
6 changed files with 817 additions and 15 deletions
+6 -3
View File
@@ -19,7 +19,11 @@ mod identity;
mod interfaces;
pub(crate) mod lnd;
mod marketplace;
mod mesh;
// pub(crate): 13-10's `assistant::backends::select_backend` reuses
// `mesh::assistant::detect_ollama()` (D-04) rather than re-probing —
// matches the existing `pub(crate) mod bitcoin_relay;`/`pub(crate) mod
// lnd;` convention already in this file for cross-module reuse.
pub(crate) mod mesh;
mod middleware;
mod monitoring;
mod music;
@@ -69,8 +73,7 @@ pub use middleware::PeerAddr;
// "this module" to "this crate".
pub(crate) use middleware::UNAUTHENTICATED_METHODS;
use middleware::{
derive_csrf_token, extract_client_ip, extract_cookie, sanitize_error_message,
CACHEABLE_METHODS,
derive_csrf_token, extract_client_ip, extract_cookie, sanitize_error_message, CACHEABLE_METHODS,
};
use response::{cookie_header, json_response, ResponseCache, RpcError, RpcRequest, RpcResponse};