From 6ab6a86cf2da389c3658f5eb7d769ef1d922b91e Mon Sep 17 00:00:00 2001 From: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Date: Thu, 14 May 2026 18:54:18 -0700 Subject: [PATCH] feat(orchestrator): use reasoning-quick-v1 for low-latency chat (#1761) --- src/openhuman/agent/agents/loader.rs | 2 +- .../agent/agents/orchestrator/agent.toml | 9 +++++++- src/openhuman/agent/cost.rs | 22 ++++++++++++++++--- src/openhuman/config/mod.rs | 3 ++- src/openhuman/config/schema/identity_cost.rs | 12 +++++++++- src/openhuman/config/schema/types.rs | 7 ++++++ src/openhuman/providers/router.rs | 8 ++++--- src/openhuman/routing/provider.rs | 5 ++++- 8 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/openhuman/agent/agents/loader.rs b/src/openhuman/agent/agents/loader.rs index a6415b038..20f72c6a1 100644 --- a/src/openhuman/agent/agents/loader.rs +++ b/src/openhuman/agent/agents/loader.rs @@ -303,7 +303,7 @@ mod tests { #[test] fn orchestrator_has_reasoning_hint_and_named_tools() { let def = find("orchestrator"); - assert!(matches!(def.model, ModelSpec::Hint(ref h) if h == "reasoning")); + assert!(matches!(def.model, ModelSpec::Hint(ref h) if h == "reasoning-quick")); match def.tools { ToolScope::Named(tools) => { // spawn_subagent was removed in #1141; spawn_worker_thread is the replacement diff --git a/src/openhuman/agent/agents/orchestrator/agent.toml b/src/openhuman/agent/agents/orchestrator/agent.toml index d6753ce01..f3be3381d 100644 --- a/src/openhuman/agent/agents/orchestrator/agent.toml +++ b/src/openhuman/agent/agents/orchestrator/agent.toml @@ -61,7 +61,14 @@ subagents = [ ] [model] -hint = "reasoning" +# `reasoning-quick` (Kimi K2.6 Turbo on Fireworks via backend PR #760) +# is tuned for low time-to-first-token on conversational turns. The +# orchestrator is a planner/router that mostly picks a delegate and +# synthesises sub-agent output — workload that doesn't benefit from +# the slower deep-reasoning tier. Sub-agents that need heavier +# reasoning can still opt into `reasoning-v1` (DeepSeek V4 Pro) via +# `ModelSpec::Hint("reasoning")` in their own definitions. +hint = "reasoning-quick" [tools] # Direct tools — things the orchestrator calls itself rather than diff --git a/src/openhuman/agent/cost.rs b/src/openhuman/agent/cost.rs index 325a1cde9..188e8bee3 100644 --- a/src/openhuman/agent/cost.rs +++ b/src/openhuman/agent/cost.rs @@ -69,6 +69,15 @@ pub const PRICING_TABLE: &[ModelPricing] = &[ cached_input_per_mtok_usd: 1.50, output_per_mtok_usd: 75.00, }, + // Quick reasoning tier — Kimi K2.6 Turbo on Fireworks (backend PR + // #760). Low TTFT, 128k context, `supportsThinking: false`. Rates + // track Fireworks' published Kimi turbo pricing at time of writing. + ModelPricing { + model: "reasoning-quick-v1", + input_per_mtok_usd: 0.60, + cached_input_per_mtok_usd: 0.06, + output_per_mtok_usd: 2.50, + }, // Agentic tier — maps to Sonnet-class models. ModelPricing { model: "agentic-v1", @@ -95,14 +104,21 @@ pub fn lookup_pricing(model: &str) -> ModelPricing { return *row; } let lower = model.to_ascii_lowercase(); + let by_tier = |tier: &str| { + PRICING_TABLE + .iter() + .find(|row| row.model == tier) + .copied() + .unwrap_or(FALLBACK_PRICING) + }; if lower.contains("opus") { - return PRICING_TABLE[0]; + return by_tier("reasoning-v1"); } if lower.contains("coding") { - return PRICING_TABLE[2]; + return by_tier("coding-v1"); } if lower.contains("sonnet") || lower.contains("agentic") { - return PRICING_TABLE[1]; + return by_tier("agentic-v1"); } FALLBACK_PRICING } diff --git a/src/openhuman/config/mod.rs b/src/openhuman/config/mod.rs index ef3716c49..da019c7c7 100644 --- a/src/openhuman/config/mod.rs +++ b/src/openhuman/config/mod.rs @@ -36,7 +36,8 @@ pub use schema::{ SecretsConfig, SecurityConfig, SlackConfig, StorageConfig, StorageProviderConfig, StorageProviderSection, StreamMode, TelegramConfig, UpdateConfig, UpdateRestartStrategy, VoiceActivationMode, VoiceServerConfig, WebSearchConfig, WebhookConfig, - DEFAULT_CLOUD_LLM_MODEL, DEFAULT_MODEL, MODEL_AGENTIC_V1, MODEL_CODING_V1, MODEL_REASONING_V1, + DEFAULT_CLOUD_LLM_MODEL, DEFAULT_MODEL, MODEL_AGENTIC_V1, MODEL_CODING_V1, + MODEL_REASONING_QUICK_V1, MODEL_REASONING_V1, }; pub use schema::{ clear_active_user, default_root_openhuman_dir, pre_login_user_dir, read_active_user_id, diff --git a/src/openhuman/config/schema/identity_cost.rs b/src/openhuman/config/schema/identity_cost.rs index 6f93d5be0..00d38d847 100644 --- a/src/openhuman/config/schema/identity_cost.rs +++ b/src/openhuman/config/schema/identity_cost.rs @@ -68,7 +68,9 @@ impl Default for CostConfig { /// Default pricing for popular models (USD per 1M tokens) fn get_default_pricing() -> HashMap { - use super::types::{MODEL_AGENTIC_V1, MODEL_CODING_V1, MODEL_REASONING_V1}; + use super::types::{ + MODEL_AGENTIC_V1, MODEL_CODING_V1, MODEL_REASONING_QUICK_V1, MODEL_REASONING_V1, + }; let mut prices = HashMap::new(); @@ -79,6 +81,14 @@ fn get_default_pricing() -> HashMap { output: 2.52, }, ); + // Kimi K2.6 Turbo on Fireworks — see backend PR #760. + prices.insert( + MODEL_REASONING_QUICK_V1.into(), + ModelPricing { + input: 0.60, + output: 2.50, + }, + ); prices.insert( MODEL_AGENTIC_V1.into(), ModelPricing { diff --git a/src/openhuman/config/schema/types.rs b/src/openhuman/config/schema/types.rs index cf622340d..33b6fd5bf 100644 --- a/src/openhuman/config/schema/types.rs +++ b/src/openhuman/config/schema/types.rs @@ -9,6 +9,13 @@ use std::path::PathBuf; /// Standard model identifiers matching the backend model registry. pub const MODEL_AGENTIC_V1: &str = "agentic-v1"; pub const MODEL_REASONING_V1: &str = "reasoning-v1"; +/// Low-latency reasoning tier. Backend maps this to Kimi K2.6 Turbo on +/// Fireworks (128k context, `supportsThinking: false`) — tuned for +/// time-to-first-token on conversational turns. See backend PR #760. +/// The orchestrator (user-facing front-line agent) rides on this tier +/// by default so chat responses feel snappy; reach for the slower +/// `reasoning-v1` (DeepSeek V4 Pro) only when deep reasoning is needed. +pub const MODEL_REASONING_QUICK_V1: &str = "reasoning-quick-v1"; pub const MODEL_CODING_V1: &str = "coding-v1"; /// Default model used when no explicit model is configured. /// diff --git a/src/openhuman/providers/router.rs b/src/openhuman/providers/router.rs index c49905623..51048cdfe 100644 --- a/src/openhuman/providers/router.rs +++ b/src/openhuman/providers/router.rs @@ -3,12 +3,14 @@ use super::Provider; use async_trait::async_trait; use std::collections::HashMap; -/// Maps OpenHuman's abstract tier model names (`reasoning-v1`, `agentic-v1`, -/// `coding-v1`, `summarization-v1`) to the hint slot in `model_routes`. -/// Returns `None` for any model the router shouldn't rewrite. +/// Maps OpenHuman's abstract tier model names (`reasoning-v1`, +/// `reasoning-quick-v1`, `agentic-v1`, `coding-v1`, `summarization-v1`) +/// to the hint slot in `model_routes`. Returns `None` for any model the +/// router shouldn't rewrite. fn openhuman_tier_to_hint(model: &str) -> Option<&'static str> { match model { "reasoning-v1" => Some("reasoning"), + "reasoning-quick-v1" => Some("reasoning-quick"), "agentic-v1" => Some("agentic"), "coding-v1" => Some("coding"), "summarization-v1" => Some("summarization"), diff --git a/src/openhuman/routing/provider.rs b/src/openhuman/routing/provider.rs index e4c5a0f2c..8e3be8dd8 100644 --- a/src/openhuman/routing/provider.rs +++ b/src/openhuman/routing/provider.rs @@ -17,7 +17,9 @@ use std::time::Instant; use anyhow::Result; use async_trait::async_trait; -use crate::openhuman::config::{MODEL_AGENTIC_V1, MODEL_CODING_V1, MODEL_REASONING_V1}; +use crate::openhuman::config::{ + MODEL_AGENTIC_V1, MODEL_CODING_V1, MODEL_REASONING_QUICK_V1, MODEL_REASONING_V1, +}; use crate::openhuman::providers::traits::{ ChatMessage, ChatRequest, ChatResponse, Provider, ProviderCapabilities, StreamChunk, StreamError, StreamOptions, StreamResult, ToolsPayload, @@ -93,6 +95,7 @@ impl IntelligentRoutingProvider { // Keep remote model naming aligned with backend modelRegistry. match requested_model.strip_prefix("hint:") { Some("reasoning") => MODEL_REASONING_V1.to_string(), + Some("reasoning-quick") => MODEL_REASONING_QUICK_V1.to_string(), Some("agentic") => MODEL_AGENTIC_V1.to_string(), Some("coding") => MODEL_CODING_V1.to_string(), _ => requested_model.to_string(),