mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-11 22:53:00 +00:00
Add ReasoningEffort enum to replace raw Option<String>
Introduces a typed ReasoningEffort enum (Low, Medium, High) with serde, Display, and FromStr support. Updates Request, GenerateParams, and SessionConfig to use Option<ReasoningEffort> instead of Option<String>. Aligns with spec change removing "none" as a valid reasoning_effort value. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0c649ea482
commit
0acda783b1
10 changed files with 72 additions and 26 deletions
|
|
@ -63,7 +63,7 @@ pub struct SessionConfig {
|
|||
pub max_tool_rounds_per_input: usize,
|
||||
pub default_command_timeout_ms: u64,
|
||||
pub max_command_timeout_ms: u64,
|
||||
pub reasoning_effort: Option<String>,
|
||||
pub reasoning_effort: Option<fabro_llm::types::ReasoningEffort>,
|
||||
pub speed: Option<String>,
|
||||
pub tool_output_limits: HashMap<String, usize>,
|
||||
pub tool_line_limits: HashMap<String, usize>,
|
||||
|
|
@ -161,7 +161,7 @@ mod tests {
|
|||
fn default_config_values() {
|
||||
let config = SessionConfig::default();
|
||||
assert_eq!(config.max_turns, 0);
|
||||
assert_eq!(config.max_tool_rounds_per_input, 200);
|
||||
assert_eq!(config.max_tool_rounds_per_input, 0);
|
||||
assert_eq!(config.default_command_timeout_ms, 10_000);
|
||||
assert_eq!(config.max_command_timeout_ms, 600_000);
|
||||
assert!(config.reasoning_effort.is_none());
|
||||
|
|
@ -187,12 +187,15 @@ mod tests {
|
|||
fn config_with_custom_values() {
|
||||
let config = SessionConfig {
|
||||
max_turns: 50,
|
||||
reasoning_effort: Some("high".into()),
|
||||
reasoning_effort: Some(fabro_llm::types::ReasoningEffort::High),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(config.max_turns, 50);
|
||||
assert_eq!(config.reasoning_effort, Some("high".into()));
|
||||
assert_eq!(config.max_tool_rounds_per_input, 200);
|
||||
assert_eq!(
|
||||
config.reasoning_effort,
|
||||
Some(fabro_llm::types::ReasoningEffort::High)
|
||||
);
|
||||
assert_eq!(config.max_tool_rounds_per_input, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ impl Session {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_reasoning_effort(&mut self, effort: Option<String>) {
|
||||
pub fn set_reasoning_effort(&mut self, effort: Option<fabro_llm::types::ReasoningEffort>) {
|
||||
self.config.reasoning_effort = effort;
|
||||
}
|
||||
|
||||
|
|
@ -1574,14 +1574,17 @@ mod tests {
|
|||
let mut session = Session::new(client, profile, env, SessionConfig::default());
|
||||
|
||||
// Default reasoning_effort is None
|
||||
session.set_reasoning_effort(Some("high".to_string()));
|
||||
session.set_reasoning_effort(Some(fabro_llm::types::ReasoningEffort::High));
|
||||
session.process_input("test").await.unwrap();
|
||||
|
||||
let captured = provider_ref.captured_request.lock().unwrap();
|
||||
let request = captured
|
||||
.as_ref()
|
||||
.expect("request should have been captured");
|
||||
assert_eq!(request.reasoning_effort, Some("high".to_string()));
|
||||
assert_eq!(
|
||||
request.reasoning_effort,
|
||||
Some(fabro_llm::types::ReasoningEffort::High)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
|||
|
|
@ -430,7 +430,7 @@ macro_rules! reasoning_effort_tests {
|
|||
let tmp = tempfile::tempdir().expect("failed to create tempdir");
|
||||
let config = SessionConfig {
|
||||
max_turns: 20,
|
||||
reasoning_effort: Some("low".to_string()),
|
||||
reasoning_effort: Some(fabro_llm::types::ReasoningEffort::Low),
|
||||
..SessionConfig::default()
|
||||
};
|
||||
let mut session = make_session_with_config($provider, $model, tmp.path(), config).await;
|
||||
|
|
|
|||
|
|
@ -1232,7 +1232,7 @@ async fn create_completion(
|
|||
} else {
|
||||
Some(req.stop_sequences)
|
||||
},
|
||||
reasoning_effort: req.reasoning_effort,
|
||||
reasoning_effort: req.reasoning_effort.as_deref().and_then(|s| s.parse().ok()),
|
||||
speed: None,
|
||||
metadata: None,
|
||||
provider_options: req.provider_options,
|
||||
|
|
|
|||
|
|
@ -869,7 +869,7 @@ fn build_deep_test_params(info: &Model) -> Option<GenerateParams> {
|
|||
.max_tokens(1024);
|
||||
|
||||
if info.features.reasoning {
|
||||
params = params.reasoning_effort("high");
|
||||
params = params.reasoning_effort(crate::types::ReasoningEffort::High);
|
||||
}
|
||||
|
||||
Some(params)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ use crate::provider::StreamEventStream;
|
|||
use crate::retry::retry;
|
||||
use crate::tools::{execute_all_tools_with_repair, RepairToolCallFn, Tool};
|
||||
use crate::types::{
|
||||
FinishReason, GenerateResult, Message, ObjectStreamEvent, Request, Response, ResponseFormat,
|
||||
ResponseFormatType, RetryPolicy, StepResult, StreamEvent, TimeoutConfig, ToolCall, ToolChoice,
|
||||
ToolDefinition, Usage,
|
||||
FinishReason, GenerateResult, Message, ObjectStreamEvent, ReasoningEffort, Request, Response,
|
||||
ResponseFormat, ResponseFormatType, RetryPolicy, StepResult, StreamEvent, TimeoutConfig,
|
||||
ToolCall, ToolChoice, ToolDefinition, Usage,
|
||||
};
|
||||
use futures::{Stream, StreamExt};
|
||||
use std::pin::Pin;
|
||||
|
|
@ -288,7 +288,7 @@ pub struct GenerateParams {
|
|||
pub top_p: Option<f64>,
|
||||
pub max_tokens: Option<i64>,
|
||||
pub stop_sequences: Option<Vec<String>>,
|
||||
pub reasoning_effort: Option<String>,
|
||||
pub reasoning_effort: Option<ReasoningEffort>,
|
||||
pub speed: Option<String>,
|
||||
pub provider: Option<String>,
|
||||
pub provider_options: Option<serde_json::Value>,
|
||||
|
|
@ -412,8 +412,8 @@ impl GenerateParams {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn reasoning_effort(mut self, reasoning_effort: impl Into<String>) -> Self {
|
||||
self.reasoning_effort = Some(reasoning_effort.into());
|
||||
pub fn reasoning_effort(mut self, reasoning_effort: ReasoningEffort) -> Self {
|
||||
self.reasoning_effort = Some(reasoning_effort);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -1513,7 +1513,7 @@ mod tests {
|
|||
.top_p(0.9)
|
||||
.max_tokens(100)
|
||||
.stop_sequences(vec!["STOP".to_string()])
|
||||
.reasoning_effort("high")
|
||||
.reasoning_effort(ReasoningEffort::High)
|
||||
.provider("anthropic")
|
||||
.provider_options(serde_json::json!({"key": "value"}))
|
||||
.max_retries(5)
|
||||
|
|
@ -1532,7 +1532,7 @@ mod tests {
|
|||
assert_eq!(params.top_p, Some(0.9));
|
||||
assert_eq!(params.max_tokens, Some(100));
|
||||
assert_eq!(params.stop_sequences, Some(vec!["STOP".to_string()]));
|
||||
assert_eq!(params.reasoning_effort.as_deref(), Some("high"));
|
||||
assert_eq!(params.reasoning_effort, Some(ReasoningEffort::High));
|
||||
assert_eq!(params.provider.as_deref(), Some("anthropic"));
|
||||
assert!(params.provider_options.is_some());
|
||||
assert_eq!(params.max_retries, 5);
|
||||
|
|
|
|||
|
|
@ -1124,12 +1124,12 @@ fn build_api_request(
|
|||
if supports_effort {
|
||||
(
|
||||
explicit_thinking,
|
||||
Some(serde_json::json!({"effort": effort})),
|
||||
Some(serde_json::json!({"effort": effort.as_str()})),
|
||||
)
|
||||
} else if explicit_thinking.is_none() {
|
||||
// Convert effort level to a thinking budget for models that don't
|
||||
// support the effort parameter (e.g. claude-sonnet-4-5).
|
||||
let budget = effort_to_budget_tokens(effort, resolved_max_tokens);
|
||||
let budget = effort_to_budget_tokens(effort.as_str(), resolved_max_tokens);
|
||||
if resolved_max_tokens <= budget {
|
||||
resolved_max_tokens = budget + 1024;
|
||||
}
|
||||
|
|
@ -2138,7 +2138,7 @@ mod tests {
|
|||
fn build_api_request_maps_reasoning_effort_to_output_config() {
|
||||
let adapter = Adapter::new("test-key");
|
||||
let request = Request {
|
||||
reasoning_effort: Some("medium".to_string()),
|
||||
reasoning_effort: Some(crate::types::ReasoningEffort::Medium),
|
||||
..make_base_request()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -387,7 +387,7 @@ fn build_api_request(request: &Request, stream: bool, codex_mode: bool) -> ApiRe
|
|||
let reasoning = request
|
||||
.reasoning_effort
|
||||
.as_ref()
|
||||
.map(|effort| serde_json::json!({"effort": effort}));
|
||||
.map(|effort| serde_json::json!({"effort": effort.as_str()}));
|
||||
let text = request
|
||||
.response_format
|
||||
.as_ref()
|
||||
|
|
|
|||
|
|
@ -440,6 +440,46 @@ pub struct RateLimitInfo {
|
|||
pub reset_at: Option<String>,
|
||||
}
|
||||
|
||||
// --- 3.8 ReasoningEffort ---
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ReasoningEffort {
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
}
|
||||
|
||||
impl ReasoningEffort {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Low => "low",
|
||||
Self::Medium => "medium",
|
||||
Self::High => "high",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ReasoningEffort {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for ReasoningEffort {
|
||||
type Err = String;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"low" => Ok(Self::Low),
|
||||
"medium" => Ok(Self::Medium),
|
||||
"high" => Ok(Self::High),
|
||||
other => Err(format!(
|
||||
"invalid reasoning_effort: {other:?} (expected low, medium, or high)"
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 3.6 Request ---
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -454,7 +494,7 @@ pub struct Request {
|
|||
pub top_p: Option<f64>,
|
||||
pub max_tokens: Option<i64>,
|
||||
pub stop_sequences: Option<Vec<String>>,
|
||||
pub reasoning_effort: Option<String>,
|
||||
pub reasoning_effort: Option<ReasoningEffort>,
|
||||
pub speed: Option<String>,
|
||||
pub metadata: Option<HashMap<String, String>>,
|
||||
pub provider_options: Option<serde_json::Value>,
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ impl AgentApiBackend {
|
|||
|
||||
let config = SessionConfig {
|
||||
max_tokens: node.max_tokens(),
|
||||
reasoning_effort: Some(node.reasoning_effort().to_string()),
|
||||
reasoning_effort: node.reasoning_effort().parse().ok(),
|
||||
speed: node.speed().map(String::from),
|
||||
tool_hooks,
|
||||
mcp_servers,
|
||||
|
|
@ -291,7 +291,7 @@ impl CodergenBackend for AgentApiBackend {
|
|||
model: model.to_string(),
|
||||
messages,
|
||||
provider,
|
||||
reasoning_effort: Some(node.reasoning_effort().to_string()),
|
||||
reasoning_effort: node.reasoning_effort().parse().ok(),
|
||||
speed: node.speed().map(String::from),
|
||||
tools: None,
|
||||
tool_choice: None,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue