Skip to main content

rig_core/providers/xiaomimimo/
extension.rs

1//! Xiaomi MiMo's typed reply extras on its OpenAI-format and Messages APIs.
2//! MiMo has no typed request option: its web search is a server tool,
3//! declared in `additional_params.tools`.
4//!
5//! ```
6//! use rig_core::completion::CompletionResponse;
7//! use rig_core::providers::xiaomimimo::extension::XiaomiMimoExt;
8//!
9//! fn annotations(reply: &CompletionResponse) -> usize {
10//!     reply
11//!         .extras_lossy::<XiaomiMimoExt>()
12//!         .annotations
13//!         .map_or(0, |annotations| annotations.len())
14//! }
15//! # let _ = annotations;
16//! ```
17
18use serde::Serialize;
19use serde_json::Value;
20
21use crate::completion::provider_options::reply_field;
22use crate::completion::{ExtensionOptions, ProviderExtension, ReplyExtras};
23use crate::message::Api;
24use crate::providers::anthropic::extension::MessagesStop;
25use crate::providers::anthropic::wire::MESSAGES_API;
26
27/// Xiaomi MiMo's extension marker.
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
29pub struct XiaomiMimoExt;
30
31impl ProviderExtension for XiaomiMimoExt {
32    const PROVIDER: &'static str = super::PROVIDER_NAME;
33    type Options = XiaomiMimoOptions;
34    type Extras = XiaomiMimoExtras;
35}
36
37/// Xiaomi MiMo's request options: none.
38#[non_exhaustive]
39#[derive(Clone, Debug, Default, PartialEq, Serialize)]
40pub struct XiaomiMimoOptions {}
41
42impl XiaomiMimoOptions {
43    /// No option set.
44    pub fn new() -> Self {
45        Self::default()
46    }
47}
48
49impl ExtensionOptions for XiaomiMimoOptions {
50    type Ext = XiaomiMimoExt;
51}
52
53/// Xiaomi MiMo's reply fields. Each is `None` when the reply lacks it.
54#[non_exhaustive]
55#[derive(Clone, Debug, Default, PartialEq)]
56pub struct XiaomiMimoExtras {
57    /// The message's annotations, such as web-search citations.
58    /// OpenAI-format API only.
59    pub annotations: Option<Vec<Value>>,
60    /// `stop_reason`, verbatim. Messages route.
61    pub stop_reason: Option<String>,
62    /// `stop_sequence`: the stop sequence the turn ended on. Messages route.
63    pub stop_sequence: Option<String>,
64}
65
66impl ReplyExtras for XiaomiMimoExtras {
67    fn from_reply(api: &Api, raw: &Value) -> Result<Self, serde_json::Error> {
68        if api.as_str() == MESSAGES_API {
69            let MessagesStop {
70                stop_reason,
71                stop_sequence,
72            } = MessagesStop::read("Xiaomi MiMo", api, raw)?;
73            return Ok(Self {
74                stop_reason,
75                stop_sequence,
76                ..Self::default()
77            });
78        }
79        if api.as_str() != "openai.chat" {
80            return Ok(Self::default());
81        }
82        Ok(Self {
83            annotations: reply_field(raw, "/choices/0/message/annotations")?,
84            ..Self::default()
85        })
86    }
87}
88
89#[cfg(test)]
90mod tests;