sim_lib_openai_server/
citizen.rs1use sim_citizen_derive::Citizen;
2use sim_kernel::{Error, Expr, Result, Symbol};
3
4use crate::{
5 content_id::content_id_for_expr,
6 objects::{GatewayEvent, GatewayRequest, GatewayResponse, GatewayRun},
7 runtime::OpenAiGatewayKey,
8};
9use sim_kernel::CapabilitySet;
10
11#[derive(Clone, Debug, PartialEq, Citizen)]
13#[citizen(symbol = "openai/GatewayRequest", version = 1)]
14pub struct OpenAiGatewayRequestDescriptor {
15 #[citizen(with = "gateway_request_expr")]
16 request: Expr,
17}
18
19#[derive(Clone, Debug, PartialEq, Citizen)]
21#[citizen(symbol = "openai/GatewayResponse", version = 1)]
22pub struct OpenAiGatewayResponseDescriptor {
23 #[citizen(with = "gateway_response_expr")]
24 response: Expr,
25}
26
27#[derive(Clone, Debug, PartialEq, Citizen)]
29#[citizen(symbol = "openai/GatewayRun", version = 1)]
30pub struct OpenAiGatewayRunDescriptor {
31 #[citizen(with = "gateway_run_expr")]
32 run: Expr,
33}
34
35#[derive(Clone, Debug, PartialEq, Citizen)]
37#[citizen(symbol = "openai/GatewayEvent", version = 1)]
38pub struct OpenAiGatewayEventDescriptor {
39 #[citizen(with = "gateway_event_expr")]
40 event: Expr,
41}
42
43#[derive(Clone, Debug, PartialEq, Citizen)]
45#[citizen(symbol = "openai/Plan", version = 1)]
46pub struct OpenAiPlanDescriptor {
47 #[citizen(with = "plan_source")]
48 source: String,
49}
50
51#[derive(Clone, Debug, PartialEq, Citizen)]
53#[citizen(symbol = "openai/GatewayKey", version = 1)]
54pub struct OpenAiGatewayKeyDescriptor {
55 #[citizen(with = "gateway_key_expr")]
56 key: Expr,
57}
58
59impl OpenAiGatewayRequestDescriptor {
60 pub fn from_expr(request: Expr) -> Result<Self> {
62 gateway_request_expr::decode(&request)?;
63 Ok(Self { request })
64 }
65
66 pub fn as_expr(&self) -> &Expr {
68 &self.request
69 }
70}
71
72impl Default for OpenAiGatewayRequestDescriptor {
73 fn default() -> Self {
74 Self::from_expr(GatewayRequest::get("/v1/models").to_expr())
75 .expect("default OpenAI request descriptor should be valid")
76 }
77}
78
79impl OpenAiGatewayResponseDescriptor {
80 pub fn from_expr(response: Expr) -> Result<Self> {
82 gateway_response_expr::decode(&response)?;
83 Ok(Self { response })
84 }
85
86 pub fn as_expr(&self) -> &Expr {
88 &self.response
89 }
90}
91
92impl Default for OpenAiGatewayResponseDescriptor {
93 fn default() -> Self {
94 Self::from_expr(GatewayResponse::json(200, br#"{"ok":true}"#.to_vec()).to_expr())
95 .expect("default OpenAI response descriptor should be valid")
96 }
97}
98
99impl OpenAiGatewayRunDescriptor {
100 pub fn from_expr(run: Expr) -> Result<Self> {
102 gateway_run_expr::decode(&run)?;
103 Ok(Self { run })
104 }
105
106 pub fn as_expr(&self) -> &Expr {
108 &self.run
109 }
110}
111
112impl Default for OpenAiGatewayRunDescriptor {
113 fn default() -> Self {
114 let request_id = content_id_for_expr(&GatewayRequest::get("/v1/models").to_expr())
115 .expect("default OpenAI run content id should be valid");
116 Self::from_expr(GatewayRun::new("gwrun-citizen", request_id, 1).to_expr())
117 .expect("default OpenAI run descriptor should be valid")
118 }
119}
120
121impl OpenAiGatewayEventDescriptor {
122 pub fn from_expr(event: Expr) -> Result<Self> {
124 gateway_event_expr::decode(&event)?;
125 Ok(Self { event })
126 }
127
128 pub fn as_expr(&self) -> &Expr {
130 &self.event
131 }
132}
133
134impl Default for OpenAiGatewayEventDescriptor {
135 fn default() -> Self {
136 Self::from_expr(
137 GatewayEvent::new(
138 "gwevt-citizen",
139 "gwrun-citizen",
140 0,
141 Symbol::new("final"),
142 Expr::String("ok".to_owned()),
143 1,
144 )
145 .to_expr(),
146 )
147 .expect("default OpenAI event descriptor should be valid")
148 }
149}
150
151impl OpenAiPlanDescriptor {
152 pub fn new(source: impl Into<String>) -> Result<Self> {
154 let source = source.into();
155 plan_source::decode(&Expr::String(source.clone()))?;
156 Ok(Self { source })
157 }
158
159 pub fn source(&self) -> &str {
161 &self.source
162 }
163}
164
165impl Default for OpenAiPlanDescriptor {
166 fn default() -> Self {
167 Self::new("fixture").expect("default OpenAI plan descriptor should be valid")
168 }
169}
170
171impl OpenAiGatewayKeyDescriptor {
172 pub fn from_expr(key: Expr) -> Result<Self> {
174 gateway_key_expr::decode(&key)?;
175 Ok(Self { key })
176 }
177
178 pub fn as_expr(&self) -> &Expr {
180 &self.key
181 }
182}
183
184impl Default for OpenAiGatewayKeyDescriptor {
185 fn default() -> Self {
186 Self::from_expr(OpenAiGatewayKey::from_secret("citizen", CapabilitySet::new()).to_expr())
187 .expect("default OpenAI key descriptor should be valid")
188 }
189}
190
191pub fn openai_gateway_request_class_symbol() -> Symbol {
193 Symbol::qualified("openai", "GatewayRequest")
194}
195
196pub fn openai_gateway_response_class_symbol() -> Symbol {
198 Symbol::qualified("openai", "GatewayResponse")
199}
200
201pub fn openai_gateway_run_class_symbol() -> Symbol {
203 Symbol::qualified("openai", "GatewayRun")
204}
205
206pub fn openai_gateway_event_class_symbol() -> Symbol {
208 Symbol::qualified("openai", "GatewayEvent")
209}
210
211pub fn openai_plan_class_symbol() -> Symbol {
213 Symbol::qualified("openai", "Plan")
214}
215
216pub fn openai_gateway_key_class_symbol() -> Symbol {
218 Symbol::qualified("openai", "GatewayKey")
219}
220
221pub(crate) mod gateway_request_expr {
222 use sim_kernel::{Expr, Result};
223
224 use super::expect_object;
225
226 pub fn encode(expr: &Expr) -> Expr {
227 expr.clone()
228 }
229
230 pub fn decode(expr: &Expr) -> Result<Expr> {
231 expect_object(expr, "openai-gateway/request")?;
232 Ok(expr.clone())
233 }
234}
235
236pub(crate) mod gateway_response_expr {
237 use sim_kernel::{Expr, Result};
238
239 use super::expect_object;
240
241 pub fn encode(expr: &Expr) -> Expr {
242 expr.clone()
243 }
244
245 pub fn decode(expr: &Expr) -> Result<Expr> {
246 expect_object(expr, "openai-gateway/response")?;
247 Ok(expr.clone())
248 }
249}
250
251pub(crate) mod gateway_run_expr {
252 use sim_kernel::{Expr, Result};
253
254 use super::expect_object;
255
256 pub fn encode(expr: &Expr) -> Expr {
257 expr.clone()
258 }
259
260 pub fn decode(expr: &Expr) -> Result<Expr> {
261 expect_object(expr, "openai-gateway/run")?;
262 Ok(expr.clone())
263 }
264}
265
266pub(crate) mod gateway_event_expr {
267 use sim_kernel::{Expr, Result};
268
269 use super::expect_object;
270
271 pub fn encode(expr: &Expr) -> Expr {
272 expr.clone()
273 }
274
275 pub fn decode(expr: &Expr) -> Result<Expr> {
276 expect_object(expr, "openai-gateway/event")?;
277 Ok(expr.clone())
278 }
279}
280
281pub(crate) mod plan_source {
282 use sim_kernel::{Error, Expr, Result};
283
284 use crate::parse_plan;
285
286 pub fn encode(source: &str) -> Expr {
287 Expr::String(source.to_owned())
288 }
289
290 pub fn decode(expr: &Expr) -> Result<String> {
291 let Expr::String(source) = expr else {
292 return Err(Error::Eval(
293 "OpenAI plan descriptor source must be a string".to_owned(),
294 ));
295 };
296 parse_plan(source)?;
297 Ok(source.clone())
298 }
299}
300
301pub(crate) mod gateway_key_expr {
302 use sim_kernel::{Expr, Result};
303
304 use super::expect_object;
305
306 pub fn encode(expr: &Expr) -> Expr {
307 expr.clone()
308 }
309
310 pub fn decode(expr: &Expr) -> Result<Expr> {
311 expect_object(expr, "openai-gateway/key")?;
312 Ok(expr.clone())
313 }
314}
315
316fn expect_object(expr: &Expr, expected: &str) -> Result<()> {
317 let Expr::Map(entries) = expr else {
318 return Err(Error::Eval(format!("{expected} descriptor must be a map")));
319 };
320 let object = entries.iter().find_map(|(key, value)| {
321 field_name(key)
322 .as_deref()
323 .filter(|name| *name == "object")
324 .map(|_| value)
325 });
326 match object {
327 Some(Expr::String(value)) if value == expected => Ok(()),
328 _ => Err(Error::Eval(format!(
329 "{expected} descriptor has wrong object field"
330 ))),
331 }
332}
333
334fn field_name(expr: &Expr) -> Option<String> {
335 match expr {
336 Expr::Symbol(symbol) if symbol.namespace.is_none() => Some(symbol.name.to_string()),
337 Expr::String(value) => Some(value.clone()),
338 _ => None,
339 }
340}