Skip to main content

kcode_k1_codex_web_search_protocol_values/
lib.rs

1use std::error::Error;
2use std::fmt;
3
4#[derive(Debug)]
5pub enum ProtocolError {
6    Invalid(&'static str),
7    Json(serde_json::Error),
8}
9
10impl fmt::Display for ProtocolError {
11    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
12        match self {
13            Self::Invalid(message) => formatter.write_str(message),
14            Self::Json(error) => error.fmt(formatter),
15        }
16    }
17}
18
19impl Error for ProtocolError {
20    fn source(&self) -> Option<&(dyn Error + 'static)> {
21        match self {
22            Self::Invalid(_) => None,
23            Self::Json(error) => Some(error),
24        }
25    }
26}
27
28impl From<serde_json::Error> for ProtocolError {
29    fn from(error: serde_json::Error) -> Self {
30        Self::Json(error)
31    }
32}
33
34#[derive(Clone, Debug, Eq, PartialEq)]
35pub enum RpcResponse<T> {
36    Success(T),
37    Error(RpcError),
38}
39
40#[derive(Clone, Debug, Eq, PartialEq)]
41pub struct RpcError {
42    pub code: i64,
43    pub message: String,
44}
45
46#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub struct ChatGptAccount;
48
49#[derive(Clone, Debug, Eq, PartialEq)]
50pub struct ReasoningEffort {
51    pub reasoning_effort: String,
52}
53
54#[derive(Clone, Debug, Eq, PartialEq)]
55pub struct Model {
56    pub id: String,
57    pub supported_reasoning_efforts: Vec<ReasoningEffort>,
58    pub default_reasoning_effort: Option<String>,
59}
60
61#[derive(Clone, Debug, Eq, PartialEq)]
62pub struct ModelPage {
63    pub data: Vec<Model>,
64    pub next_cursor: Option<String>,
65}
66
67#[derive(Clone, Debug, Eq, PartialEq)]
68pub struct Started {
69    pub id: String,
70}
71
72pub type ThreadStarted = Started;
73pub type TurnStarted = Started;
74
75#[derive(Clone, Copy, Debug, Eq, PartialEq)]
76pub enum MessagePhase {
77    Commentary,
78    FinalAnswer,
79    Unknown,
80}
81
82#[derive(Clone, Debug, Eq, PartialEq)]
83pub struct EventScope {
84    pub thread_id: String,
85    pub turn_id: String,
86}
87
88#[derive(Clone, Debug, Eq, PartialEq)]
89pub struct SourceCandidate {
90    pub url: String,
91    pub title: String,
92}
93
94#[derive(Clone, Copy, Debug, Eq, PartialEq)]
95pub enum TurnStatus {
96    Completed,
97    Failed,
98    Interrupted,
99}
100
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub struct Usage {
103    pub input_tokens: Option<u64>,
104    pub cached_input_tokens: Option<u64>,
105    pub output_tokens: Option<u64>,
106    pub reasoning_output_tokens: Option<u64>,
107    pub total_tokens: Option<u64>,
108}
109
110pub type ProviderError = String;
111
112#[derive(Clone, Debug, Eq, PartialEq)]
113pub enum InboundEvent {
114    AgentMessage {
115        scope: EventScope,
116        id: String,
117        phase: MessagePhase,
118        text: String,
119    },
120    SourceCandidates {
121        scope: EventScope,
122        id: String,
123        sources: Vec<SourceCandidate>,
124    },
125    UsageUpdated {
126        scope: EventScope,
127        usage: Usage,
128    },
129    TurnTerminal {
130        id: String,
131        status: TurnStatus,
132        usage: Option<Usage>,
133        error: Option<ProviderError>,
134    },
135    ProviderError(ProviderError),
136    Ignored {
137        method: String,
138        item_type: Option<String>,
139    },
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    fn assert_value_traits<T: Clone + fmt::Debug + Eq + PartialEq>() {}
147    fn assert_scalar_traits<T: Copy>() {}
148
149    #[test]
150    fn values_have_expected_traits_and_shape() {
151        assert_value_traits::<RpcResponse<ChatGptAccount>>();
152        assert_value_traits::<InboundEvent>();
153        assert_scalar_traits::<MessagePhase>();
154        assert_scalar_traits::<TurnStatus>();
155        assert_scalar_traits::<Usage>();
156
157        assert_eq!(
158            RpcResponse::Success(ChatGptAccount),
159            RpcResponse::Success(ChatGptAccount)
160        );
161        assert!(matches!(
162            InboundEvent::Ignored {
163                method: "unknown".to_owned(),
164                item_type: None,
165            },
166            InboundEvent::Ignored {
167                method,
168                item_type: None,
169            } if method == "unknown"
170        ));
171        assert!(matches!(
172            InboundEvent::UsageUpdated {
173                scope: EventScope {
174                    thread_id: "thread".to_owned(),
175                    turn_id: "turn".to_owned(),
176                },
177                usage: Usage {
178                    input_tokens: Some(1),
179                    cached_input_tokens: Some(2),
180                    output_tokens: Some(3),
181                    reasoning_output_tokens: Some(4),
182                    total_tokens: Some(5),
183                },
184            },
185            InboundEvent::UsageUpdated {
186                scope: EventScope { thread_id, turn_id },
187                usage: Usage {
188                    input_tokens: Some(1),
189                    cached_input_tokens: Some(2),
190                    output_tokens: Some(3),
191                    reasoning_output_tokens: Some(4),
192                    total_tokens: Some(5),
193                },
194            } if thread_id == "thread" && turn_id == "turn"
195        ));
196    }
197}