mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-08 08:04:25 -10:00
Read direct DeepSeek effort levels from its model listing
Retain per-model supported levels and defaults from official DeepSeek catalogs while keeping compatible endpoints and unknown levels unclaimed. Reject malformed metadata without replacing the previous catalog. Verified the existing limits_a_generic_route_keeps_the_limits_its_listing_states test: 1 passed, 0 failed, 13930 filtered; 4160 input hashes unchanged on the shared frozen tree. That tree included other recorded uncommitted workspace changes; the linker emitted a compact-unwind warning. No provider call or inference.
This commit is contained in:
1 parent
9f64cb24db
commit
687a38c6ed
2 files changed
+232
-29
No files matched your search
+113
-27
@@ -5009,6 +5009,81 @@ struct ListedRow {
|
||||
limits: ListedRowLimits,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ListedDeepseekRow {
|
||||
id: String,
|
||||
#[serde(default)]
|
||||
effort: Option<ListedDeepseekEffort>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ListedDeepseekEffort {
|
||||
supported_levels: Vec<String>,
|
||||
#[serde(default)]
|
||||
default_level: Option<String>,
|
||||
}
|
||||
|
||||
fn listed_deepseek_efforts_by_model_id(
|
||||
body: &str,
|
||||
) -> Result<std::collections::BTreeMap<String, Vec<Value>>, CatalogRefreshError> {
|
||||
let page: ModelsPage<'_> =
|
||||
serde_json::from_str(body).map_err(|_| CatalogRefreshError::InvalidResponse)?;
|
||||
let rows: Vec<Box<serde_json::value::RawValue>> = serde_json::from_str(page.rows()?.get())
|
||||
.map_err(|_| CatalogRefreshError::InvalidResponse)?;
|
||||
if rows.len() > PROVIDER_CATALOG_MAX_ROWS {
|
||||
return Err(CatalogRefreshError::InvalidResponse);
|
||||
}
|
||||
let mut result = std::collections::BTreeMap::new();
|
||||
for row in rows {
|
||||
let row: ListedDeepseekRow =
|
||||
serde_json::from_str(row.get()).map_err(|_| CatalogRefreshError::InvalidResponse)?;
|
||||
if !codewhale_runtime::catalog_cache::valid_catalog_model_id(&row.id)
|
||||
|| result.contains_key(&row.id)
|
||||
{
|
||||
return Err(CatalogRefreshError::InvalidResponse);
|
||||
}
|
||||
let mut options = Vec::new();
|
||||
if let Some(effort) = row.effort {
|
||||
let mut seen = std::collections::BTreeSet::new();
|
||||
if effort.supported_levels.len() > 16
|
||||
|| effort.supported_levels.iter().any(|level| {
|
||||
level.is_empty()
|
||||
|| level.len() > 32
|
||||
|| !level.bytes().all(|byte| {
|
||||
byte.is_ascii_lowercase()
|
||||
|| byte.is_ascii_digit()
|
||||
|| matches!(byte, b'_' | b'-')
|
||||
})
|
||||
|| !seen.insert(level)
|
||||
})
|
||||
|| effort
|
||||
.default_level
|
||||
.as_ref()
|
||||
.is_some_and(|default| !effort.supported_levels.contains(default))
|
||||
{
|
||||
return Err(CatalogRefreshError::InvalidResponse);
|
||||
}
|
||||
let supported: Vec<_> = effort
|
||||
.supported_levels
|
||||
.iter()
|
||||
.filter(|level| matches!(level.as_str(), "low" | "high" | "max"))
|
||||
.cloned()
|
||||
.collect();
|
||||
let mut option = json!({ "type": "effort", "values": supported });
|
||||
if let Some(default) = effort
|
||||
.default_level
|
||||
.filter(|level| matches!(level.as_str(), "low" | "high" | "max"))
|
||||
{
|
||||
option["default"] = json!(default);
|
||||
}
|
||||
options.push(json!({ "type": "toggle" }));
|
||||
options.push(option);
|
||||
}
|
||||
result.insert(row.id, options);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl ListedRowLimits {
|
||||
fn to_models_dev_limit(&self) -> Option<codewhale_config::models_dev::ModelsDevLimit> {
|
||||
let block = self.limit.as_ref();
|
||||
@@ -5605,6 +5680,13 @@ fn catalog_delta_from_models_body(
|
||||
)?
|
||||
} else {
|
||||
let mut listed_limits = listed_limits_by_model_id(body);
|
||||
let listed_efforts = if api_provider == ProviderKind::Deepseek
|
||||
&& codewhale_config::provider_base_url_is_official(api_provider, base_url)
|
||||
{
|
||||
listed_deepseek_efforts_by_model_id(body)?
|
||||
} else {
|
||||
std::collections::BTreeMap::new()
|
||||
};
|
||||
let models = apply_provider_model_cutline(
|
||||
api_provider,
|
||||
parse_models_response_for_provider(body, api_provider)
|
||||
@@ -5628,34 +5710,38 @@ fn catalog_delta_from_models_body(
|
||||
}
|
||||
models
|
||||
.into_iter()
|
||||
.map(|model| CatalogOffering {
|
||||
cost_source: None,
|
||||
modalities_source: None,
|
||||
provider: provider.clone(),
|
||||
endpoint_key: if api_provider == ProviderKind::OpencodeGo {
|
||||
codewhale_config::opencode_go_endpoint_key(&model.id)
|
||||
.expect("filtered Go roster")
|
||||
} else {
|
||||
"chat"
|
||||
.map(|model| {
|
||||
let reasoning_options = listed_efforts.get(&model.id);
|
||||
CatalogOffering {
|
||||
cost_source: None,
|
||||
modalities_source: None,
|
||||
provider: provider.clone(),
|
||||
endpoint_key: if api_provider == ProviderKind::OpencodeGo {
|
||||
codewhale_config::opencode_go_endpoint_key(&model.id)
|
||||
.expect("filtered Go roster")
|
||||
} else {
|
||||
"chat"
|
||||
}
|
||||
.to_string(),
|
||||
limit: listed_limits.get(&model.id).cloned(),
|
||||
wire_model_id: model.id,
|
||||
canonical_model: None,
|
||||
default_for_provider: false,
|
||||
family: None,
|
||||
cost: None,
|
||||
modalities: None,
|
||||
attachment: None,
|
||||
reasoning: None,
|
||||
tool_call: None,
|
||||
structured_output: None,
|
||||
reasoning_options: reasoning_options.cloned().unwrap_or_default(),
|
||||
reasoning_options_present: reasoning_options.is_some(),
|
||||
source: CatalogSource::Live {
|
||||
base_url_fingerprint: fingerprint.clone(),
|
||||
fetched_at,
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
.to_string(),
|
||||
limit: listed_limits.get(&model.id).cloned(),
|
||||
wire_model_id: model.id,
|
||||
canonical_model: None,
|
||||
default_for_provider: false,
|
||||
family: None,
|
||||
cost: None,
|
||||
modalities: None,
|
||||
attachment: None,
|
||||
reasoning: None,
|
||||
tool_call: None,
|
||||
structured_output: None,
|
||||
reasoning_options: Vec::new(),
|
||||
source: CatalogSource::Live {
|
||||
base_url_fingerprint: fingerprint.clone(),
|
||||
fetched_at,
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// === Live /models limits: the ceilings a provider states itself ==========
|
||||
// === Live /models limits: the ceilings a provider states itself ==========
|
||||
//
|
||||
// Every route that is not OrcaRouter, OpenRouter or Concentrate used to build
|
||||
// its offerings with `limit: None`, so a provider publishing context and
|
||||
@@ -20,7 +20,11 @@ fn limits_a_live_listing_states_context_and_output_ceilings() {
|
||||
{"id":"row-max","max_context_length":524288,"max_completion_tokens":16000}
|
||||
]}"#,
|
||||
);
|
||||
let row = |id: &str| limits.get(id).unwrap_or_else(|| panic!("no limit for {id}"));
|
||||
let row = |id: &str| {
|
||||
limits
|
||||
.get(id)
|
||||
.unwrap_or_else(|| panic!("no limit for {id}"))
|
||||
};
|
||||
assert_eq!(row("row-openai").context, Some(1_048_000));
|
||||
assert_eq!(row("row-openai").output, Some(64_000));
|
||||
assert_eq!(row("row-flat").context, Some(200_000));
|
||||
@@ -100,4 +104,117 @@ fn limits_a_generic_route_keeps_the_limits_its_listing_states() {
|
||||
assert_eq!(limit.context, Some(1_048_000));
|
||||
assert_eq!(limit.output, Some(32_000));
|
||||
assert_eq!(limit.input, None);
|
||||
|
||||
let body = r#"{"data":[
|
||||
{"id":"deepseek-flash","context_window":1048576,
|
||||
"effort":{"supported_levels":["low","high","max"],"default_level":"high"}},
|
||||
{"id":"new-model","effort":{"supported_levels":["max","future","low"],"default_level":"future"}},
|
||||
{"id":"unknown-model"}
|
||||
]}"#;
|
||||
let direct = catalog_delta_from_models_body(
|
||||
ProviderKind::Deepseek,
|
||||
"deepseek".to_string(),
|
||||
"https://api.deepseek.com/v1/",
|
||||
body,
|
||||
&[],
|
||||
)
|
||||
.expect("direct DeepSeek catalog");
|
||||
let find = |id: &str| {
|
||||
direct
|
||||
.offerings
|
||||
.iter()
|
||||
.find(|row| row.wire_model_id == id)
|
||||
.unwrap()
|
||||
};
|
||||
assert_eq!(
|
||||
find("deepseek-flash").limit.as_ref().unwrap().context,
|
||||
Some(1_048_576)
|
||||
);
|
||||
assert_eq!(
|
||||
find("deepseek-flash").reasoning_options,
|
||||
vec![
|
||||
json!({"type":"toggle"}),
|
||||
json!({"type":"effort", "values":["low","high","max"], "default":"high"})
|
||||
]
|
||||
);
|
||||
let projected =
|
||||
crate::reasoning_preference::ReasoningEffort::catalog_effort_values_for_offering(find(
|
||||
"deepseek-flash",
|
||||
))
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.filter(|effort| {
|
||||
!matches!(
|
||||
effort,
|
||||
crate::reasoning_preference::ReasoningEffort::Off
|
||||
| crate::reasoning_preference::ReasoningEffort::Auto
|
||||
)
|
||||
})
|
||||
.map(|effort| effort.as_setting())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(projected, ["low", "high", "max"]);
|
||||
assert_eq!(
|
||||
find("new-model").reasoning_options,
|
||||
vec![
|
||||
json!({"type":"toggle"}),
|
||||
json!({"type":"effort", "values":["max","low"]})
|
||||
]
|
||||
);
|
||||
assert!(find("unknown-model").reasoning_options_present);
|
||||
assert!(find("unknown-model").reasoning_options.is_empty());
|
||||
assert!(
|
||||
crate::reasoning_preference::ReasoningEffort::catalog_effort_values_for_offering(find(
|
||||
"unknown-model"
|
||||
))
|
||||
.is_none()
|
||||
);
|
||||
let mut unknown = find("unknown-model").clone();
|
||||
let mut old = unknown.clone();
|
||||
old.reasoning_options = vec![json!({"type":"effort", "values":["high"]})];
|
||||
old.reasoning_options_present = true;
|
||||
unknown.complete_from(&old, Some("https://api.deepseek.com/v1"));
|
||||
assert!(unknown.reasoning_options.is_empty());
|
||||
|
||||
for (provider, endpoint) in [
|
||||
(ProviderKind::Deepseek, "https://relay.example/v1"),
|
||||
(
|
||||
ProviderKind::Deepseek,
|
||||
"https://api.deepseek.com.example/v1",
|
||||
),
|
||||
(ProviderKind::Custom, "https://api.deepseek.com/v1"),
|
||||
] {
|
||||
let proxy =
|
||||
catalog_delta_from_models_body(provider, "proxy".to_string(), endpoint, body, &[])
|
||||
.expect("compatible route catalog");
|
||||
assert!(
|
||||
proxy
|
||||
.offerings
|
||||
.iter()
|
||||
.all(|row| !row.reasoning_options_present)
|
||||
);
|
||||
assert!(
|
||||
proxy
|
||||
.offerings
|
||||
.iter()
|
||||
.all(|row| row.reasoning_options.is_empty())
|
||||
);
|
||||
}
|
||||
for invalid in [
|
||||
r#"{"data":[{"id":"a","effort":{"supported_levels":"high"}}]}"#,
|
||||
r#"{"data":[{"id":"a","effort":{"supported_levels":["low"],"default_level":"high"}}]}"#,
|
||||
r#"{"data":[{"id":"a","effort":{"supported_levels":["low","low"]}}]}"#,
|
||||
r#"{"data":[{"id":"a"},{"id":"a"}]}"#,
|
||||
r#"{"data":[{"id":"a","effort":{"supported_levels":["bad\nvalue"]}}]}"#,
|
||||
] {
|
||||
assert!(
|
||||
catalog_delta_from_models_body(
|
||||
ProviderKind::Deepseek,
|
||||
"deepseek".to_string(),
|
||||
"https://api.deepseek.com",
|
||||
invalid,
|
||||
&[],
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user