Skip to main content

llm/providers/openai_compatible/
mod.rs

1#![doc = include_str!(concat!(env!("OUT_DIR"), "/docs/openai_compatible.md"))]
2
3pub mod config;
4pub mod generic;
5pub mod streaming;
6pub mod types;
7
8use async_openai::types::chat::ChatCompletionStreamOptions;
9use schemars::Schema;
10
11use crate::providers::openai::mappers::map_tools;
12use crate::{Context, LlmError};
13use types::CompatibleChatRequest;
14
15pub use config::AetherOpenAiConfig;
16pub use streaming::{create_custom_stream_generic, process_compatible_stream};
17pub use types::ChatCompletionStreamResponse;
18
19/// Selects what `prompt_cache_key` is sent to a LLM provider.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum PromptCacheKeySource {
22    /// Do not send a prompt cache key.
23    Omit,
24    /// Use a hash of the prompt prefix.
25    Prefix,
26    /// Use a stable session identifier.
27    SessionAffinity,
28}
29
30impl PromptCacheKeySource {
31    pub(crate) fn resolve(self, context: &Context) -> Option<&str> {
32        match self {
33            Self::Omit => None,
34            Self::Prefix => context.prompt_cache_key(),
35            Self::SessionAffinity => context.session_affinity_key(),
36        }
37    }
38}
39
40/// Build a chat completion request from a context
41///
42/// This is shared logic for OpenAI-compatible providers like `OpenRouter` and Z.ai.
43/// Uses `CompatibleChatRequest` which preserves `reasoning_content` on assistant messages.
44pub(crate) fn build_chat_request(
45    model: &str,
46    context: &Context,
47    tool_schema_transform: Option<fn(&mut Schema)>,
48) -> Result<CompatibleChatRequest, LlmError> {
49    let messages = types::map_messages(context.messages())?;
50    let tools =
51        if context.tools().is_empty() { None } else { Some(map_tools(context.tools(), tool_schema_transform)?) };
52    let settings = context.model_settings();
53
54    Ok(CompatibleChatRequest {
55        model: model.to_string(),
56        messages,
57        stream: Some(true),
58        tools,
59        stream_options: Some(ChatCompletionStreamOptions { include_usage: Some(true), include_obfuscation: None }),
60        reasoning_effort: context.reasoning_effort(),
61        temperature: settings.temperature,
62        top_p: settings.top_p,
63        max_tokens: settings.max_tokens,
64        prompt_cache_key: None,
65    })
66}