bamboo-infrastructure 2026.5.4

Infrastructure services and integrations for the Bamboo agent framework
Documentation
use bamboo_domain::{ProviderFormat, ReasoningEffort};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::BTreeMap;

use crate::config::config::RequestOverridesConfig;

fn default_true() -> bool {
    true
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderInstance {
    pub id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub display_name: Option<String>,
    pub format: ProviderFormat,
    #[serde(default = "default_true")]
    pub enabled: bool,

    #[serde(default, skip_serializing)]
    pub api_key: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub api_key_encrypted: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub base_url: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub fast_model: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub vision_model: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_tokens: Option<u32>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reasoning_effort: Option<ReasoningEffort>,

    #[serde(default)]
    pub headless_auth: bool,

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub responses_only_models: Vec<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub target_provider: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub request_overrides: Option<RequestOverridesConfig>,

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub custom_models: Vec<String>,

    #[serde(default, flatten)]
    pub extra: BTreeMap<String, Value>,
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn deserialize_minimal() {
        let json = r#"{"id":"my-openai","format":"openai"}"#;
        let v: ProviderInstance = serde_json::from_str(json).unwrap();
        assert_eq!(v.id, "my-openai");
        assert_eq!(v.format, ProviderFormat::OpenAI);
        assert!(v.enabled);
        assert!(v.custom_models.is_empty());
    }
    #[test]
    fn deserialize_full() {
        let json = r#"{
            "id":"deepseek","display_name":"DeepSeek",
            "format":"openai","enabled":true,
            "base_url":"https://api.deepseek.com",
            "model":"deepseek-chat",
            "custom_models":["deepseek-coder","deepseek-reasoner"]
        }"#;
        let v: ProviderInstance = serde_json::from_str(json).unwrap();
        assert_eq!(v.id, "deepseek");
        assert_eq!(v.custom_models.len(), 2);
    }
}