Skip to main content

rig_core/providers/anthropic/
modality.rs

1//! Model listing and credential verification for a Messages-format
2//! provider: `GET /v1/models`, paged by cursor, and the same endpoint read
3//! for its status alone.
4//!
5//! ```no_run
6//! use rig_core::providers::anthropic::Anthropic;
7//!
8//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
9//! let models = Anthropic::from_env()?.list_models().await?;
10//! # let _ = models;
11//! # Ok(())
12//! # }
13//! ```
14
15use serde::{Deserialize, Serialize};
16
17use super::wire::AnthropicConfig;
18use crate::error::EncodeError;
19use crate::model::{ModelInfo, ModelList};
20pub use crate::operation::VerifyDecoder;
21use crate::operation::{ModelListing, ModelPage, Verify as VerifyOp};
22use crate::wire::{Body, Descriptor, DocumentDecoder, Encoded, Framing, Mode, Wire};
23
24impl AnthropicConfig {
25    /// The model-listing wire.
26    pub(crate) fn models(&self) -> Models {
27        Models {
28            provider: self.clone(),
29        }
30    }
31
32    /// The credential-check wire.
33    pub(crate) fn verify(&self) -> Verify {
34        Verify {
35            provider: self.clone(),
36        }
37    }
38}
39
40/// The model-listing wire: `GET /v1/models`, cursor-paged.
41#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub struct Models {
43    /// The provider this wire speaks to.
44    pub provider: AnthropicConfig,
45}
46
47impl Wire for Models {
48    type Op = ModelListing;
49    type Payload = crate::wire::Encoded;
50    type Frame = crate::wire::WireFrame;
51    type Event = ModelsPage;
52    type Decoder<'id> = DocumentDecoder<ModelListing, ModelsPage>;
53    type Reassembler = crate::wire::document::Unreassembled;
54
55    fn describe(&self) -> Descriptor<'_> {
56        Descriptor::new(self.provider.dialect.name)
57    }
58
59    fn encode(&self, cursor: Option<String>, _mode: Mode) -> Result<Encoded, EncodeError> {
60        Ok(Encoded::new(
61            self.models_request(cursor.as_deref())?,
62            Framing::Whole,
63        ))
64    }
65
66    fn decoder<'id>(&self) -> Self::Decoder<'id> {
67        DocumentDecoder::new(
68            |body| crate::providers::internal::wire::classify_marker_keyed_frame(body, &["data"]),
69            |page| {
70                // Missing or empty cursors would repeatedly fetch page one, even with has_more.
71                let next = page
72                    .last_id
73                    .filter(|cursor| page.has_more && !cursor.is_empty());
74                Ok(ModelPage {
75                    models: ModelList::new(page.data.into_iter().map(ModelInfo::from).collect()),
76                    next,
77                })
78            },
79        )
80    }
81}
82
83impl Models {
84    /// One page's request, after `cursor` when the previous page named one.
85    fn models_request(&self, cursor: Option<&str>) -> Result<http::Request<Body>, EncodeError> {
86        let uri = match cursor {
87            Some(cursor) => format!(
88                "{}{}",
89                self.provider.base_url,
90                crate::providers::internal::with_query_pairs("/v1/models", &[("after_id", cursor)],)
91            ),
92            None => format!("{}/v1/models", self.provider.base_url),
93        };
94        self.provider
95            .headers(http::Request::get(uri))
96            .body(Body::empty())
97            .map_err(EncodeError::from)
98    }
99}
100
101/// One page of `GET /v1/models`.
102#[derive(Debug, Deserialize)]
103#[doc(hidden)]
104pub struct ModelsPage {
105    data: Vec<ModelEntry>,
106    #[serde(default)]
107    has_more: bool,
108    #[serde(default)]
109    last_id: Option<String>,
110}
111
112#[derive(Debug, Deserialize)]
113struct ModelEntry {
114    id: String,
115    display_name: String,
116}
117
118impl From<ModelEntry> for ModelInfo {
119    fn from(entry: ModelEntry) -> Self {
120        ModelInfo::new(entry.id, entry.display_name)
121    }
122}
123
124/// The credential-check wire: `GET /v1/models`, status only, decoded by
125/// the shared [`VerifyDecoder`].
126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
127pub struct Verify {
128    /// The provider this wire speaks to.
129    pub provider: AnthropicConfig,
130}
131
132impl Wire for Verify {
133    type Op = VerifyOp;
134    type Payload = crate::wire::Encoded;
135    type Frame = crate::wire::WireFrame;
136    type Event = ();
137    type Decoder<'id> = VerifyDecoder;
138    type Reassembler = crate::wire::document::Unreassembled;
139
140    fn describe(&self) -> Descriptor<'_> {
141        Descriptor::new(self.provider.dialect.name)
142    }
143
144    fn encode(&self, _request: (), _mode: Mode) -> Result<Encoded, EncodeError> {
145        let request = self
146            .provider
147            .headers(http::Request::get(format!(
148                "{}/v1/models",
149                self.provider.base_url
150            )))
151            .body(Body::empty())?;
152        Ok(Encoded::new(request, Framing::Whole))
153    }
154
155    fn decoder<'id>(&self) -> Self::Decoder<'id> {
156        VerifyDecoder
157    }
158}