rig_core/model/
listing.rs1use crate::error::ProviderError;
11use serde::{Deserialize, Serialize};
12use std::fmt;
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
16pub struct ModelInfo {
17 pub id: String,
19
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub name: Option<String>,
23
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub description: Option<String>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
30 #[serde(rename = "type")]
31 pub r#type: Option<String>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub created_at: Option<u64>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub owned_by: Option<String>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub context_length: Option<u32>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
48 pub max_output_tokens: Option<u32>,
49}
50
51impl ModelInfo {
52 pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
54 Self {
55 id: id.into(),
56 name: Some(name.into()),
57 description: None,
58 r#type: None,
59 created_at: None,
60 owned_by: None,
61 context_length: None,
62 max_output_tokens: None,
63 }
64 }
65
66 pub fn from_id(id: impl Into<String>) -> Self {
68 Self {
69 id: id.into(),
70 name: None,
71 description: None,
72 r#type: None,
73 created_at: None,
74 owned_by: None,
75 context_length: None,
76 max_output_tokens: None,
77 }
78 }
79
80 pub fn display_name(&self) -> &str {
82 self.name.as_ref().unwrap_or(&self.id)
83 }
84}
85
86impl fmt::Display for ModelInfo {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 write!(f, "{}", self.display_name())
89 }
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct ModelList {
95 pub data: Vec<ModelInfo>,
97}
98
99impl ModelList {
100 pub fn new(data: Vec<ModelInfo>) -> Self {
102 Self { data }
103 }
104
105 pub fn is_empty(&self) -> bool {
107 self.data.is_empty()
108 }
109
110 pub fn len(&self) -> usize {
112 self.data.len()
113 }
114
115 pub fn iter(&self) -> std::slice::Iter<'_, ModelInfo> {
117 self.data.iter()
118 }
119}
120
121impl IntoIterator for ModelList {
122 type Item = ModelInfo;
123 type IntoIter = std::vec::IntoIter<ModelInfo>;
124
125 fn into_iter(self) -> Self::IntoIter {
126 self.data.into_iter()
127 }
128}
129
130impl<'a> IntoIterator for &'a ModelList {
131 type Item = &'a ModelInfo;
132 type IntoIter = std::slice::Iter<'a, ModelInfo>;
133
134 fn into_iter(self) -> Self::IntoIter {
135 self.data.iter()
136 }
137}
138
139const RESPONSE_BODY_PREVIEW_LIMIT: usize = 2048;
140
141fn format_response_body_preview(body: &[u8]) -> String {
142 let preview_len = body.len().min(RESPONSE_BODY_PREVIEW_LIMIT);
143 let preview_bytes = body.get(..preview_len).unwrap_or(body);
144 let mut preview = String::from_utf8_lossy(preview_bytes).into_owned();
145
146 if body.len() > RESPONSE_BODY_PREVIEW_LIMIT {
147 preview.push_str(&format!(
148 "\n...<truncated {} bytes>",
149 body.len() - RESPONSE_BODY_PREVIEW_LIMIT
150 ));
151 }
152
153 preview
154}
155
156fn format_response_context(
157 provider: &str,
158 path: &str,
159 details: impl fmt::Display,
160 body: &[u8],
161) -> String {
162 format!(
163 "provider={provider}\npath={path}\n{details}\nbody_bytes={}\nresponse_body_preview:\n{}",
164 body.len(),
165 format_response_body_preview(body)
166 )
167}
168
169pub(crate) fn parse_error(
172 provider: &str,
173 path: &str,
174 details: impl fmt::Display,
175 body: &[u8],
176) -> ProviderError {
177 ProviderError::Response(format_response_context(provider, path, details, body))
178}
179
180pub(crate) fn with_route(error: ProviderError, provider: &str, path: &str) -> ProviderError {
183 match error {
184 ProviderError::ProviderResponse(mut response) => {
185 response.route = Some(format!("provider={provider} path={path}"));
186 ProviderError::ProviderResponse(response)
187 }
188 ProviderError::Json(error) => parse_error(
189 provider,
190 path,
191 format_args!("parse_error"),
192 error.to_string().as_bytes(),
193 ),
194 ProviderError::Response(message) => parse_error(
195 provider,
196 path,
197 format_args!("parse_error"),
198 message.as_bytes(),
199 ),
200 other => other,
201 }
202}
203
204#[cfg(test)]
205mod tests;