Skip to main content

synaps_cli/runtime/openai/catalog/
groq.rs

1//! Groq `/models` parser and reasoning inference.
2
3use serde::Deserialize;
4
5use super::{CatalogModel, CatalogProviderKind, CatalogSource, ReasoningSupport};
6
7#[derive(Debug, Deserialize)]
8struct GroqModelsResponse {
9    data: Vec<GroqModelItem>,
10}
11
12#[derive(Debug, Deserialize)]
13struct GroqModelItem {
14    id: String,
15    #[serde(default = "default_true")]
16    active: bool,
17    #[serde(default)]
18    context_window: Option<u64>,
19    #[serde(default)]
20    owned_by: Option<String>,
21}
22
23fn default_true() -> bool { true }
24
25pub fn infer_groq_reasoning(model_id: &str) -> ReasoningSupport {
26    let id = model_id.to_ascii_lowercase();
27    if id.starts_with("openai/gpt-oss-") || id.starts_with("qwen/qwen3-") || id.starts_with("groq/compound") {
28        ReasoningSupport::GroqReasoning
29    } else {
30        ReasoningSupport::None
31    }
32}
33
34pub fn parse_groq_catalog_models(body: &str) -> Result<Vec<CatalogModel>, serde_json::Error> {
35    let resp: GroqModelsResponse = serde_json::from_str(body)?;
36    Ok(resp
37        .data
38        .into_iter()
39        .filter(|item| item.active)
40        .filter_map(|item| {
41            let mut m = CatalogModel::new("groq", "Groq", item.id)?;
42            m.provider_kind = CatalogProviderKind::Groq;
43            m.label = item.owned_by.as_ref().map(|owner| format!("{} — {}", m.id, owner));
44            m.context_tokens = item.context_window;
45            m.reasoning = infer_groq_reasoning(&m.id);
46            m.source = CatalogSource::Live;
47            Some(m)
48        })
49        .collect())
50}