mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
wip
This commit is contained in:
parent
74578c22bc
commit
1dd59ceea5
8 changed files with 122 additions and 31 deletions
|
|
@ -113,7 +113,7 @@ function names, error messages, and exact values. Omit pleasantries and conversa
|
|||
tools: None,
|
||||
tool_choice: None,
|
||||
response_format: None,
|
||||
temperature: Some(0.0),
|
||||
temperature: None,
|
||||
top_p: None,
|
||||
max_tokens: Some(4096),
|
||||
stop_sequences: None,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ fn bare() {
|
|||
exit_code: 0
|
||||
----- stdout -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-7 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-6 anthropic 1m $5.0 / $25.0 25 tok/s
|
||||
claude-sonnet-4-5 anthropic 200k $3.0 / $15.0 50 tok/s
|
||||
claude-sonnet-4-6 anthropic sonnet, claude-sonnet 200k $3.0 / $15.0 50 tok/s
|
||||
claude-haiku-4-5 anthropic haiku, claude-haiku 200k $0.8 / $4.0 100 tok/s
|
||||
|
|
@ -72,7 +73,8 @@ fn list() {
|
|||
exit_code: 0
|
||||
----- stdout -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-7 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-6 anthropic 1m $5.0 / $25.0 25 tok/s
|
||||
claude-sonnet-4-5 anthropic 200k $3.0 / $15.0 50 tok/s
|
||||
claude-sonnet-4-6 anthropic sonnet, claude-sonnet 200k $3.0 / $15.0 50 tok/s
|
||||
claude-haiku-4-5 anthropic haiku, claude-haiku 200k $0.8 / $4.0 100 tok/s
|
||||
|
|
@ -106,7 +108,8 @@ fn list_provider() {
|
|||
exit_code: 0
|
||||
----- stdout -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-7 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-6 anthropic 1m $5.0 / $25.0 25 tok/s
|
||||
claude-sonnet-4-5 anthropic 200k $3.0 / $15.0 50 tok/s
|
||||
claude-sonnet-4-6 anthropic sonnet, claude-sonnet 200k $3.0 / $15.0 50 tok/s
|
||||
claude-haiku-4-5 anthropic haiku, claude-haiku 200k $0.8 / $4.0 100 tok/s
|
||||
|
|
@ -124,7 +127,8 @@ fn list_query() {
|
|||
exit_code: 0
|
||||
----- stdout -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-7 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-6 anthropic 1m $5.0 / $25.0 25 tok/s
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
|
@ -156,7 +160,8 @@ fn list_query_case_insensitive() {
|
|||
exit_code: 0
|
||||
----- stdout -----
|
||||
MODEL PROVIDER ALIASES CONTEXT COST SPEED
|
||||
claude-opus-4-6 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-7 anthropic opus, claude-opus 1m $5.0 / $25.0 25 tok/s
|
||||
claude-opus-4-6 anthropic 1m $5.0 / $25.0 25 tok/s
|
||||
----- stderr -----
|
||||
");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -544,6 +544,8 @@ fn effort_to_budget_tokens(effort: &str, max_tokens: i64) -> i64 {
|
|||
let budget = match effort {
|
||||
"low" => max_tokens / 4,
|
||||
"high" => max_tokens * 3 / 4,
|
||||
"xhigh" => max_tokens * 7 / 8,
|
||||
"max" => max_tokens,
|
||||
_ => max_tokens / 2, // "medium" or unknown
|
||||
};
|
||||
// Anthropic requires budget_tokens >= 1024
|
||||
|
|
@ -2231,4 +2233,36 @@ mod tests {
|
|||
"should contain fast-mode header"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effort_to_budget_tokens_xhigh_maps_to_seven_eighths() {
|
||||
assert_eq!(effort_to_budget_tokens("xhigh", 16_000), 14_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effort_to_budget_tokens_max_maps_to_full_budget() {
|
||||
assert_eq!(effort_to_budget_tokens("max", 16_000), 16_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_api_request_falls_back_to_thinking_budget_for_non_effort_model() {
|
||||
let adapter = Adapter::new("test-key");
|
||||
let request = Request {
|
||||
model: "claude-sonnet-4-5".to_string(),
|
||||
max_tokens: Some(16_000),
|
||||
reasoning_effort: Some(ReasoningEffort::XHigh),
|
||||
..make_base_request()
|
||||
};
|
||||
|
||||
let (api_request, _req_builder) = build_api_request(&adapter, &request, false);
|
||||
assert!(
|
||||
api_request.output_config.is_none(),
|
||||
"non-effort models must not receive output_config"
|
||||
);
|
||||
let thinking = api_request
|
||||
.thinking
|
||||
.expect("thinking must be set for fallback path");
|
||||
assert_eq!(thinking["type"], "enabled");
|
||||
assert_eq!(thinking["budget_tokens"], 14_000);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -417,6 +417,8 @@ pub enum ReasoningEffort {
|
|||
Low,
|
||||
Medium,
|
||||
High,
|
||||
XHigh,
|
||||
Max,
|
||||
}
|
||||
|
||||
impl ReasoningEffort {
|
||||
|
|
@ -425,6 +427,8 @@ impl ReasoningEffort {
|
|||
Self::Low => "low",
|
||||
Self::Medium => "medium",
|
||||
Self::High => "high",
|
||||
Self::XHigh => "xhigh",
|
||||
Self::Max => "max",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -442,8 +446,10 @@ impl std::str::FromStr for ReasoningEffort {
|
|||
"low" => Ok(Self::Low),
|
||||
"medium" => Ok(Self::Medium),
|
||||
"high" => Ok(Self::High),
|
||||
"xhigh" => Ok(Self::XHigh),
|
||||
"max" => Ok(Self::Max),
|
||||
other => Err(format!(
|
||||
"invalid reasoning_effort: {other:?} (expected low, medium, or high)"
|
||||
"invalid reasoning_effort: {other:?} (expected low, medium, high, xhigh, or max)"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
@ -1292,4 +1298,32 @@ mod tests {
|
|||
fn tool_choice_mode_str_named() {
|
||||
assert_eq!(ToolChoice::named("get_weather").mode_str(), "named");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reasoning_effort_from_str_round_trip() {
|
||||
use std::str::FromStr;
|
||||
assert_eq!(ReasoningEffort::from_str("low"), Ok(ReasoningEffort::Low));
|
||||
assert_eq!(
|
||||
ReasoningEffort::from_str("medium"),
|
||||
Ok(ReasoningEffort::Medium)
|
||||
);
|
||||
assert_eq!(ReasoningEffort::from_str("high"), Ok(ReasoningEffort::High));
|
||||
assert_eq!(
|
||||
ReasoningEffort::from_str("xhigh"),
|
||||
Ok(ReasoningEffort::XHigh)
|
||||
);
|
||||
assert_eq!(ReasoningEffort::from_str("max"), Ok(ReasoningEffort::Max));
|
||||
assert_eq!(ReasoningEffort::XHigh.as_str(), "xhigh");
|
||||
assert_eq!(ReasoningEffort::Max.as_str(), "max");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reasoning_effort_from_str_rejects_unknown_with_updated_error() {
|
||||
use std::str::FromStr;
|
||||
let err = ReasoningEffort::from_str("bogus").expect_err("should reject");
|
||||
assert!(
|
||||
err.contains("low, medium, high, xhigh, or max"),
|
||||
"error should list all accepted levels, got: {err}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -389,22 +389,26 @@ impl Model {
|
|||
.map(PricePerMTok::from_usd);
|
||||
|
||||
let (input, output, cached_input) = match (self.provider, speed) {
|
||||
(Provider::Anthropic, Some(Speed::Fast)) if self.id == "claude-opus-4-6" => (
|
||||
input.multiply_ratio(
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_NUMERATOR,
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_DENOMINATOR,
|
||||
),
|
||||
output.multiply_ratio(
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_NUMERATOR,
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_DENOMINATOR,
|
||||
),
|
||||
cached_input.map(|rate| {
|
||||
rate.multiply_ratio(
|
||||
(Provider::Anthropic, Some(Speed::Fast))
|
||||
if self.id == "claude-opus-4-7" || self.id == "claude-opus-4-6" =>
|
||||
{
|
||||
(
|
||||
input.multiply_ratio(
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_NUMERATOR,
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_DENOMINATOR,
|
||||
)
|
||||
}),
|
||||
),
|
||||
),
|
||||
output.multiply_ratio(
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_NUMERATOR,
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_DENOMINATOR,
|
||||
),
|
||||
cached_input.map(|rate| {
|
||||
rate.multiply_ratio(
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_NUMERATOR,
|
||||
ANTHROPIC_FAST_MODE_MULTIPLIER_DENOMINATOR,
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
(_, None | Some(Speed::Standard)) => (input, output, cached_input),
|
||||
_ => return None,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
[
|
||||
{
|
||||
"id": "claude-opus-4-7",
|
||||
"provider": "anthropic",
|
||||
"family": "claude-4",
|
||||
"display_name": "Claude Opus 4.7",
|
||||
"limits": { "context_window": 1000000, "max_output": 128000 },
|
||||
"training": "2025-08-01",
|
||||
"knowledge_cutoff": "May 2025",
|
||||
"features": { "tools": true, "vision": true, "reasoning": true, "effort": true },
|
||||
"costs": {
|
||||
"input_cost_per_mtok": 5.0,
|
||||
"output_cost_per_mtok": 25.0,
|
||||
"cache_input_cost_per_mtok": 0.50
|
||||
},
|
||||
"estimated_output_tps": 25,
|
||||
"aliases": ["opus", "claude-opus"]
|
||||
},
|
||||
{
|
||||
"id": "claude-opus-4-6",
|
||||
"provider": "anthropic",
|
||||
|
|
@ -14,7 +31,7 @@
|
|||
"cache_input_cost_per_mtok": 0.50
|
||||
},
|
||||
"estimated_output_tps": 25,
|
||||
"aliases": ["opus", "claude-opus"]
|
||||
"aliases": []
|
||||
},
|
||||
{
|
||||
"id": "claude-sonnet-4-5",
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ mod tests {
|
|||
#[test]
|
||||
fn builtin_get_by_alias() {
|
||||
let m = Catalog::builtin().get("opus").unwrap();
|
||||
assert_eq!(m.id, "claude-opus-4-6");
|
||||
assert_eq!(m.id, "claude-opus-4-7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -485,10 +485,7 @@ mod tests {
|
|||
estimated_output_tps: Some(
|
||||
25.0,
|
||||
),
|
||||
aliases: [
|
||||
"opus",
|
||||
"claude-opus",
|
||||
],
|
||||
aliases: [],
|
||||
default: false,
|
||||
}
|
||||
"#);
|
||||
|
|
@ -498,7 +495,7 @@ mod tests {
|
|||
fn get_model_info_by_alias() {
|
||||
assert_eq!(
|
||||
Catalog::builtin().get("opus").unwrap().id,
|
||||
"claude-opus-4-6"
|
||||
"claude-opus-4-7"
|
||||
);
|
||||
assert_eq!(
|
||||
Catalog::builtin().get("sonnet").unwrap().id,
|
||||
|
|
|
|||
|
|
@ -129,11 +129,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn inherent_methods_return_correct_values() {
|
||||
let info = Catalog::builtin().get("claude-opus-4-6").unwrap();
|
||||
assert_eq!(info.id(), "claude-opus-4-6");
|
||||
let info = Catalog::builtin().get("claude-opus-4-7").unwrap();
|
||||
assert_eq!(info.id(), "claude-opus-4-7");
|
||||
assert_eq!(info.provider(), Provider::Anthropic);
|
||||
assert_eq!(info.family(), "claude-4");
|
||||
assert_eq!(info.display_name(), "Claude Opus 4.6");
|
||||
assert_eq!(info.display_name(), "Claude Opus 4.7");
|
||||
assert_eq!(info.context_window(), 1_000_000);
|
||||
assert_eq!(info.max_output(), Some(128_000));
|
||||
assert!(info.supports_tools());
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue