steer_core/api/openai/
client.rs

1use super::OpenAIMode;
2use super::chat;
3use super::responses;
4use crate::api::error::ApiError;
5use crate::api::provider::{CompletionResponse, Provider};
6use crate::app::conversation::Message;
7use crate::config::model::{ModelId, ModelParameters};
8use async_trait::async_trait;
9use steer_tools::ToolSchema;
10use tokio_util::sync::CancellationToken;
11
12/// Unified OpenAI client that supports both the Chat and Responses APIs.
13///
14/// The client internally manages two separate clients for the different API modes
15/// and delegates requests based on the configured default mode.
16pub struct OpenAIClient {
17    responses_client: responses::Client,
18    chat_client: chat::Client,
19    default_mode: OpenAIMode,
20}
21
22impl OpenAIClient {
23    /// Create a new OpenAI client with a specific mode.
24    pub fn with_mode(api_key: String, mode: OpenAIMode) -> Self {
25        Self {
26            responses_client: responses::Client::new(api_key.clone()),
27            chat_client: chat::Client::new(api_key),
28            default_mode: mode,
29        }
30    }
31
32    /// Create a new OpenAI client with a custom base URL and mode.
33    pub fn with_base_url_mode(api_key: String, base_url: Option<String>, mode: OpenAIMode) -> Self {
34        Self {
35            responses_client: responses::Client::with_base_url(api_key.clone(), base_url.clone()),
36            chat_client: chat::Client::with_base_url(api_key, base_url),
37            default_mode: mode,
38        }
39    }
40}
41
42#[async_trait]
43impl Provider for OpenAIClient {
44    fn name(&self) -> &'static str {
45        "openai"
46    }
47
48    async fn complete(
49        &self,
50        model_id: &ModelId,
51        messages: Vec<Message>,
52        system: Option<String>,
53        tools: Option<Vec<ToolSchema>>,
54        call_options: Option<ModelParameters>,
55        token: CancellationToken,
56    ) -> Result<CompletionResponse, ApiError> {
57        match self.default_mode {
58            OpenAIMode::Responses => {
59                self.responses_client
60                    .complete(model_id, messages, system, tools, call_options, token)
61                    .await
62            }
63            OpenAIMode::Chat => {
64                self.chat_client
65                    .complete(model_id, messages, system, tools, call_options, token)
66                    .await
67            }
68        }
69    }
70}