Skip to main content

rig_core/providers/venice/
client.rs

1//! Venice client, provider extension, and capability wiring.
2
3use crate::client::{self, BearerAuth, DebugExt, Provider};
4use crate::model::Model;
5
6// ================================================================
7// Venice Client
8// ================================================================
9// The base URL carries the `/api/v1` prefix, so request paths are bare
10// (`/chat/completions`), matching every other OpenAI-compatible provider here.
11/// Venice's API base URL.
12pub const VENICE_API_BASE_URL: &str = "https://api.venice.ai/api/v1";
13
14/// Provider extension type for Venice.
15#[derive(Debug, Default, Clone, Copy)]
16pub struct VeniceExt;
17
18/// Builder state for [`VeniceExt`].
19#[derive(Debug, Default, Clone, Copy)]
20pub struct VeniceBuilder;
21
22type VeniceApiKey = BearerAuth;
23
24/// Venice client.
25pub type Client<H = reqwest::Client> = client::Client<VeniceExt, H>;
26/// Builder for the Venice [`Client`].
27pub type ClientBuilder<H = crate::markers::Missing> =
28    client::ClientBuilder<VeniceBuilder, VeniceApiKey, H>;
29
30impl Provider for VeniceExt {
31    type Builder = VeniceBuilder;
32
33    const VERIFY_PATH: &'static str = "/models";
34}
35
36impl DebugExt for VeniceExt {}
37
38impl crate::providers::openai::completion::OpenAICompatibleProvider for VeniceExt {
39    const PROVIDER_NAME: &'static str = "venice";
40
41    type StreamingUsage = crate::providers::openai::Usage;
42
43    // Venice echoes its resolved `venice_parameters` block (including web
44    // search citations) and a per-request `cost` alongside the OpenAI-shaped
45    // payload; the Venice response type preserves both.
46    type Response = super::completion::CompletionResponse;
47}
48
49client::impl_capabilities!(
50    VeniceExt,
51    completion = super::completion::CompletionModel<H>,
52    embeddings = super::embedding::EmbeddingModel<H>,
53    transcription = super::transcription::TranscriptionModel<H>,
54    model_listing = VeniceModelLister<H>,
55    image_generation = super::image_generation::ImageGenerationModel<H>,
56    audio_generation = super::audio_generation::AudioGenerationModel<H>,
57);
58
59client::impl_default_provider_builder!(
60    VeniceBuilder => VeniceExt,
61    api_key = VeniceApiKey,
62    base_url = VENICE_API_BASE_URL,
63);
64
65client::impl_provider_client!(
66    Client,
67    input = String,
68    api_key_env = "VENICE_API_KEY",
69    base_url_env_first = "VENICE_BASE_URL",
70);
71
72/// A `GET /models` entry.
73///
74/// Venice returns the OpenAI-compatible envelope plus a `type` discriminator
75/// (`text`, `image`, `embedding`, `tts`, `asr`, …) and a `model_spec` object;
76/// only the fields [`Model`] can carry are decoded here.
77#[derive(Debug, serde::Deserialize)]
78struct ListModelEntry {
79    id: String,
80    #[serde(default)]
81    owned_by: Option<String>,
82}
83
84impl From<ListModelEntry> for Model {
85    fn from(value: ListModelEntry) -> Self {
86        let mut model = Model::from_id(value.id);
87        model.owned_by = value.owned_by;
88        model
89    }
90}
91
92crate::providers::internal::model_listing::impl_model_lister!(
93    /// [`ModelLister`](crate::client::ModelLister) implementation for the
94    /// Venice API (`GET /models`).
95    ///
96    /// Venice also accepts a `?type=` filter; [`list_all`](crate::client::ModelLister::list_all) requests the
97    /// unfiltered listing, which Venice answers with its text models.
98    VeniceModelLister,
99    Client<H>,
100    ListModelEntry,
101    "Venice",
102    "/models"
103);
104
105#[cfg(test)]
106mod tests {
107    #[test]
108    fn test_client_initialization() {
109        let _client =
110            crate::providers::venice::Client::new("dummy-key").expect("Client::new() failed");
111        let _client_from_builder = crate::providers::venice::Client::builder()
112            .api_key("dummy-key")
113            .build()
114            .expect("Client::builder() failed");
115    }
116}