Add cloud API support and per-agent model override

Cloud API support:
- Add chat_api config flag to BackendConfig, threaded through
  SessionConfig → ResolvedModel → Agent → Mind
- New StreamToken::TextDelta variant for chat completions streaming
- stream_chat_completion() method on ApiClient: builds messages array,
  sends to /v1/chat/completions, parses SSE stream
- ChatMessage struct and wire_messages() on ContextState: converts the
  AST (system/identity/journal/conversation nodes) into a messages
  array for the chat API, handling images as base64 data URIs
- ResponseParser handles TextDelta alongside Token variants
- TUI rendering fix: tokens() returns byte-length estimate (~4
  bytes/token) when tokenizer isn't loaded, so the change detector
  actually triggers re-renders
- Gate all vLLM-specific scoring (memory scoring, finetune scoring,
  compare scoring) behind !chat_api checks

Per-agent model override:
- Add model field to agent definition headers (.agent files)
- Thread through AutoAgent → prepare_spawn → resolve_model
- Agents fall back to default_backend when model is unset
- Enables cheaper backends (e.g. Kimi) for graph maintenance agents
  while keeping Sonnet for conversation

Tested: end-to-end with Poe API + Haiku, chat_api: true in config.
TUI starts, messages send, responses stream and render.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Waffles 2026-05-22 15:39:13 -04:00
commit 6c26cee86e
10 changed files with 353 additions and 28 deletions

View file

@ -101,6 +101,7 @@ impl Unconscious {
let auto = AutoAgent::new(
def.agent.clone(), effective_tools, steps,
def.temperature.unwrap_or(0.6), def.priority,
def.model.clone(),
);
agents.push(UnconsciousAgent {
name: def.agent.clone(),
@ -285,7 +286,8 @@ pub async fn prepare_spawn(name: &str, mut auto: AutoAgent, wake: std::sync::Arc
return Err(auto);
}
};
let resolved = match app.resolve_model(&app.default_backend) {
let backend_name = auto.model.as_deref().unwrap_or(&app.default_backend);
let resolved = match app.resolve_model(backend_name) {
Ok(r) => r,
Err(e) => {
dbglog!("[unconscious] API not configured: {}", e);
@ -302,6 +304,7 @@ pub async fn prepare_spawn(name: &str, mut auto: AutoAgent, wake: std::sync::Arc
app, None,
crate::agent::tools::ActiveTools::new(),
auto.tools.clone(),
resolved.chat_api,
).await;
{
let mut st = agent.state.lock().await;