Skip to main content

vtcode_llm/
provider.rs

1#![expect(
2    unused_results,
3    reason = "Provider abstraction caches and compatibility adapters intentionally discard replacement results after state updates."
4)]
5
6//! Universal LLM provider abstraction with API-specific role handling
7//!
8//! This module provides a unified interface for different LLM providers (OpenAI, Anthropic, Gemini)
9//! while properly handling their specific requirements for message roles and tool calling.
10//!
11//! ## Message Role Mapping
12//!
13//! Different LLM providers have varying support for message roles, especially for tool calling:
14//!
15//! ### OpenAI API
16//! - **Full Support**: `system`, `user`, `assistant`, `tool`
17//! - **Tool Messages**: Must include `tool_call_id` to reference the original tool call
18//! - **Tool Calls**: Only `assistant` messages can contain `tool_calls`
19//!
20//! ### Anthropic API
21//! - **Standard Roles**: `user`, `assistant`
22//! - **System Messages**: Can be hoisted to system parameter or treated as user messages
23//! - **Tool Responses**: Converted to `user` messages (no separate tool role)
24//! - **Tool Choice**: Supports `auto`, `any`, `tool`, `none` modes
25//!
26//! ### Gemini API
27//! - **Conversation Roles**: Only `user` and `model` (not `assistant`)
28//! - **System Messages**: Handled separately as `systemInstruction` parameter
29//! - **Tool Responses**: Converted to `user` messages with `functionResponse` format
30//! - **Function Calls**: Uses `functionCall` in `model` messages
31//!
32//! ## Best Practices
33//!
34//! 1. Always use `MessageRole::tool_response()` constructor for tool responses
35//! 2. Validate messages using `validate_for_provider()` before sending
36//! 3. Use appropriate role mapping methods for each provider
37//! 4. Handle provider-specific constraints (e.g., Gemini's system instruction requirement)
38//!
39//! ## Example Usage
40//!
41//! ```rust,ignore
42//! use vtcode_llm::provider::{Message, MessageRole};
43//!
44//! // Create a proper tool response message
45//! let tool_response = Message::tool_response(
46//!     "call_123".to_string(),
47//!     "Tool execution completed successfully".to_string()
48//! );
49//!
50//! // Validate for specific provider
51//! tool_response.validate_for_provider("openai").unwrap();
52//! ```
53
54mod call;
55mod message;
56mod provider_trait;
57mod request;
58mod response;
59mod responses_continuation;
60#[cfg(test)]
61mod tests;
62mod tool;
63
64pub use call::{FunctionCall, ToolCall};
65pub use message::{AssistantPhase, ContentPart, ImageDetail, Message, MessageClearAt, MessageContent, MessageRole};
66pub use provider_trait::{
67    ContextWindowProvider, LLMError, LLMErrorMetadata, LLMProvider, ProviderCapabilities, get_cached_capabilities,
68};
69pub(crate) use provider_trait::{
70    GENERIC_REASONING_EFFORTS, catalog_context_window, catalog_or_explicit_reasoning_efforts,
71    catalog_or_generic_reasoning_efforts, catalog_reasoning_efforts,
72};
73pub use request::{
74    AnthropicOptionalStringOverride, AnthropicOptionalU32Override, AnthropicRequestOverrides, AnthropicThinkingConfig,
75    AnthropicThinkingDisplayOverride, AnthropicThinkingModeOverride, CodingAgentSettings, FallbackModel, LLMRequest,
76    ParallelToolConfig, PromptCacheProfile, ResponsesCompactionOptions, SamplingOverrides, SpecificFunctionChoice,
77    SpecificToolChoice, ToolChoice,
78};
79pub use response::{
80    BorrowedLLMStream, FinishReason, LLMNormalizedStream, LLMResponse, LLMStream, LLMStreamEvent,
81    NormalizedStreamEvent, ReasoningSource, Usage,
82};
83pub use responses_continuation::{
84    PreparedResponsesRequest, ResponsesContinuationState, prepare_openai_responses_request,
85    prepare_responses_continuation_request, records_responses_continuation_state, responses_continuation_key,
86    supports_responses_chaining, uses_incremental_responses_history,
87};
88pub use tool::{
89    FunctionDefinition, GrammarDefinition, ShellToolDefinition, ToolDefinition, ToolNamespace, ToolSearchAlgorithm,
90};