Skip to main content

edgecrab_types/
config.rs

1//! API modes, platform identifiers, and constants.
2
3use serde::{Deserialize, Serialize};
4
5/// Default model when none is specified.
6pub const DEFAULT_MODEL: &str = "anthropic/claude-sonnet-4-20250514";
7pub const OPENROUTER_BASE_URL: &str = "https://openrouter.ai/api/v1";
8
9/// API protocol variant — determines how requests/responses are shaped.
10///
11/// ```text
12///   ChatCompletions   ── OpenAI / OpenRouter standard
13///   AnthropicMessages ── Direct Anthropic API
14///   CodexResponses    ── OpenAI Codex Responses API
15/// ```
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17pub enum ApiMode {
18    ChatCompletions,
19    AnthropicMessages,
20    CodexResponses,
21}
22
23impl ApiMode {
24    /// Auto-detect API mode from base URL and model name.
25    pub fn detect(base_url: &str, model: &str) -> Self {
26        if base_url.contains("api.anthropic.com") {
27            ApiMode::AnthropicMessages
28        } else if base_url.contains("api.openai.com") && model.contains("codex") {
29            ApiMode::CodexResponses
30        } else {
31            ApiMode::ChatCompletions
32        }
33    }
34}
35
36/// Platform the agent is running on — affects prompt hints and tool availability.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
38#[serde(rename_all = "lowercase")]
39pub enum Platform {
40    #[default]
41    Cli,
42    Telegram,
43    Discord,
44    Slack,
45    Whatsapp,
46    Feishu,
47    Wecom,
48    Signal,
49    Email,
50    Matrix,
51    Mattermost,
52    DingTalk,
53    Sms,
54    Webhook,
55    Api,
56    HomeAssistant,
57    Acp,
58    /// Scheduled cron job — no interactive user present.
59    Cron,
60}
61
62impl std::fmt::Display for Platform {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        let s = match self {
65            Platform::Cli => "cli",
66            Platform::Telegram => "telegram",
67            Platform::Discord => "discord",
68            Platform::Slack => "slack",
69            Platform::Whatsapp => "whatsapp",
70            Platform::Feishu => "feishu",
71            Platform::Wecom => "wecom",
72            Platform::Signal => "signal",
73            Platform::Email => "email",
74            Platform::Matrix => "matrix",
75            Platform::Mattermost => "mattermost",
76            Platform::DingTalk => "dingtalk",
77            Platform::Sms => "sms",
78            Platform::Webhook => "webhook",
79            Platform::Api => "api",
80            Platform::HomeAssistant => "homeassistant",
81            Platform::Acp => "acp",
82            Platform::Cron => "cron",
83        };
84        write!(f, "{s}")
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn api_mode_detect_anthropic() {
94        assert_eq!(
95            ApiMode::detect("https://api.anthropic.com/v1", "claude-4"),
96            ApiMode::AnthropicMessages
97        );
98    }
99
100    #[test]
101    fn api_mode_detect_codex() {
102        assert_eq!(
103            ApiMode::detect("https://api.openai.com/v1", "codex-mini"),
104            ApiMode::CodexResponses
105        );
106    }
107
108    #[test]
109    fn api_mode_detect_default() {
110        assert_eq!(
111            ApiMode::detect("https://openrouter.ai/api/v1", "anthropic/claude-4"),
112            ApiMode::ChatCompletions
113        );
114    }
115
116    #[test]
117    fn platform_display() {
118        assert_eq!(format!("{}", Platform::Cli), "cli");
119        assert_eq!(format!("{}", Platform::Telegram), "telegram");
120    }
121
122    #[test]
123    fn platform_serde_roundtrip() {
124        for p in [
125            Platform::Cli,
126            Platform::Telegram,
127            Platform::Discord,
128            Platform::Slack,
129            Platform::Feishu,
130            Platform::Wecom,
131        ] {
132            let json = serde_json::to_string(&p).expect("serialize");
133            let deser: Platform = serde_json::from_str(&json).expect("deserialize");
134            assert_eq!(p, deser);
135        }
136    }
137}