rig_core/completion/message/
native.rs1use std::borrow::Cow;
16
17use serde::{Deserialize, Serialize};
18
19#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
22#[serde(transparent)]
23pub struct Api(Cow<'static, str>);
24
25impl Api {
26 pub const fn from_static(name: &'static str) -> Self {
28 Self(Cow::Borrowed(name))
29 }
30
31 pub fn as_str(&self) -> &str {
33 &self.0
34 }
35}
36
37impl From<&'static str> for Api {
38 fn from(name: &'static str) -> Self {
39 Self::from_static(name)
40 }
41}
42
43impl From<String> for Api {
44 fn from(name: String) -> Self {
45 Self(Cow::Owned(name))
46 }
47}
48
49impl std::fmt::Display for Api {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 f.write_str(&self.0)
52 }
53}
54
55#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
61pub struct Origin {
62 pub api: Api,
64 pub provider: String,
66 pub model: String,
68 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub response_model: Option<String>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub response_id: Option<String>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub context: Option<Fingerprint>,
81}
82
83impl Origin {
84 pub fn new(api: impl Into<Api>, provider: impl Into<String>, model: impl Into<String>) -> Self {
87 Self {
88 api: api.into(),
89 provider: provider.into(),
90 model: model.into(),
91 response_model: None,
92 response_id: None,
93 context: None,
94 }
95 }
96
97 pub fn same_model(&self, api: &Api, provider: &str, model: &str) -> bool {
100 &self.api == api && self.provider == provider && self.model == model
101 }
102}
103
104#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
110#[serde(rename_all = "snake_case")]
111#[non_exhaustive]
112pub enum StopReason {
113 Stop,
115 Length,
117 ToolUse,
119 Error(String),
121 Aborted(String),
123}
124
125impl StopReason {
126 pub fn is_failure(&self) -> bool {
128 matches!(self, Self::Error(_) | Self::Aborted(_))
129 }
130}
131
132#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
138pub struct Fingerprint(u64);
139
140impl Serialize for Fingerprint {
141 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
142 serializer.serialize_str(&format!("{:016x}", self.0))
143 }
144}
145
146impl<'de> Deserialize<'de> for Fingerprint {
147 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
148 let text = std::borrow::Cow::<'de, str>::deserialize(deserializer)?;
149 u64::from_str_radix(&text, 16)
150 .map(Self)
151 .map_err(|_| serde::de::Error::custom("a fingerprint is 16 hex digits"))
152 }
153}
154
155impl Fingerprint {
156 pub fn of(value: &impl Serialize) -> Self {
159 let mut hash = Fnv(0xcbf2_9ce4_8422_2325);
160 if let Ok(value) = serde_json::to_value(value) {
163 let _ = serde_json::to_writer(&mut hash, &Canonical(&value));
164 }
165 Self(hash.0)
166 }
167
168 pub(crate) fn of_canonical(value: &impl Serialize) -> Self {
172 let mut hash = Fnv(0xcbf2_9ce4_8422_2325);
173 let _ = serde_json::to_writer(&mut hash, value);
174 Self(hash.0)
175 }
176}
177
178struct Fnv(u64);
180
181impl std::io::Write for Fnv {
182 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
183 for byte in bytes {
184 self.0 ^= u64::from(*byte);
185 self.0 = self.0.wrapping_mul(0x0000_0100_0000_01b3);
186 }
187 Ok(bytes.len())
188 }
189
190 fn flush(&mut self) -> std::io::Result<()> {
191 Ok(())
192 }
193}
194
195pub(crate) struct Canonical<'a, T: ?Sized>(pub(crate) &'a T);
199
200impl Serialize for Canonical<'_, serde_json::Map<String, serde_json::Value>> {
201 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
202 let mut fields: Vec<_> = self.0.iter().collect();
203 fields.sort_by_key(|(left, _)| *left);
204 serializer.collect_map(
205 fields
206 .into_iter()
207 .map(|(key, value)| (key, Canonical(value))),
208 )
209 }
210}
211
212impl Serialize for Canonical<'_, serde_json::Value> {
213 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
214 match self.0 {
215 serde_json::Value::Object(fields) => Canonical(fields).serialize(serializer),
216 serde_json::Value::Array(values) => {
217 serializer.collect_seq(values.iter().map(Canonical))
218 }
219 serde_json::Value::Number(number) => match number
220 .as_f64()
221 .filter(|float| number.is_f64() && float.fract() == 0.0)
222 .and_then(|float| format!("{float:.0}").parse::<serde_json::Number>().ok())
223 {
224 Some(whole) => whole.serialize(serializer),
225 None => number.serialize(serializer),
226 },
227 value => value.serialize(serializer),
228 }
229 }
230}
231
232#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
239pub struct Native {
240 pub item: serde_json::Value,
242 pub fingerprint: Fingerprint,
244 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub baseline: Option<serde_json::Value>,
251 #[serde(default, skip_serializing_if = "crate::json_utils::is_false")]
254 pub edited: bool,
255 #[serde(default, skip_serializing_if = "Option::is_none")]
261 pub blob_slot: Option<Cow<'static, str>>,
262}
263
264#[derive(Clone, Copy, Debug, PartialEq, Eq)]
266pub struct Blob<'a> {
267 pub key: &'a str,
269 pub value: &'a str,
271}
272
273impl Native {
274 pub fn reasoning_blob(&self) -> Option<Blob<'_>> {
291 let key = self.blob_slot.as_deref()?;
292 let value = self.item.pointer(key)?.as_str()?;
293 (!value.is_empty()).then_some(Blob { key, value })
294 }
295
296 pub(crate) fn find_blob(&mut self, slots: &[&'static str]) {
299 let holds = |slot: &&str| {
300 self.item
301 .pointer(slot)
302 .and_then(serde_json::Value::as_str)
303 .is_some_and(|value| !value.is_empty())
304 };
305 self.blob_slot = slots.iter().copied().find(holds).map(Cow::Borrowed);
306 }
307}
308
309pub(super) fn baseline(value: serde_json::Value) -> serde_json::Value {
310 use serde_json::Value;
311 match value {
312 Value::Number(number) if !number.is_f64() => Value::String(format!("i:{number}")),
313 Value::String(text) => Value::String(format!("s:{text}")),
314 Value::Array(values) => Value::Array(values.into_iter().map(baseline).collect()),
315 Value::Object(fields) => Value::Object(
316 fields
317 .into_iter()
318 .map(|(key, value)| (key, baseline(value)))
319 .collect(),
320 ),
321 value => value,
322 }
323}
324
325pub(super) fn matches_baseline(saved: &serde_json::Value, current: &serde_json::Value) -> bool {
326 use serde_json::Value;
327 match (saved, current) {
328 (Value::String(saved), Value::Number(number)) => {
329 let Some(integer) = saved.strip_prefix("i:") else {
330 return false;
331 };
332 if number.is_f64() {
333 number
334 .as_f64()
335 .filter(|value| value.fract() == 0.0)
336 .is_some_and(|value| format!("{value:.0}") == integer)
337 } else {
338 number.to_string() == integer
339 }
340 }
341 (Value::String(saved), Value::String(current)) => {
342 saved.strip_prefix("s:") == Some(current.as_str())
343 }
344 (Value::Array(saved), Value::Array(current)) => {
345 saved.len() == current.len()
346 && saved
347 .iter()
348 .zip(current)
349 .all(|(saved, current)| matches_baseline(saved, current))
350 }
351 (Value::Object(saved), Value::Object(current)) => {
352 saved.len() == current.len()
353 && saved.iter().all(|(key, saved)| {
354 current
355 .get(key)
356 .is_some_and(|current| matches_baseline(saved, current))
357 })
358 }
359 (Value::Number(_), Value::Number(_))
360 | (Value::Bool(_), Value::Bool(_))
361 | (Value::Null, Value::Null) => saved == current,
362 _ => false,
363 }
364}
365
366pub(crate) fn lenient<'de, D: serde::Deserializer<'de>>(
370 deserializer: D,
371) -> Result<Option<Native>, D::Error> {
372 let value = Option::<serde_json::Value>::deserialize(deserializer)?;
373 Ok(value.and_then(|value| Native::deserialize(value).ok()))
374}
375
376#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
379pub struct Opaque {
380 pub item: serde_json::Value,
382 pub replay: bool,
385}
386
387impl Opaque {
388 pub fn kind(&self) -> Option<&str> {
390 self.item.get("type").and_then(serde_json::Value::as_str)
391 }
392}
393
394#[cfg(test)]
395mod tests;