mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
feat(rust): add standalone typed LLM wire contracts (#45188)
* refactor(rust): add typed LLM wire contracts to llms-types Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * style(rust): drop blank lines left in new wire types Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(rust): complete additive wire type contracts * feat(rust): complete additive nested wire contracts * feat(rust): give Messages content blocks concrete per-block contracts Replace the shared all-optional ContentBlockPayload with one struct per documented block and nested tagged unions for server tool results, so required fields from the official Messages reference are enforced. Make CustomTool a plain struct with required name and input_schema, add MessagesToolParam plus the toolset and undated tool-search tags, model MiniMax media sources as a tagged union, require documented fields on chat audio, image URLs, and logprobs, and rename the new block enum to MessagesContentPart so it no longer shadows the streaming struct. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(rust): tighten new wire contracts against the official references Give each Messages builtin tool a concrete contract with its documented required fields, fixed names, and typed allowed callers in place of the shared all-optional ToolDefinition bag. Make MCP tool result content and fallback triggers optional as the request params document, and narrow tool search references, web fetch content, and bash results to their documented block types. Keep unmodeled Responses output items and new usage iteration and stop detail types as Recognized values instead of rejecting the whole payload, accept find_in_page and optional open_page URLs, preserve JSON Schema property order, and move prompt_cache_breakpoint to Chat Completions content parts where OpenAI documents it. Drop OCR table and key/value types that only matched Azure Document Intelligence and would reject other providers' normalized passthrough, along with unsourced bounding box and applied edit fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(rust): type every documented Responses output item and close wire gaps Add the 18 remaining documented Responses output item variants with concrete nested actions, outputs, and safety checks, and add the documented namespace, async, caller, and image generation fields to the existing items. Restrict MCP tool result content to a string or text blocks, model the Messages diagnostics cache-miss union and its request param with null distinct from absent, and accept draft-07 tuple items and prefixItems in JSON Schema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(rust): keep Messages contract assertions in test bodies Compare built-in tool decodes against fully built expected values and split the required-field rejections per type, so no case carries its assertions in a callback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(rust): cite the Mistral OCR reference beside the OCR wire types Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
82a5d9842a
commit
35b0992533
30 changed files with 5574 additions and 4 deletions
2
litellm-rust/Cargo.lock
generated
2
litellm-rust/Cargo.lock
generated
|
|
@ -4248,6 +4248,7 @@ dependencies = [
|
|||
name = "litellm-llms-types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"indexmap 2.14.0",
|
||||
"macro_rules_attribute",
|
||||
"rstest",
|
||||
"schemars 1.2.2",
|
||||
|
|
@ -6512,6 +6513,7 @@ checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a"
|
|||
dependencies = [
|
||||
"chrono",
|
||||
"dyn-clone",
|
||||
"indexmap 2.14.0",
|
||||
"ref-cast",
|
||||
"schemars_derive 1.2.2",
|
||||
"serde",
|
||||
|
|
|
|||
|
|
@ -67,3 +67,9 @@ The same ownership rule applies to Messages, Responses, Chat Completions, OCR, a
|
|||
- Follow the workspace test-placement and `rstest` rules
|
||||
- Test provider transformations, header policy, and stream execution in their owning crates
|
||||
- Do not test import locations or Rust source structure as substitutes for behavior
|
||||
|
||||
# references
|
||||
|
||||
## json_schema.rs
|
||||
|
||||
- https://json-schema.org/draft/2020-12/json-schema-core
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[features]
|
||||
schema = ["dep:schemars"]
|
||||
schema = ["dep:schemars", "schemars/indexmap2"]
|
||||
|
||||
[dependencies]
|
||||
indexmap = { version = "2.14.0", features = ["serde"] }
|
||||
macro_rules_attribute.workspace = true
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde.workspace = true
|
||||
|
|
|
|||
36
litellm-rust/crates/llms-types/src/formats/AGENTS.md
Normal file
36
litellm-rust/crates/llms-types/src/formats/AGENTS.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# references
|
||||
|
||||
These links describe shared format fields and discriminators. The official API reference is authoritative, and SDK sources only clarify shapes the reference leaves implicit. These contracts are partial typed projections, not exhaustive upstream schemas
|
||||
|
||||
## chat_completions.rs and chat_completions/content.rs
|
||||
|
||||
- https://developers.openai.com/api/reference/resources/chat
|
||||
|
||||
SDK wire definitions:
|
||||
|
||||
- https://github.com/openai/openai-python/blob/main/src/openai/types/chat/chat_completion_content_part_param.py
|
||||
|
||||
## responses/output.rs and responses/streaming_websocket.rs
|
||||
|
||||
- https://developers.openai.com/api/reference/resources/responses
|
||||
- https://developers.openai.com/api/reference/resources/responses/streaming-events
|
||||
|
||||
SDK wire definitions:
|
||||
|
||||
- https://github.com/openai/openai-python/blob/main/src/openai/types/responses/response.py
|
||||
- https://github.com/openai/openai-python/blob/main/src/openai/types/responses/response_output_item.py
|
||||
- https://github.com/openai/openai-python/blob/main/src/openai/types/responses/mcp_tool_call_error.py
|
||||
|
||||
## batches.rs
|
||||
|
||||
- https://developers.openai.com/api/reference/resources/batches
|
||||
|
||||
## audio_transcription.rs
|
||||
|
||||
- https://developers.openai.com/api/reference/resources/audio/subresources/transcriptions
|
||||
|
||||
## ocr.rs
|
||||
|
||||
- https://docs.mistral.ai/api/endpoint/ocr
|
||||
|
||||
Messages references live in `messages/AGENTS.md`. `LiteLLMOcrResponse` follows the Mistral OCR response shape, and `OcrBoundingBox` describes the corner coordinates providers copy into the normalized `bbox`. Normalized `tables` and `keyValuePairs` stay open because providers pass through different native shapes
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
use serde_json::{Map, Value};
|
||||
use strum::IntoStaticStr;
|
||||
|
||||
mod content;
|
||||
pub use content::{
|
||||
ChatContentPart, ChatFile, ChatInputAudio, ChatLogprobs, ChatMediaUrl, ChatMediaUrlParameters,
|
||||
ChatTokenLogprob, ChatTopLogprob, ChatVideoMetadata, PromptCacheBreakpoint, PromptCacheMode,
|
||||
};
|
||||
|
||||
/// Reasoning effort level accepted or applied by the model.
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq, IntoStaticStr)]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::formats::messages::{CacheControl, CitationsConfig, ContentSource};
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ChatContentPart {
|
||||
Text {
|
||||
text: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
cache_control: Option<CacheControl>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
ImageUrl {
|
||||
image_url: ChatMediaUrl,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
VideoUrl {
|
||||
video_url: ChatMediaUrl,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
InputAudio {
|
||||
input_audio: ChatInputAudio,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
File {
|
||||
file: Box<ChatFile>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Document {
|
||||
source: Box<ContentSource>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
title: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
context: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
citations: Option<CitationsConfig>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Refusal {
|
||||
refusal: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct PromptCacheBreakpoint {
|
||||
pub mode: PromptCacheMode,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PromptCacheMode {
|
||||
Explicit,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum ChatMediaUrl {
|
||||
Url(String),
|
||||
Parameters(Box<ChatMediaUrlParameters>),
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ChatMediaUrlParameters {
|
||||
pub url: String,
|
||||
pub detail: Option<String>,
|
||||
pub format: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ChatInputAudio {
|
||||
pub data: String,
|
||||
pub format: String,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ChatFile {
|
||||
pub file_data: Option<String>,
|
||||
pub file_id: Option<String>,
|
||||
pub filename: Option<String>,
|
||||
pub format: Option<String>,
|
||||
pub detail: Option<String>,
|
||||
pub video_metadata: Option<ChatVideoMetadata>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ChatVideoMetadata {
|
||||
pub fps: Option<serde_json::Number>,
|
||||
pub start_offset: Option<String>,
|
||||
pub end_offset: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ChatLogprobs {
|
||||
pub content: Option<Vec<ChatTokenLogprob>>,
|
||||
pub refusal: Option<Vec<ChatTokenLogprob>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ChatTokenLogprob {
|
||||
pub token: String,
|
||||
pub logprob: serde_json::Number,
|
||||
pub bytes: Option<Vec<u8>>,
|
||||
pub top_logprobs: Option<Vec<ChatTopLogprob>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ChatTopLogprob {
|
||||
pub token: String,
|
||||
pub logprob: serde_json::Number,
|
||||
pub bytes: Option<Vec<u8>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
|
@ -3,3 +3,17 @@ This directory owns shared Messages API data contracts and serialization: reques
|
|||
Adapter contracts and execution inputs such as `MessagesTransformContext` belong in `llms/src/base_llm/messages`. Provider rewriting and interpretation belong in `llms/src/<provider>/messages`. Call envelopes, live streams, and call orchestration belong in `inference-messages`
|
||||
|
||||
Represent web-search results, encrypted-content fields, and thinking configuration as data here. Decisions to flatten results, remove encrypted content, select thinking budgets, or require beta headers belong to provider transformations. Data-shape validation belongs here, while model capability checks and request adaptation do not
|
||||
|
||||
# references
|
||||
|
||||
These upstream contracts document fields and discriminators. They are documentation links, not saved golden snapshots. Beta-only blocks and tools (MCP, compaction, tool changes, advisor, fallback, browser state, and toolsets) follow the beta reference
|
||||
|
||||
## Messages bodies, content, tools, and streaming
|
||||
|
||||
- https://platform.claude.com/docs/en/api/http/messages/create
|
||||
- https://platform.claude.com/docs/en/api/messages/create.md
|
||||
- https://platform.claude.com/docs/en/api/beta/messages/create.md
|
||||
- https://platform.claude.com/docs/en/build-with-claude/streaming.md
|
||||
- https://platform.claude.com/docs/en/build-with-claude/context-editing
|
||||
|
||||
Anthropic-compatible hosts document their deviations in the `llms/src/<provider>/messages` guides. Copy a host reference into `../../providers/AGENTS.md` only when that host gets a typed extension in `providers`
|
||||
|
|
|
|||
712
litellm-rust/crates/llms-types/src/formats/messages/content.rs
Normal file
712
litellm-rust/crates/llms-types/src/formats/messages/content.rs
Normal file
|
|
@ -0,0 +1,712 @@
|
|||
use serde_json::{Map, Value};
|
||||
|
||||
use super::{CacheControl, MessagesToolParam};
|
||||
use crate::json_schema::JsonSchema;
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ContentSource {
|
||||
Base64 {
|
||||
media_type: String,
|
||||
data: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Url {
|
||||
url: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
File {
|
||||
file_id: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Text {
|
||||
media_type: String,
|
||||
data: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Content {
|
||||
content: BlockContent,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum BlockContent {
|
||||
Text(String),
|
||||
Blocks(Vec<MessagesContentPart>),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ToolCaller {
|
||||
Direct {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
#[serde(rename = "code_execution_20250825")]
|
||||
CodeExecution {
|
||||
tool_id: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
#[serde(rename = "code_execution_20260120")]
|
||||
CodeExecution20260120 {
|
||||
tool_id: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct CitationsConfig {
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct PageCitation {
|
||||
pub cited_text: String,
|
||||
pub document_index: u64,
|
||||
pub document_title: Option<String>,
|
||||
pub start_page_number: u64,
|
||||
pub end_page_number: u64,
|
||||
pub file_id: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct CharCitation {
|
||||
pub cited_text: String,
|
||||
pub document_index: u64,
|
||||
pub document_title: Option<String>,
|
||||
pub start_char_index: u64,
|
||||
pub end_char_index: u64,
|
||||
pub file_id: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ContentBlockCitation {
|
||||
pub cited_text: String,
|
||||
pub document_index: u64,
|
||||
pub document_title: Option<String>,
|
||||
pub start_block_index: u64,
|
||||
pub end_block_index: u64,
|
||||
pub file_id: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebSearchCitation {
|
||||
pub cited_text: String,
|
||||
pub url: String,
|
||||
pub encrypted_index: String,
|
||||
pub title: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct SearchResultCitation {
|
||||
pub cited_text: String,
|
||||
pub search_result_index: u64,
|
||||
pub source: String,
|
||||
pub title: Option<String>,
|
||||
pub start_block_index: u64,
|
||||
pub end_block_index: u64,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum Citation {
|
||||
PageLocation(PageCitation),
|
||||
CharLocation(CharCitation),
|
||||
WebSearchResultLocation(WebSearchCitation),
|
||||
ContentBlockLocation(ContentBlockCitation),
|
||||
SearchResultLocation(SearchResultCitation),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum MessagesContentPart {
|
||||
Text(TextBlock),
|
||||
Image(ImageBlock),
|
||||
Document(Box<DocumentBlock>),
|
||||
SearchResult(SearchResultBlock),
|
||||
Thinking(ThinkingBlock),
|
||||
RedactedThinking(RedactedThinkingBlock),
|
||||
ToolUse(ToolUseBlock),
|
||||
ToolResult(ToolResultBlock),
|
||||
ToolReference(ToolReferenceBlock),
|
||||
BrowserState(BrowserStateBlock),
|
||||
ServerToolUse(ServerToolUseBlock),
|
||||
WebSearchToolResult(ServerToolResultBlock<WebSearchToolResultContent>),
|
||||
WebFetchToolResult(ServerToolResultBlock<WebFetchToolResultContent>),
|
||||
CodeExecutionToolResult(ServerToolResultBlock<CodeExecutionToolResultContent>),
|
||||
BashCodeExecutionToolResult(ServerToolResultBlock<BashCodeExecutionToolResultContent>),
|
||||
TextEditorCodeExecutionToolResult(
|
||||
ServerToolResultBlock<TextEditorCodeExecutionToolResultContent>,
|
||||
),
|
||||
ToolSearchToolResult(ServerToolResultBlock<ToolSearchToolResultContent>),
|
||||
AdvisorToolResult(ServerToolResultBlock<AdvisorToolResultContent>),
|
||||
ContainerUpload(ContainerUploadBlock),
|
||||
McpToolUse(McpToolUseBlock),
|
||||
McpToolResult(McpToolResultBlock),
|
||||
McpToolListing(McpToolListingBlock),
|
||||
Compaction(CompactionBlock),
|
||||
ToolAddition(ToolChangeBlock),
|
||||
ToolRemoval(ToolChangeBlock),
|
||||
Fallback(FallbackBlock),
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct TextBlock {
|
||||
pub text: String,
|
||||
pub citations: Option<Vec<Citation>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ImageBlock {
|
||||
pub source: ContentSource,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct DocumentBlock {
|
||||
pub source: ContentSource,
|
||||
pub title: Option<String>,
|
||||
pub context: Option<String>,
|
||||
pub citations: Option<CitationsConfig>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct SearchResultBlock {
|
||||
pub source: String,
|
||||
pub title: String,
|
||||
pub content: Vec<MessagesContentPart>,
|
||||
pub citations: Option<CitationsConfig>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ThinkingBlock {
|
||||
pub thinking: String,
|
||||
pub signature: String,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct RedactedThinkingBlock {
|
||||
pub data: String,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ToolUseBlock {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub input: Map<String, Value>,
|
||||
pub caller: Option<ToolCaller>,
|
||||
pub toolset_name: Option<String>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ToolResultBlock {
|
||||
pub tool_use_id: String,
|
||||
pub content: Option<BlockContent>,
|
||||
pub is_error: Option<bool>,
|
||||
pub toolset_name: Option<String>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ToolReferenceBlock {
|
||||
pub tool_name: String,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct BrowserStateBlock {
|
||||
pub tabs: Vec<BrowserTab>,
|
||||
pub state_changes: Option<Vec<BrowserStateChange>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct BrowserTab {
|
||||
pub tab_id: String,
|
||||
pub title: String,
|
||||
pub url: String,
|
||||
pub active: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum BrowserStateChange {
|
||||
TabOpened {
|
||||
tab_id: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
DownloadStarted {
|
||||
download_id: String,
|
||||
url: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
DownloadCompleted {
|
||||
download_id: String,
|
||||
url: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
path: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
size_bytes: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
DownloadFailed {
|
||||
download_id: String,
|
||||
url: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
error: Option<String>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ServerToolUseBlock {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub input: Map<String, Value>,
|
||||
pub caller: Option<ToolCaller>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ServerToolResultBlock<C> {
|
||||
pub tool_use_id: String,
|
||||
pub content: C,
|
||||
pub caller: Option<ToolCaller>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ServerToolError {
|
||||
pub error_code: String,
|
||||
pub error_message: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum WebSearchToolResultContent {
|
||||
Error(WebSearchResultError),
|
||||
Results(Vec<WebSearchResult>),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebSearchResultError {
|
||||
#[serde(rename = "type")]
|
||||
pub error_type: WebSearchResultErrorType,
|
||||
pub error_code: String,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WebSearchResultErrorType {
|
||||
WebSearchToolResultError,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebSearchResult {
|
||||
#[serde(rename = "type")]
|
||||
pub result_type: WebSearchResultType,
|
||||
pub url: String,
|
||||
pub title: String,
|
||||
pub encrypted_content: String,
|
||||
pub page_age: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WebSearchResultType {
|
||||
WebSearchResult,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum WebFetchToolResultContent {
|
||||
WebFetchToolResultError(ServerToolError),
|
||||
WebFetchResult(WebFetchResult),
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebFetchResult {
|
||||
pub url: String,
|
||||
pub content: WebFetchDocument,
|
||||
pub retrieved_at: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum WebFetchDocument {
|
||||
Document(Box<DocumentBlock>),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum CodeExecutionToolResultContent {
|
||||
CodeExecutionToolResultError(ServerToolError),
|
||||
CodeExecutionResult(CodeExecutionResult),
|
||||
EncryptedCodeExecutionResult(EncryptedCodeExecutionResult),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum BashCodeExecutionToolResultContent {
|
||||
BashCodeExecutionToolResultError(ServerToolError),
|
||||
BashCodeExecutionResult(CodeExecutionResult<BashCodeExecutionOutput>),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct CodeExecutionResult<O = CodeExecutionOutput> {
|
||||
pub stdout: String,
|
||||
pub stderr: String,
|
||||
pub return_code: i64,
|
||||
pub content: Vec<O>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct EncryptedCodeExecutionResult {
|
||||
pub encrypted_stdout: String,
|
||||
pub stderr: String,
|
||||
pub return_code: i64,
|
||||
pub content: Vec<CodeExecutionOutput>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum CodeExecutionOutput {
|
||||
CodeExecutionOutput {
|
||||
file_id: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum BashCodeExecutionOutput {
|
||||
BashCodeExecutionOutput {
|
||||
file_id: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum TextEditorCodeExecutionToolResultContent {
|
||||
TextEditorCodeExecutionToolResultError(ServerToolError),
|
||||
TextEditorCodeExecutionViewResult(TextEditorViewResult),
|
||||
TextEditorCodeExecutionCreateResult(TextEditorCreateResult),
|
||||
TextEditorCodeExecutionStrReplaceResult(TextEditorStrReplaceResult),
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct TextEditorViewResult {
|
||||
pub content: String,
|
||||
pub file_type: TextEditorFileType,
|
||||
pub num_lines: Option<u64>,
|
||||
pub start_line: Option<u64>,
|
||||
pub total_lines: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TextEditorFileType {
|
||||
Text,
|
||||
Image,
|
||||
Pdf,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct TextEditorCreateResult {
|
||||
pub is_file_update: bool,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct TextEditorStrReplaceResult {
|
||||
pub lines: Option<Vec<String>>,
|
||||
pub new_lines: Option<u64>,
|
||||
pub new_start: Option<u64>,
|
||||
pub old_lines: Option<u64>,
|
||||
pub old_start: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ToolSearchToolResultContent {
|
||||
ToolSearchToolResultError(ServerToolError),
|
||||
ToolSearchToolSearchResult(ToolSearchResult),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ToolSearchResult {
|
||||
pub tool_references: Vec<ToolSearchReference>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ToolSearchReference {
|
||||
ToolReference(ToolReferenceBlock),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum AdvisorToolResultContent {
|
||||
AdvisorToolResultError(ServerToolError),
|
||||
AdvisorResult {
|
||||
text: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
stop_reason: Option<String>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
AdvisorRedactedResult {
|
||||
encrypted_content: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
stop_reason: Option<String>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ContainerUploadBlock {
|
||||
pub file_id: String,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct McpToolUseBlock {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub server_name: String,
|
||||
pub input: Map<String, Value>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct McpToolResultBlock {
|
||||
pub tool_use_id: String,
|
||||
pub content: Option<McpToolResultContent>,
|
||||
pub is_error: Option<bool>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum McpToolResultContent {
|
||||
Text(String),
|
||||
Blocks(Vec<McpToolResultText>),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum McpToolResultText {
|
||||
Text(TextBlock),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct McpToolListingBlock {
|
||||
pub mcp_server_name: String,
|
||||
pub tools: Vec<McpListedTool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct McpListedTool {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub input_schema: JsonSchema,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct CompactionBlock {
|
||||
pub content: Option<String>,
|
||||
pub encrypted_content: Option<String>,
|
||||
pub signature: Option<String>,
|
||||
pub tool_changes: Option<Vec<ToolChange>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ToolChange {
|
||||
ToolAddition(ToolChangeBlock),
|
||||
ToolRemoval(ToolChangeBlock),
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ToolChangeBlock {
|
||||
pub tool: ToolChangeTarget,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ToolChangeTarget {
|
||||
ToolReference {
|
||||
name: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
McpToolReference {
|
||||
server_name: String,
|
||||
name: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
McpToolsetReference {
|
||||
server_name: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
ToolDefinition {
|
||||
definition: Box<MessagesToolParam>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct FallbackBlock {
|
||||
pub from: FallbackModel,
|
||||
pub to: FallbackModel,
|
||||
pub trigger: Option<FallbackTrigger>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct FallbackModel {
|
||||
pub model: String,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum FallbackTrigger {
|
||||
Refusal {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
category: Option<String>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
331
litellm-rust/crates/llms-types/src/formats/messages/metadata.rs
Normal file
331
litellm-rust/crates/llms-types/src/formats/messages/metadata.rs
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::json_schema::JsonSchema;
|
||||
use crate::recognized::Recognized;
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct MessagesMetadata {
|
||||
pub user_id: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct OutputFormat {
|
||||
#[serde(rename = "type")]
|
||||
pub format_type: OutputFormatType,
|
||||
pub schema: JsonSchema,
|
||||
pub strict: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum OutputFormatType {
|
||||
JsonSchema,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct MessagesCompaction {
|
||||
#[serde(rename = "type")]
|
||||
pub compaction_type: CompactionType,
|
||||
pub instructions: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CompactionType {
|
||||
Summarize,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct MessagesContainer {
|
||||
pub id: Option<String>,
|
||||
pub expires_at: Option<String>,
|
||||
pub skills: Option<Vec<ContainerSkill>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ContainerSkill {
|
||||
#[serde(rename = "type")]
|
||||
pub skill_type: SkillType,
|
||||
pub skill_id: String,
|
||||
pub version: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SkillType {
|
||||
Anthropic,
|
||||
Custom,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct McpServer {
|
||||
#[serde(rename = "type")]
|
||||
pub server_type: McpServerType,
|
||||
pub url: String,
|
||||
pub name: String,
|
||||
pub authorization_token: Option<String>,
|
||||
pub tool_configuration: Option<McpToolConfiguration>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum McpServerType {
|
||||
Url,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct McpToolConfiguration {
|
||||
pub allowed_tools: Option<Vec<String>>,
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct StopDetails {
|
||||
#[serde(rename = "type")]
|
||||
pub detail_type: Recognized<StopDetailsType>,
|
||||
pub category: Option<String>,
|
||||
pub explanation: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum StopDetailsType {
|
||||
Refusal,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ContextManagementResponse {
|
||||
pub applied_edits: Option<Vec<AppliedEdit>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct AppliedEdit {
|
||||
#[serde(rename = "type")]
|
||||
pub edit_type: Option<String>,
|
||||
pub cleared_input_tokens: Option<u64>,
|
||||
pub cleared_tool_uses: Option<u64>,
|
||||
pub cleared_thinking_turns: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct Safeguard {
|
||||
#[serde(rename = "type")]
|
||||
pub safeguard_type: String,
|
||||
pub classifier_context: Option<Map<String, Value>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
strum::Display,
|
||||
strum::EnumString,
|
||||
strum::AsRefStr,
|
||||
serde_with::DeserializeFromStr,
|
||||
serde_with::SerializeDisplay,
|
||||
)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[cfg_attr(feature = "schema", schemars(from = "String", into = "String"))]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum MessageRole {
|
||||
User,
|
||||
Assistant,
|
||||
System,
|
||||
#[strum(default, transparent)]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl MessageRole {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for MessageRole {
|
||||
fn from(value: String) -> Self {
|
||||
value.parse().unwrap_or_else(|never| match never {})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MessageRole> for String {
|
||||
fn from(value: MessageRole) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
strum::Display,
|
||||
strum::EnumString,
|
||||
strum::AsRefStr,
|
||||
serde_with::DeserializeFromStr,
|
||||
serde_with::SerializeDisplay,
|
||||
)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[cfg_attr(feature = "schema", schemars(from = "String", into = "String"))]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum MessageType {
|
||||
Message,
|
||||
#[strum(default, transparent)]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl MessageType {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for MessageType {
|
||||
fn from(value: String) -> Self {
|
||||
value.parse().unwrap_or_else(|never| match never {})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MessageType> for String {
|
||||
fn from(value: MessageType) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
strum::Display,
|
||||
strum::EnumString,
|
||||
strum::AsRefStr,
|
||||
serde_with::DeserializeFromStr,
|
||||
serde_with::SerializeDisplay,
|
||||
)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[cfg_attr(feature = "schema", schemars(from = "String", into = "String"))]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum StopReason {
|
||||
EndTurn,
|
||||
MaxTokens,
|
||||
StopSequence,
|
||||
ToolUse,
|
||||
Refusal,
|
||||
Compaction,
|
||||
PauseTurn,
|
||||
ModelContextWindowExceeded,
|
||||
#[strum(default, transparent)]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl StopReason {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for StopReason {
|
||||
fn from(value: String) -> Self {
|
||||
value.parse().unwrap_or_else(|never| match never {})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StopReason> for String {
|
||||
fn from(value: StopReason) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum ContainerReference {
|
||||
Id(String),
|
||||
Parameters(Box<MessagesContainer>),
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct MessagesDiagnosticsParam {
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
with = "::serde_with::rust::double_option"
|
||||
)]
|
||||
#[cfg_attr(feature = "schema", schemars(with = "Option<String>"))]
|
||||
pub previous_message_id: Option<Option<String>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct MessagesDiagnostics {
|
||||
pub cache_miss_reason: Option<Recognized<CacheMissReason>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum CacheMissReason {
|
||||
ModelChanged(CacheMissedTokens),
|
||||
SystemChanged(CacheMissedTokens),
|
||||
ToolsChanged(CacheMissedTokens),
|
||||
MessagesChanged(CacheMissedTokens),
|
||||
PreviousMessageNotFound {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Unavailable {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct CacheMissedTokens {
|
||||
pub cache_missed_input_tokens: u64,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
|
@ -1,11 +1,54 @@
|
|||
mod content;
|
||||
mod metadata;
|
||||
mod request;
|
||||
mod response;
|
||||
pub mod streaming;
|
||||
mod tools;
|
||||
mod usage;
|
||||
|
||||
pub use content::{
|
||||
AdvisorToolResultContent, BashCodeExecutionOutput, BashCodeExecutionToolResultContent,
|
||||
BlockContent, BrowserStateBlock, BrowserStateChange, BrowserTab, CharCitation, Citation,
|
||||
CitationsConfig, CodeExecutionOutput, CodeExecutionResult, CodeExecutionToolResultContent,
|
||||
CompactionBlock, ContainerUploadBlock, ContentBlockCitation, ContentSource, DocumentBlock,
|
||||
EncryptedCodeExecutionResult, FallbackBlock, FallbackModel, FallbackTrigger, ImageBlock,
|
||||
McpListedTool, McpToolListingBlock, McpToolResultBlock, McpToolResultContent,
|
||||
McpToolResultText, McpToolUseBlock, MessagesContentPart, PageCitation, RedactedThinkingBlock,
|
||||
SearchResultBlock, SearchResultCitation, ServerToolError, ServerToolResultBlock,
|
||||
ServerToolUseBlock, TextBlock, TextEditorCodeExecutionToolResultContent,
|
||||
TextEditorCreateResult, TextEditorFileType, TextEditorStrReplaceResult, TextEditorViewResult,
|
||||
ThinkingBlock, ToolCaller, ToolChange, ToolChangeBlock, ToolChangeTarget, ToolReferenceBlock,
|
||||
ToolResultBlock, ToolSearchReference, ToolSearchResult, ToolSearchToolResultContent,
|
||||
ToolUseBlock, WebFetchDocument, WebFetchResult, WebFetchToolResultContent, WebSearchCitation,
|
||||
WebSearchResult, WebSearchResultError, WebSearchResultErrorType, WebSearchResultType,
|
||||
WebSearchToolResultContent,
|
||||
};
|
||||
pub use metadata::{
|
||||
AppliedEdit, CacheMissReason, CacheMissedTokens, CompactionType, ContainerReference,
|
||||
ContainerSkill, ContextManagementResponse, McpServer, McpServerType, McpToolConfiguration,
|
||||
MessageRole, MessageType, MessagesCompaction, MessagesContainer, MessagesDiagnostics,
|
||||
MessagesDiagnosticsParam, MessagesMetadata, OutputFormat, OutputFormatType, Safeguard,
|
||||
SkillType, StopDetails, StopDetailsType, StopReason,
|
||||
};
|
||||
pub use request::{
|
||||
AdaptiveThinking, CacheControl, ContentBlock, ContentBlockType, ContextEdit, ContextManagement,
|
||||
DisabledThinking, EffortLevel, EnabledThinking, Message, MessageContent,
|
||||
ContextTrigger, DisabledThinking, EffortLevel, EnabledThinking, Message, MessageContent,
|
||||
MessagesOptionalParams, MessagesRequest, MessagesTool, OutputConfig, Speed, SystemPrompt,
|
||||
ThinkingConfig, ThinkingDisplay,
|
||||
};
|
||||
pub use response::MessagesResponse;
|
||||
pub use tools::{
|
||||
AdvisorTool, AdvisorToolName, AllowedCaller, BashToolName, BrowserToolsetConfigs,
|
||||
BuiltinMessagesTool, ClientTool, CodeExecutionToolName, ComputerTool, ComputerTool20251124,
|
||||
ComputerToolName, ComputerToolsetConfigs, CustomTool, CustomToolType, McpToolset,
|
||||
MemoryToolName, MessagesToolParam, ResponseInclusion, ServerTool, StrReplaceBasedEditToolName,
|
||||
StrReplaceEditorName, TextEditorTool20250728, ToolChoice, ToolChoiceType, ToolResultUrlSource,
|
||||
ToolSearchBm25ToolName, ToolSearchRegexToolName, Toolset, ToolsetToolConfig,
|
||||
UrlSourceToolReference, UserInputUrlSource, UserLocationType, WebFetchTool,
|
||||
WebFetchTool20260309, WebFetchTool20260318, WebFetchToolName, WebFetchUrlSources,
|
||||
WebSearchTool, WebSearchTool20260318, WebSearchToolName, WebSearchUserLocation,
|
||||
};
|
||||
pub use usage::{
|
||||
CacheCreationUsage, MessagesOutputTokensDetails, MessagesUsage, ServerToolUsage,
|
||||
UsageIteration, UsageIterationType,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -190,6 +190,21 @@ pub enum MessagesTool {
|
|||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ContextTrigger {
|
||||
InputTokens {
|
||||
value: u64,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
ToolUses {
|
||||
value: u64,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum ContextEdit {
|
||||
|
|
|
|||
564
litellm-rust/crates/llms-types/src/formats/messages/tools.rs
Normal file
564
litellm-rust/crates/llms-types/src/formats/messages/tools.rs
Normal file
|
|
@ -0,0 +1,564 @@
|
|||
use indexmap::IndexMap;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
use super::{CacheControl, CitationsConfig, McpListedTool};
|
||||
use crate::json_schema::JsonSchema;
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
pub enum AllowedCaller {
|
||||
#[serde(rename = "direct")]
|
||||
Direct,
|
||||
#[serde(rename = "code_execution_20250825")]
|
||||
CodeExecution20250825,
|
||||
#[serde(rename = "code_execution_20260120")]
|
||||
CodeExecution20260120,
|
||||
#[serde(rename = "code_execution_20260521")]
|
||||
CodeExecution20260521,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebSearchUserLocation {
|
||||
#[serde(rename = "type")]
|
||||
pub location_type: UserLocationType,
|
||||
pub city: Option<String>,
|
||||
pub country: Option<String>,
|
||||
pub region: Option<String>,
|
||||
pub timezone: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum UserLocationType {
|
||||
Approximate,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ToolChoice {
|
||||
#[serde(rename = "type")]
|
||||
pub choice_type: ToolChoiceType,
|
||||
pub name: Option<String>,
|
||||
pub disable_parallel_tool_use: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ToolChoiceType {
|
||||
Auto,
|
||||
Any,
|
||||
Tool,
|
||||
None,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct CustomTool {
|
||||
#[serde(rename = "type")]
|
||||
pub tool_type: Option<CustomToolType>,
|
||||
pub name: String,
|
||||
pub input_schema: JsonSchema,
|
||||
pub description: Option<String>,
|
||||
pub strict: Option<bool>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub input_examples: Option<Vec<Map<String, Value>>>,
|
||||
pub eager_input_streaming: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CustomToolType {
|
||||
Custom,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BashToolName {
|
||||
Bash,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum StrReplaceEditorName {
|
||||
StrReplaceEditor,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum StrReplaceBasedEditToolName {
|
||||
StrReplaceBasedEditTool,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MemoryToolName {
|
||||
Memory,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ComputerToolName {
|
||||
Computer,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CodeExecutionToolName {
|
||||
CodeExecution,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ToolSearchRegexToolName {
|
||||
ToolSearchToolRegex,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ToolSearchBm25ToolName {
|
||||
ToolSearchToolBm25,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WebSearchToolName {
|
||||
WebSearch,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum WebFetchToolName {
|
||||
WebFetch,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AdvisorToolName {
|
||||
Advisor,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ServerTool<N> {
|
||||
pub name: N,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub strict: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ClientTool<N> {
|
||||
pub name: N,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub input_examples: Option<Vec<Map<String, Value>>>,
|
||||
pub strict: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct TextEditorTool20250728 {
|
||||
pub name: StrReplaceBasedEditToolName,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub input_examples: Option<Vec<Map<String, Value>>>,
|
||||
pub max_characters: Option<u64>,
|
||||
pub strict: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ComputerTool {
|
||||
pub name: ComputerToolName,
|
||||
pub display_width_px: u64,
|
||||
pub display_height_px: u64,
|
||||
pub display_number: Option<u64>,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub input_examples: Option<Vec<Map<String, Value>>>,
|
||||
pub strict: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ComputerTool20251124 {
|
||||
pub name: ComputerToolName,
|
||||
pub display_width_px: u64,
|
||||
pub display_height_px: u64,
|
||||
pub display_number: Option<u64>,
|
||||
pub enable_zoom: Option<bool>,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub input_examples: Option<Vec<Map<String, Value>>>,
|
||||
pub strict: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Copy, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ResponseInclusion {
|
||||
Full,
|
||||
Excluded,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebSearchTool {
|
||||
pub name: WebSearchToolName,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub allowed_domains: Option<Vec<String>>,
|
||||
pub blocked_domains: Option<Vec<String>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub max_uses: Option<u64>,
|
||||
pub strict: Option<bool>,
|
||||
pub user_location: Option<WebSearchUserLocation>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebSearchTool20260318 {
|
||||
pub name: WebSearchToolName,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub allowed_domains: Option<Vec<String>>,
|
||||
pub blocked_domains: Option<Vec<String>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub max_uses: Option<u64>,
|
||||
pub response_inclusion: Option<ResponseInclusion>,
|
||||
pub strict: Option<bool>,
|
||||
pub user_location: Option<WebSearchUserLocation>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum UrlSourceToolReference {
|
||||
ToolReference {
|
||||
name: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ToolResultUrlSource {
|
||||
All {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
None {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Only {
|
||||
tools: Vec<UrlSourceToolReference>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Except {
|
||||
tools: Vec<UrlSourceToolReference>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum UserInputUrlSource {
|
||||
All {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
None {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebFetchUrlSources {
|
||||
pub client_tool_results: Option<ToolResultUrlSource>,
|
||||
pub server_tool_results: Option<ToolResultUrlSource>,
|
||||
pub user_input: Option<UserInputUrlSource>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebFetchTool {
|
||||
pub name: WebFetchToolName,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub allowed_domains: Option<Vec<String>>,
|
||||
pub blocked_domains: Option<Vec<String>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub citations: Option<CitationsConfig>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub max_content_tokens: Option<u64>,
|
||||
pub max_uses: Option<u64>,
|
||||
pub strict: Option<bool>,
|
||||
pub url_sources: Option<WebFetchUrlSources>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebFetchTool20260309 {
|
||||
pub name: WebFetchToolName,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub allowed_domains: Option<Vec<String>>,
|
||||
pub blocked_domains: Option<Vec<String>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub citations: Option<CitationsConfig>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub max_content_tokens: Option<u64>,
|
||||
pub max_uses: Option<u64>,
|
||||
pub strict: Option<bool>,
|
||||
pub url_sources: Option<WebFetchUrlSources>,
|
||||
pub use_cache: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct WebFetchTool20260318 {
|
||||
pub name: WebFetchToolName,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub allowed_domains: Option<Vec<String>>,
|
||||
pub blocked_domains: Option<Vec<String>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub citations: Option<CitationsConfig>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub max_content_tokens: Option<u64>,
|
||||
pub max_uses: Option<u64>,
|
||||
pub response_inclusion: Option<ResponseInclusion>,
|
||||
pub strict: Option<bool>,
|
||||
pub url_sources: Option<WebFetchUrlSources>,
|
||||
pub use_cache: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct AdvisorTool {
|
||||
pub name: AdvisorToolName,
|
||||
pub model: String,
|
||||
pub allowed_callers: Option<Vec<AllowedCaller>>,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub caching: Option<CacheControl>,
|
||||
pub defer_loading: Option<bool>,
|
||||
pub max_tokens: Option<u64>,
|
||||
pub max_uses: Option<u64>,
|
||||
pub strict: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ToolsetToolConfig {
|
||||
pub defer_loading: Option<bool>,
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct McpToolset {
|
||||
pub mcp_server_name: String,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub configs: Option<IndexMap<String, ToolsetToolConfig>>,
|
||||
pub default_config: Option<ToolsetToolConfig>,
|
||||
pub tools: Option<Vec<McpListedTool>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct BrowserToolsetConfigs {
|
||||
#[serde(rename = "type")]
|
||||
pub type_text: Option<ToolsetToolConfig>,
|
||||
pub close_tab: Option<ToolsetToolConfig>,
|
||||
pub double_click: Option<ToolsetToolConfig>,
|
||||
pub file_upload: Option<ToolsetToolConfig>,
|
||||
pub find: Option<ToolsetToolConfig>,
|
||||
pub form_input: Option<ToolsetToolConfig>,
|
||||
pub get_page_text: Option<ToolsetToolConfig>,
|
||||
pub hold_key: Option<ToolsetToolConfig>,
|
||||
pub hover: Option<ToolsetToolConfig>,
|
||||
pub javascript_exec: Option<ToolsetToolConfig>,
|
||||
pub key: Option<ToolsetToolConfig>,
|
||||
pub left_click: Option<ToolsetToolConfig>,
|
||||
pub left_click_drag: Option<ToolsetToolConfig>,
|
||||
pub left_mouse_down: Option<ToolsetToolConfig>,
|
||||
pub left_mouse_up: Option<ToolsetToolConfig>,
|
||||
pub list_tabs: Option<ToolsetToolConfig>,
|
||||
pub middle_click: Option<ToolsetToolConfig>,
|
||||
pub mouse_move: Option<ToolsetToolConfig>,
|
||||
pub navigate: Option<ToolsetToolConfig>,
|
||||
pub new_tab: Option<ToolsetToolConfig>,
|
||||
pub read_console: Option<ToolsetToolConfig>,
|
||||
pub read_network: Option<ToolsetToolConfig>,
|
||||
pub read_page: Option<ToolsetToolConfig>,
|
||||
pub right_click: Option<ToolsetToolConfig>,
|
||||
pub screenshot: Option<ToolsetToolConfig>,
|
||||
pub scroll: Option<ToolsetToolConfig>,
|
||||
pub scroll_to: Option<ToolsetToolConfig>,
|
||||
pub switch_tab: Option<ToolsetToolConfig>,
|
||||
pub triple_click: Option<ToolsetToolConfig>,
|
||||
pub wait: Option<ToolsetToolConfig>,
|
||||
pub zoom: Option<ToolsetToolConfig>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ComputerToolsetConfigs {
|
||||
#[serde(rename = "type")]
|
||||
pub type_text: Option<ToolsetToolConfig>,
|
||||
pub cursor_position: Option<ToolsetToolConfig>,
|
||||
pub double_click: Option<ToolsetToolConfig>,
|
||||
pub hold_key: Option<ToolsetToolConfig>,
|
||||
pub key: Option<ToolsetToolConfig>,
|
||||
pub left_click: Option<ToolsetToolConfig>,
|
||||
pub left_click_drag: Option<ToolsetToolConfig>,
|
||||
pub left_mouse_down: Option<ToolsetToolConfig>,
|
||||
pub left_mouse_up: Option<ToolsetToolConfig>,
|
||||
pub middle_click: Option<ToolsetToolConfig>,
|
||||
pub mouse_move: Option<ToolsetToolConfig>,
|
||||
pub right_click: Option<ToolsetToolConfig>,
|
||||
pub screenshot: Option<ToolsetToolConfig>,
|
||||
pub scroll: Option<ToolsetToolConfig>,
|
||||
pub triple_click: Option<ToolsetToolConfig>,
|
||||
pub wait: Option<ToolsetToolConfig>,
|
||||
pub zoom: Option<ToolsetToolConfig>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct Toolset<C> {
|
||||
pub cache_control: Option<CacheControl>,
|
||||
pub configs: Option<Box<C>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum BuiltinMessagesTool {
|
||||
#[serde(rename = "bash_20241022")]
|
||||
Bash20241022(ClientTool<BashToolName>),
|
||||
#[serde(rename = "bash_20250124")]
|
||||
Bash20250124(ClientTool<BashToolName>),
|
||||
#[serde(rename = "text_editor_20241022")]
|
||||
TextEditor20241022(ClientTool<StrReplaceEditorName>),
|
||||
#[serde(rename = "text_editor_20250124")]
|
||||
TextEditor20250124(ClientTool<StrReplaceEditorName>),
|
||||
#[serde(rename = "text_editor_20250429")]
|
||||
TextEditor20250429(ClientTool<StrReplaceBasedEditToolName>),
|
||||
#[serde(rename = "text_editor_20250728")]
|
||||
TextEditor20250728(TextEditorTool20250728),
|
||||
#[serde(rename = "memory_20250818")]
|
||||
Memory20250818(ClientTool<MemoryToolName>),
|
||||
#[serde(rename = "computer_20241022")]
|
||||
Computer20241022(ComputerTool),
|
||||
#[serde(rename = "computer_20250124")]
|
||||
Computer20250124(ComputerTool),
|
||||
#[serde(rename = "computer_20251124")]
|
||||
Computer20251124(ComputerTool20251124),
|
||||
#[serde(rename = "code_execution_20250522")]
|
||||
CodeExecution20250522(ServerTool<CodeExecutionToolName>),
|
||||
#[serde(rename = "code_execution_20250825")]
|
||||
CodeExecution20250825(ServerTool<CodeExecutionToolName>),
|
||||
#[serde(rename = "code_execution_20260120")]
|
||||
CodeExecution20260120(ServerTool<CodeExecutionToolName>),
|
||||
#[serde(rename = "code_execution_20260521")]
|
||||
CodeExecution20260521(ServerTool<CodeExecutionToolName>),
|
||||
#[serde(rename = "tool_search_tool_regex_20251119")]
|
||||
ToolSearchRegex20251119(ServerTool<ToolSearchRegexToolName>),
|
||||
#[serde(rename = "tool_search_tool_regex")]
|
||||
ToolSearchRegex(ServerTool<ToolSearchRegexToolName>),
|
||||
#[serde(rename = "tool_search_tool_bm25_20251119")]
|
||||
ToolSearchBm2520251119(ServerTool<ToolSearchBm25ToolName>),
|
||||
#[serde(rename = "tool_search_tool_bm25")]
|
||||
ToolSearchBm25(ServerTool<ToolSearchBm25ToolName>),
|
||||
#[serde(rename = "web_search_20250305")]
|
||||
WebSearch20250305(WebSearchTool),
|
||||
#[serde(rename = "web_search_20260209")]
|
||||
WebSearch20260209(WebSearchTool),
|
||||
#[serde(rename = "web_search_20260318")]
|
||||
WebSearch20260318(WebSearchTool20260318),
|
||||
#[serde(rename = "web_fetch_20250910")]
|
||||
WebFetch20250910(WebFetchTool),
|
||||
#[serde(rename = "web_fetch_20260209")]
|
||||
WebFetch20260209(WebFetchTool),
|
||||
#[serde(rename = "web_fetch_20260309")]
|
||||
WebFetch20260309(WebFetchTool20260309),
|
||||
#[serde(rename = "web_fetch_20260318")]
|
||||
WebFetch20260318(WebFetchTool20260318),
|
||||
#[serde(rename = "advisor_20260301")]
|
||||
Advisor20260301(AdvisorTool),
|
||||
#[serde(rename = "browser_toolset_20260801")]
|
||||
BrowserToolset20260801(Toolset<BrowserToolsetConfigs>),
|
||||
#[serde(rename = "computer_toolset_20260801")]
|
||||
ComputerToolset20260801(Toolset<ComputerToolsetConfigs>),
|
||||
#[serde(rename = "mcp_toolset")]
|
||||
McpToolset(McpToolset),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum MessagesToolParam {
|
||||
Builtin(Box<BuiltinMessagesTool>),
|
||||
Custom(Box<CustomTool>),
|
||||
}
|
||||
75
litellm-rust/crates/llms-types/src/formats/messages/usage.rs
Normal file
75
litellm-rust/crates/llms-types/src/formats/messages/usage.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::recognized::Recognized;
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ServerToolUsage {
|
||||
pub web_search_requests: Option<u64>,
|
||||
pub web_fetch_requests: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct UsageIteration {
|
||||
#[serde(rename = "type")]
|
||||
pub iteration_type: Recognized<UsageIterationType>,
|
||||
pub input_tokens: Option<u64>,
|
||||
pub output_tokens: Option<u64>,
|
||||
pub cache_creation_input_tokens: Option<u64>,
|
||||
pub cache_read_input_tokens: Option<u64>,
|
||||
pub cache_creation: Option<CacheCreationUsage>,
|
||||
pub model: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum UsageIterationType {
|
||||
Compaction,
|
||||
Message,
|
||||
AdvisorMessage,
|
||||
FallbackMessage,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct MessagesUsage {
|
||||
pub input_tokens: Option<u64>,
|
||||
pub output_tokens: Option<u64>,
|
||||
pub cache_creation_input_tokens: Option<u64>,
|
||||
pub cache_read_input_tokens: Option<u64>,
|
||||
pub server_tool_use: Option<ServerToolUsage>,
|
||||
pub cache_creation: Option<CacheCreationUsage>,
|
||||
pub output_tokens_details: Option<MessagesOutputTokensDetails>,
|
||||
pub service_tier: Option<String>,
|
||||
pub inference_geo: Option<String>,
|
||||
pub speed: Option<super::Speed>,
|
||||
pub iterations: Option<Vec<UsageIteration>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct CacheCreationUsage {
|
||||
pub ephemeral_1h_input_tokens: Option<u64>,
|
||||
pub ephemeral_5m_input_tokens: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct MessagesOutputTokensDetails {
|
||||
pub thinking_tokens: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
//! Wire types for each provider API format (OpenAI Chat Completions,
|
||||
//! Anthropic Messages, Responses, ...), one submodule per format.
|
||||
|
||||
pub mod audio_transcription;
|
||||
pub mod batches;
|
||||
pub mod chat_completions;
|
||||
|
|
|
|||
|
|
@ -150,3 +150,15 @@ impl LiteLLMOcrResponse {
|
|||
fn ocr_object() -> String {
|
||||
"ocr".into()
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct OcrBoundingBox {
|
||||
pub top_left_x: Option<serde_json::Number>,
|
||||
pub top_left_y: Option<serde_json::Number>,
|
||||
pub bottom_right_x: Option<serde_json::Number>,
|
||||
pub bottom_right_y: Option<serde_json::Number>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
mod output;
|
||||
mod response;
|
||||
pub mod streaming_websocket;
|
||||
|
||||
pub use output::*;
|
||||
pub use response::ResponsesApiResponse;
|
||||
|
|
|
|||
831
litellm-rust/crates/llms-types/src/formats/responses/output.rs
Normal file
831
litellm-rust/crates/llms-types/src/formats/responses/output.rs
Normal file
|
|
@ -0,0 +1,831 @@
|
|||
use crate::formats::chat_completions::PromptCacheBreakpoint;
|
||||
use serde_json::{Map, Value};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesOutputItem {
|
||||
Message(ResponsesMessage),
|
||||
FunctionCall(ResponsesFunctionCall),
|
||||
CustomToolCall(ResponsesCustomToolCall),
|
||||
Reasoning(ResponsesReasoning),
|
||||
WebSearchCall(ResponsesWebSearchCall),
|
||||
FileSearchCall(ResponsesFileSearchCall),
|
||||
ImageGenerationCall(ResponsesImageGenerationCall),
|
||||
CodeInterpreterCall(ResponsesCodeInterpreterCall),
|
||||
McpCall(ResponsesMcpCall),
|
||||
McpListTools(ResponsesMcpListTools),
|
||||
FunctionCallOutput(ResponsesFunctionCallOutput),
|
||||
ComputerCall(ResponsesComputerCall),
|
||||
ComputerCallOutput(ResponsesComputerCallOutput),
|
||||
Program(ResponsesProgram),
|
||||
ProgramOutput(ResponsesProgramOutput),
|
||||
ToolSearchCall(ResponsesToolSearchCall),
|
||||
ToolSearchOutput(ResponsesToolSearchOutput),
|
||||
AdditionalTools(ResponsesAdditionalTools),
|
||||
Compaction(ResponsesCompaction),
|
||||
LocalShellCall(ResponsesLocalShellCall),
|
||||
LocalShellCallOutput(ResponsesLocalShellCallOutput),
|
||||
ShellCall(ResponsesShellCall),
|
||||
ShellCallOutput(ResponsesShellCallOutput),
|
||||
ApplyPatchCall(ResponsesApplyPatchCall),
|
||||
ApplyPatchCallOutput(ResponsesApplyPatchCallOutput),
|
||||
McpApprovalRequest(ResponsesMcpApprovalRequest),
|
||||
McpApprovalResponse(ResponsesMcpApprovalResponse),
|
||||
CustomToolCallOutput(ResponsesCustomToolCallOutput),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesContentPart {
|
||||
OutputText {
|
||||
text: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
annotations: Option<Vec<ResponsesAnnotation>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
logprobs: Option<Vec<crate::formats::chat_completions::ChatTokenLogprob>>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Refusal {
|
||||
refusal: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
SummaryText {
|
||||
text: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
ReasoningText {
|
||||
text: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesAnnotation {
|
||||
UrlCitation(ResponsesUrlCitation),
|
||||
FileCitation(ResponsesFileCitation),
|
||||
FilePath(ResponsesFileCitation),
|
||||
ContainerFileCitation(ResponsesFileCitation),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesCodeOutput {
|
||||
Logs {
|
||||
logs: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Image {
|
||||
url: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesMessage {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub role: Option<String>,
|
||||
pub phase: Option<String>,
|
||||
pub content: Option<Vec<ResponsesContentPart>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesFunctionCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub arguments: Option<String>,
|
||||
pub namespace: Option<String>,
|
||||
pub r#async: Option<bool>,
|
||||
pub caller: Option<ResponsesToolCaller>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesCustomToolCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub input: Option<String>,
|
||||
pub namespace: Option<String>,
|
||||
pub r#async: Option<bool>,
|
||||
pub caller: Option<ResponsesToolCaller>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesReasoning {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub summary: Option<Vec<ResponsesContentPart>>,
|
||||
pub content: Option<Vec<ResponsesContentPart>>,
|
||||
pub encrypted_content: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesWebSearchCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub action: Option<ResponsesWebSearchAction>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesFileSearchCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub queries: Option<Vec<String>>,
|
||||
pub results: Option<Vec<ResponsesFileSearchResult>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesFileSearchResult {
|
||||
pub file_id: Option<String>,
|
||||
pub filename: Option<String>,
|
||||
pub score: Option<serde_json::Number>,
|
||||
pub text: Option<String>,
|
||||
pub attributes: Option<Map<String, Value>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesImageGenerationCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub result: Option<String>,
|
||||
pub action: Option<String>,
|
||||
pub background: Option<String>,
|
||||
pub output_format: Option<String>,
|
||||
pub quality: Option<String>,
|
||||
pub revised_prompt: Option<String>,
|
||||
pub size: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesCodeInterpreterCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub code: Option<String>,
|
||||
pub container_id: Option<String>,
|
||||
pub outputs: Option<Vec<ResponsesCodeOutput>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesMcpCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub server_label: Option<String>,
|
||||
pub arguments: Option<String>,
|
||||
pub output: Option<String>,
|
||||
pub error: Option<ResponsesMcpError>,
|
||||
pub approval_request_id: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesUrlCitation {
|
||||
pub url: Option<String>,
|
||||
pub title: Option<String>,
|
||||
pub start_index: Option<u64>,
|
||||
pub end_index: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesFileCitation {
|
||||
pub file_id: Option<String>,
|
||||
pub filename: Option<String>,
|
||||
pub index: Option<u64>,
|
||||
pub start_index: Option<u64>,
|
||||
pub end_index: Option<u64>,
|
||||
pub container_id: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesWebSearchAction {
|
||||
Search {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
query: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
queries: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
sources: Option<Vec<ResponsesWebSearchSource>>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
OpenPage {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
url: Option<String>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
#[serde(alias = "find")]
|
||||
FindInPage {
|
||||
url: String,
|
||||
pattern: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesWebSearchSource {
|
||||
#[serde(rename = "type")]
|
||||
pub source_type: Option<String>,
|
||||
pub url: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesMcpListTools {
|
||||
pub id: Option<String>,
|
||||
pub server_label: Option<String>,
|
||||
pub tools: Option<Vec<ResponsesMcpTool>>,
|
||||
pub error: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesMcpTool {
|
||||
pub name: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub input_schema: Option<crate::json_schema::JsonSchema>,
|
||||
pub annotations: Option<Value>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum ResponsesMcpError {
|
||||
Message(String),
|
||||
Detail(ResponsesMcpErrorDetail),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesMcpErrorDetail {
|
||||
McpProtocolError {
|
||||
code: i64,
|
||||
message: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
HttpError {
|
||||
code: i64,
|
||||
message: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
McpToolExecutionError {
|
||||
content: Value,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesToolCaller {
|
||||
Direct {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Program {
|
||||
caller_id: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum ResponsesToolOutput {
|
||||
Text(String),
|
||||
Content(Vec<ResponsesInputContent>),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesInputContent {
|
||||
InputText {
|
||||
text: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
InputImage {
|
||||
detail: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
file_id: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
image_url: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
InputFile {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
detail: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
file_data: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
file_id: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
file_url: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
filename: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
prompt_cache_breakpoint: Option<PromptCacheBreakpoint>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesFunctionCallOutput {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub output: Option<ResponsesToolOutput>,
|
||||
pub caller: Option<ResponsesToolCaller>,
|
||||
pub created_by: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub namespace: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesCustomToolCallOutput {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub output: Option<ResponsesToolOutput>,
|
||||
pub caller: Option<ResponsesToolCaller>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesSafetyCheck {
|
||||
pub id: Option<String>,
|
||||
pub code: Option<String>,
|
||||
pub message: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesComputerCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub pending_safety_checks: Option<Vec<ResponsesSafetyCheck>>,
|
||||
pub action: Option<ResponsesComputerAction>,
|
||||
pub actions: Option<Vec<ResponsesComputerAction>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesComputerAction {
|
||||
Click {
|
||||
button: String,
|
||||
x: i64,
|
||||
y: i64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
keys: Option<Vec<String>>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
DoubleClick {
|
||||
x: i64,
|
||||
y: i64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
keys: Option<Vec<String>>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Drag {
|
||||
path: Vec<ResponsesCoordinate>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
keys: Option<Vec<String>>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Keypress {
|
||||
keys: Vec<String>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Move {
|
||||
x: i64,
|
||||
y: i64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
keys: Option<Vec<String>>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Screenshot {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Scroll {
|
||||
scroll_x: i64,
|
||||
scroll_y: i64,
|
||||
x: i64,
|
||||
y: i64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
keys: Option<Vec<String>>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Type {
|
||||
text: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Wait {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct ResponsesCoordinate {
|
||||
pub x: i64,
|
||||
pub y: i64,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesComputerCallOutput {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub output: Option<ResponsesComputerOutput>,
|
||||
pub acknowledged_safety_checks: Option<Vec<ResponsesSafetyCheck>>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesComputerOutput {
|
||||
ComputerScreenshot {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
file_id: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
image_url: Option<String>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesProgram {
|
||||
pub id: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub code: Option<String>,
|
||||
pub fingerprint: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesProgramOutput {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub result: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesToolSearchCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub arguments: Option<Value>,
|
||||
pub execution: Option<String>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesToolSearchOutput {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub execution: Option<String>,
|
||||
pub tools: Option<Vec<Map<String, Value>>>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesAdditionalTools {
|
||||
pub id: Option<String>,
|
||||
pub role: Option<String>,
|
||||
pub tools: Option<Vec<Map<String, Value>>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesCompaction {
|
||||
pub id: Option<String>,
|
||||
pub encrypted_content: Option<String>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesLocalShellCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub action: Option<ResponsesLocalShellAction>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesLocalShellAction {
|
||||
Exec {
|
||||
command: Vec<String>,
|
||||
env: BTreeMap<String, String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
timeout_ms: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
user: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
working_directory: Option<String>,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesLocalShellCallOutput {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub output: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesShellCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub action: Option<ResponsesShellAction>,
|
||||
pub environment: Option<ResponsesShellEnvironment>,
|
||||
pub caller: Option<ResponsesToolCaller>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesShellAction {
|
||||
pub commands: Option<Vec<String>>,
|
||||
pub max_output_length: Option<u64>,
|
||||
pub timeout_ms: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesShellEnvironment {
|
||||
Local {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
ContainerReference {
|
||||
container_id: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesShellCallOutput {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub max_output_length: Option<u64>,
|
||||
pub output: Option<Vec<ResponsesShellOutputChunk>>,
|
||||
pub caller: Option<ResponsesToolCaller>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesShellOutputChunk {
|
||||
pub outcome: Option<ResponsesShellOutcome>,
|
||||
pub stdout: Option<String>,
|
||||
pub stderr: Option<String>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesShellOutcome {
|
||||
Timeout {
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
Exit {
|
||||
exit_code: i64,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesApplyPatchCall {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub operation: Option<ResponsesApplyPatchOperation>,
|
||||
pub caller: Option<ResponsesToolCaller>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ResponsesApplyPatchOperation {
|
||||
CreateFile {
|
||||
path: String,
|
||||
diff: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
DeleteFile {
|
||||
path: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
UpdateFile {
|
||||
path: String,
|
||||
diff: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesApplyPatchCallOutput {
|
||||
pub id: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub call_id: Option<String>,
|
||||
pub output: Option<String>,
|
||||
pub caller: Option<ResponsesToolCaller>,
|
||||
pub created_by: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesMcpApprovalRequest {
|
||||
pub id: Option<String>,
|
||||
pub server_label: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub arguments: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesMcpApprovalResponse {
|
||||
pub id: Option<String>,
|
||||
pub approval_request_id: Option<String>,
|
||||
pub approve: Option<bool>,
|
||||
pub reason: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
use super::ResponsesOutputItem;
|
||||
use crate::recognized::Recognized;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
#[derive(
|
||||
|
|
@ -90,6 +92,18 @@ pub struct ResponsesErrorBody {
|
|||
pub message: String,
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct ResponsesEventResponse {
|
||||
pub id: Option<String>,
|
||||
pub model: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub output: Option<Vec<Recognized<ResponsesOutputItem>>>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
|
|
|
|||
51
litellm-rust/crates/llms-types/src/json_schema.rs
Normal file
51
litellm-rust/crates/llms-types/src/json_schema.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
use indexmap::IndexMap;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum JsonSchema {
|
||||
Boolean(bool),
|
||||
Object(Box<JsonSchemaObject>),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum JsonSchemaType {
|
||||
Name(String),
|
||||
Names(Vec<String>),
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(untagged)]
|
||||
pub enum JsonSchemaItems {
|
||||
Schema(Box<JsonSchema>),
|
||||
Tuple(Vec<JsonSchema>),
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct JsonSchemaObject {
|
||||
#[serde(rename = "type")]
|
||||
pub schema_type: Option<JsonSchemaType>,
|
||||
pub properties: Option<IndexMap<String, JsonSchema>>,
|
||||
pub required: Option<Vec<String>>,
|
||||
#[serde(rename = "additionalProperties")]
|
||||
pub additional_properties: Option<Box<JsonSchema>>,
|
||||
pub items: Option<JsonSchemaItems>,
|
||||
#[serde(rename = "prefixItems")]
|
||||
pub prefix_items: Option<Vec<JsonSchema>>,
|
||||
#[serde(rename = "$defs")]
|
||||
pub defs: Option<IndexMap<String, JsonSchema>>,
|
||||
#[serde(rename = "$ref")]
|
||||
pub reference: Option<String>,
|
||||
#[serde(rename = "anyOf")]
|
||||
pub any_of: Option<Vec<JsonSchema>>,
|
||||
#[serde(rename = "allOf")]
|
||||
pub all_of: Option<Vec<JsonSchema>>,
|
||||
#[serde(rename = "oneOf")]
|
||||
pub one_of: Option<Vec<JsonSchema>>,
|
||||
pub strict: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ macro_rules_attribute::attribute_alias! {
|
|||
|
||||
pub mod formats;
|
||||
pub mod headers;
|
||||
pub mod json_schema;
|
||||
pub mod providers;
|
||||
pub mod recognized;
|
||||
pub mod serde_compat;
|
||||
|
|
|
|||
17
litellm-rust/crates/llms-types/src/providers/AGENTS.md
Normal file
17
litellm-rust/crates/llms-types/src/providers/AGENTS.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# references
|
||||
|
||||
These links describe the provider-specific wire types in this directory. Shared Messages payload references live in `../formats/messages/AGENTS.md`
|
||||
|
||||
## anthropic.rs
|
||||
|
||||
`AnthropicBeta` and `BetaSet` represent the `anthropic-beta` header values and their wire spelling
|
||||
|
||||
- https://platform.claude.com/docs/en/api/beta-headers.md
|
||||
|
||||
## minimax.rs
|
||||
|
||||
MiniMax's Messages reference documents its image, video, and mid-conversation system content extensions. Its cache reference documents `cache_control` on those blocks
|
||||
|
||||
- https://platform.minimax.io/docs/api-reference/text-chat-anthropic
|
||||
- https://platform.minimax.io/docs/api-reference/text-chat-anthropic.md
|
||||
- https://platform.minimax.io/docs/api-reference/anthropic-api-compatible-cache.md
|
||||
59
litellm-rust/crates/llms-types/src/providers/minimax.rs
Normal file
59
litellm-rust/crates/llms-types/src/providers/minimax.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use serde_json::{Map, Value};
|
||||
|
||||
use crate::formats::messages::CacheControl;
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum MinimaxMessagesContentBlock {
|
||||
Image(MinimaxMediaBlock),
|
||||
Video(MinimaxMediaBlock),
|
||||
MidConvSystem {
|
||||
text: String,
|
||||
#[serde(flatten)]
|
||||
extra: Map<String, Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
pub struct MinimaxMediaBlock {
|
||||
pub source: MinimaxMediaSource,
|
||||
pub cache_control: Option<CacheControl>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum MinimaxMediaSource {
|
||||
Base64 {
|
||||
media_type: String,
|
||||
data: String,
|
||||
#[serde(flatten)]
|
||||
options: MinimaxMediaOptions,
|
||||
},
|
||||
Url {
|
||||
url: String,
|
||||
#[serde(flatten)]
|
||||
options: MinimaxMediaOptions,
|
||||
},
|
||||
}
|
||||
|
||||
#[serde_with::skip_serializing_none]
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[derive(Default)]
|
||||
pub struct MinimaxMediaOptions {
|
||||
pub detail: Option<MinimaxMediaDetail>,
|
||||
pub fps: Option<serde_json::Number>,
|
||||
pub max_long_side_pixel: Option<u64>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Map<String, Value>,
|
||||
}
|
||||
|
||||
#[macro_rules_attribute::apply(wire_type)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MinimaxMediaDetail {
|
||||
Low,
|
||||
Default,
|
||||
High,
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
pub mod anthropic;
|
||||
pub mod minimax;
|
||||
|
|
|
|||
163
litellm-rust/crates/llms-types/tests/chat.rs
Normal file
163
litellm-rust/crates/llms-types/tests/chat.rs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
use litellm_llms_types::formats::chat_completions::{
|
||||
ChatContentPart, ChatLogprobs, ChatMediaUrl, PromptCacheMode,
|
||||
};
|
||||
use litellm_llms_types::formats::messages::ContentSource;
|
||||
use rstest::rstest;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
#[rstest]
|
||||
#[case::parts(json!([
|
||||
{"type":"text","text":"hello","cache_control":{"type":"ephemeral"},"prompt_cache_breakpoint":{"mode":"explicit"}},
|
||||
{"type":"image_url","image_url":{"url":"https://example.test/image","detail":"high","format":"image/png"}},
|
||||
{"type":"video_url","video_url":"https://example.test/video"},
|
||||
{"type":"input_audio","input_audio":{"data":"AA==","format":"wav"}},
|
||||
{"type":"file","file":{"file_id":"file_1","file_data":"JVBE","filename":"a.mp4","format":"video/mp4","detail":"low","video_metadata":{"fps":1,"start_offset":"1s","end_offset":"2s"}}},
|
||||
{"type":"document","source":{"type":"text","media_type":"text/plain","data":"doc"},"title":"T","context":"C","citations":{"enabled":true}},
|
||||
{"type":"refusal","refusal":"refused"}
|
||||
]))]
|
||||
fn content_parts_round_trip(#[case] wire: Value) {
|
||||
let parts: Vec<ChatContentPart> = serde_json::from_value(wire.clone()).unwrap();
|
||||
let [
|
||||
ChatContentPart::Text {
|
||||
text,
|
||||
cache_control,
|
||||
prompt_cache_breakpoint: Some(breakpoint),
|
||||
extra: text_extra,
|
||||
},
|
||||
ChatContentPart::ImageUrl {
|
||||
image_url: ChatMediaUrl::Parameters(image),
|
||||
prompt_cache_breakpoint: None,
|
||||
..
|
||||
},
|
||||
ChatContentPart::VideoUrl {
|
||||
video_url: ChatMediaUrl::Url(video),
|
||||
..
|
||||
},
|
||||
ChatContentPart::InputAudio { input_audio, .. },
|
||||
ChatContentPart::File { file, .. },
|
||||
ChatContentPart::Document {
|
||||
source,
|
||||
title: Some(title),
|
||||
context: Some(context),
|
||||
citations: Some(citations),
|
||||
..
|
||||
},
|
||||
ChatContentPart::Refusal { refusal, .. },
|
||||
] = parts.as_slice()
|
||||
else {
|
||||
panic!("expected typed content parts");
|
||||
};
|
||||
assert_eq!(text, "hello");
|
||||
assert_eq!(
|
||||
cache_control.as_ref().unwrap().cache_type.as_deref(),
|
||||
Some("ephemeral")
|
||||
);
|
||||
assert_eq!(breakpoint.mode, PromptCacheMode::Explicit);
|
||||
assert!(breakpoint.extra.is_empty());
|
||||
assert!(text_extra.is_empty());
|
||||
assert_eq!(image.url, "https://example.test/image");
|
||||
assert_eq!(image.detail.as_deref(), Some("high"));
|
||||
assert_eq!(image.format.as_deref(), Some("image/png"));
|
||||
assert!(image.extra.is_empty());
|
||||
assert_eq!(video, "https://example.test/video");
|
||||
assert_eq!(input_audio.data, "AA==");
|
||||
assert_eq!(input_audio.format, "wav");
|
||||
assert_eq!(file.file_id.as_deref(), Some("file_1"));
|
||||
assert_eq!(file.file_data.as_deref(), Some("JVBE"));
|
||||
assert_eq!(file.filename.as_deref(), Some("a.mp4"));
|
||||
assert_eq!(file.format.as_deref(), Some("video/mp4"));
|
||||
assert_eq!(file.detail.as_deref(), Some("low"));
|
||||
assert!(file.extra.is_empty());
|
||||
let metadata = file.video_metadata.as_ref().unwrap();
|
||||
assert_eq!(metadata.fps, Some(1.into()));
|
||||
assert_eq!(metadata.start_offset.as_deref(), Some("1s"));
|
||||
assert_eq!(metadata.end_offset.as_deref(), Some("2s"));
|
||||
assert!(metadata.extra.is_empty());
|
||||
let ContentSource::Text {
|
||||
data, media_type, ..
|
||||
} = source.as_ref()
|
||||
else {
|
||||
panic!("expected document text source");
|
||||
};
|
||||
assert_eq!(data, "doc");
|
||||
assert_eq!(media_type, "text/plain");
|
||||
assert_eq!(title, "T");
|
||||
assert_eq!(context, "C");
|
||||
assert_eq!(citations.enabled, Some(true));
|
||||
assert_eq!(refusal, "refused");
|
||||
assert_eq!(serde_json::to_value(parts).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn logprobs_round_trip_with_tokens_and_alternatives() {
|
||||
let wire = json!({
|
||||
"content":[{"token":"hi","logprob":-1,"bytes":[104,105],"top_logprobs":[{"token":"hey","logprob":-2.5,"bytes":[104]}]}],
|
||||
"refusal":[{"token":"refused","logprob":-3}]
|
||||
});
|
||||
let logprobs: ChatLogprobs = serde_json::from_value(wire.clone()).unwrap();
|
||||
let token = &logprobs.content.as_ref().unwrap()[0];
|
||||
assert_eq!(token.token, "hi");
|
||||
assert_eq!(token.logprob, (-1).into());
|
||||
assert_eq!(token.bytes.as_deref(), Some([104, 105].as_slice()));
|
||||
let alternative = &token.top_logprobs.as_ref().unwrap()[0];
|
||||
assert_eq!(alternative.token, "hey");
|
||||
assert_eq!(alternative.bytes.as_deref(), Some([104].as_slice()));
|
||||
assert_eq!(logprobs.refusal.as_ref().unwrap()[0].token, "refused");
|
||||
assert!(logprobs.refusal.as_ref().unwrap()[0].top_logprobs.is_none());
|
||||
assert_eq!(serde_json::to_value(logprobs).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::text(json!({"type":"text","text":false}))]
|
||||
#[case::missing_image(json!({"type":"image_url"}))]
|
||||
#[case::audio_shape(json!({"type":"input_audio","input_audio":{"data":7,"format":"wav"}}))]
|
||||
#[case::audio_missing_format(json!({"type":"input_audio","input_audio":{"data":"AA=="}}))]
|
||||
#[case::image_missing_url(json!({"type":"image_url","image_url":{"detail":"high"}}))]
|
||||
#[case::file_metadata(json!({"type":"file","file":{"video_metadata":{"fps":"fast"}}}))]
|
||||
#[case::document_source(json!({"type":"document","source":{"type":"url","url":false}}))]
|
||||
#[case::refusal_missing_text(json!({"type":"refusal"}))]
|
||||
#[case::file_missing_file(json!({"type":"file"}))]
|
||||
#[case::document_missing_source(json!({"type":"document"}))]
|
||||
#[case::video_missing_url(json!({"type":"video_url"}))]
|
||||
#[case::cache_control_shape(json!({"type":"text","text":"t","cache_control":"ephemeral"}))]
|
||||
#[case::breakpoint_missing_mode(json!({"type":"text","text":"t","prompt_cache_breakpoint":{}}))]
|
||||
#[case::breakpoint_unknown_mode(json!({"type":"text","text":"t","prompt_cache_breakpoint":{"mode":"auto"}}))]
|
||||
#[case::unknown_tag(json!({"type":"future"}))]
|
||||
fn content_parts_reject_malformed_typed_fields(#[case] wire: Value) {
|
||||
assert!(serde_json::from_value::<ChatContentPart>(wire).is_err());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn partial_file_preserves_extensions_and_omits_null_optionals() {
|
||||
let wire = json!({"type":"file","file":{"file_id":null,"filename":"a.pdf","extension":[1,null]},"future":true});
|
||||
let part: ChatContentPart = serde_json::from_value(wire).unwrap();
|
||||
let ChatContentPart::File {
|
||||
file,
|
||||
prompt_cache_breakpoint: None,
|
||||
extra,
|
||||
} = &part
|
||||
else {
|
||||
panic!("expected file")
|
||||
};
|
||||
assert!(file.file_id.is_none());
|
||||
assert!(file.file_data.is_none());
|
||||
assert_eq!(file.filename.as_deref(), Some("a.pdf"));
|
||||
assert_eq!(extra["future"], json!(true));
|
||||
assert_eq!(
|
||||
serde_json::to_value(part).unwrap(),
|
||||
json!({"type":"file","file":{"filename":"a.pdf","extension":[1,null]},"future":true})
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::missing_token(json!({"logprob":-1}))]
|
||||
#[case::missing_logprob(json!({"token":"hi"}))]
|
||||
#[case::wrong_bytes(json!({"token":"hi","logprob":-1,"bytes":[256]}))]
|
||||
fn token_logprobs_reject_malformed_fields(#[case] wire: Value) {
|
||||
assert!(
|
||||
serde_json::from_value::<litellm_llms_types::formats::chat_completions::ChatTokenLogprob>(
|
||||
wire
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
148
litellm-rust/crates/llms-types/tests/json_schema.rs
Normal file
148
litellm-rust/crates/llms-types/tests/json_schema.rs
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
use litellm_llms_types::json_schema::{
|
||||
JsonSchema, JsonSchemaItems, JsonSchemaObject, JsonSchemaType,
|
||||
};
|
||||
use rstest::rstest;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
fn round_trip<T>(wire: Value) -> T
|
||||
where
|
||||
T: DeserializeOwned + Serialize,
|
||||
{
|
||||
let parsed: T = serde_json::from_value(wire.clone()).unwrap();
|
||||
assert_eq!(serde_json::to_value(&parsed).unwrap(), wire);
|
||||
parsed
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::boolean(json!(false))]
|
||||
#[case::nested(json!({
|
||||
"type":["object","null"],
|
||||
"properties":{"nested":{"type":"array","items":{"$ref":"#/$defs/item"}},"free":true},
|
||||
"additionalProperties":{"type":"string"},
|
||||
"$defs":{"item":{"anyOf":[{"type":"string"},false]}},
|
||||
"required":["nested"],
|
||||
"enum":[{"custom":[1,null]}],
|
||||
"future":null
|
||||
}))]
|
||||
fn recursive_schema_round_trips(#[case] wire: Value) {
|
||||
let schema = round_trip::<JsonSchema>(wire);
|
||||
match schema {
|
||||
JsonSchema::Boolean(allowed) => assert!(!allowed),
|
||||
JsonSchema::Object(schema) => {
|
||||
assert_eq!(
|
||||
schema.schema_type,
|
||||
Some(JsonSchemaType::Names(vec!["object".into(), "null".into()]))
|
||||
);
|
||||
let properties = schema.properties.as_ref().unwrap();
|
||||
let JsonSchema::Object(nested) = &properties["nested"] else {
|
||||
panic!("expected nested schema")
|
||||
};
|
||||
let Some(JsonSchemaItems::Schema(items)) = &nested.items else {
|
||||
panic!("expected single items schema")
|
||||
};
|
||||
let JsonSchema::Object(items) = items.as_ref() else {
|
||||
panic!("expected items schema")
|
||||
};
|
||||
assert_eq!(items.reference.as_deref(), Some("#/$defs/item"));
|
||||
assert_eq!(properties["free"], JsonSchema::Boolean(true));
|
||||
let JsonSchema::Object(definition) = &schema.defs.as_ref().unwrap()["item"] else {
|
||||
panic!("expected schema definition")
|
||||
};
|
||||
assert_eq!(
|
||||
definition.any_of.as_ref().unwrap()[1],
|
||||
JsonSchema::Boolean(false)
|
||||
);
|
||||
assert_eq!(schema.extra["enum"], json!([{"custom":[1,null]}]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn schema_maps_keep_wire_key_order() {
|
||||
let wire = r#"{"type":"object","properties":{"zeta":{"type":"string"},"alpha":{"type":"integer"}},"$defs":{"y":true,"b":false}}"#;
|
||||
let schema: JsonSchemaObject = serde_json::from_str(wire).unwrap();
|
||||
let names: Vec<&str> = schema
|
||||
.properties
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.keys()
|
||||
.map(String::as_str)
|
||||
.collect();
|
||||
assert_eq!(names, ["zeta", "alpha"]);
|
||||
assert_eq!(serde_json::to_string(&schema).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn schema_object_round_trips_supported_keywords() {
|
||||
let schema = round_trip::<JsonSchemaObject>(json!({
|
||||
"type":"object",
|
||||
"properties":{"name":{"type":"string"}},
|
||||
"required":["name"],
|
||||
"additionalProperties":false,
|
||||
"$ref":"#/$defs/value",
|
||||
"strict":true,
|
||||
"extension":{"nested":[1,null]}
|
||||
}));
|
||||
assert_eq!(
|
||||
schema.schema_type,
|
||||
Some(JsonSchemaType::Name("object".into()))
|
||||
);
|
||||
assert_eq!(schema.required.as_deref(), Some(["name".into()].as_slice()));
|
||||
assert_eq!(
|
||||
schema.additional_properties.as_deref(),
|
||||
Some(&JsonSchema::Boolean(false))
|
||||
);
|
||||
assert_eq!(schema.reference.as_deref(), Some("#/$defs/value"));
|
||||
assert_eq!(schema.strict, Some(true));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::scalar(json!(7))]
|
||||
#[case::properties_shape(json!({"properties":[]}))]
|
||||
#[case::nested_schema(json!({"properties":{"name":7}}))]
|
||||
#[case::schema_type(json!({"type":["string",7]}))]
|
||||
#[case::items_shape(json!({"items":7}))]
|
||||
#[case::tuple_member(json!({"items":[{"type":"string"},7]}))]
|
||||
#[case::prefix_items_shape(json!({"prefixItems":{"type":"string"}}))]
|
||||
#[case::composite_shape(json!({"anyOf":["string"]}))]
|
||||
fn schemas_reject_malformed_known_keywords(#[case] wire: Value) {
|
||||
assert!(serde_json::from_value::<JsonSchema>(wire).is_err());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::draft_07_tuple(json!({"type":"array","items":[{"type":"string"},true]}))]
|
||||
#[case::draft_2020_12_tuple(json!({"type":"array","prefixItems":[{"type":"string"},true],"items":false}))]
|
||||
fn tuple_schemas_expose_positional_members(#[case] wire: Value) {
|
||||
let schema = round_trip::<JsonSchemaObject>(wire);
|
||||
let members = match (&schema.items, &schema.prefix_items) {
|
||||
(Some(JsonSchemaItems::Tuple(members)), None) => members,
|
||||
(Some(JsonSchemaItems::Schema(rest)), Some(members)) => {
|
||||
assert_eq!(rest.as_ref(), &JsonSchema::Boolean(false));
|
||||
members
|
||||
}
|
||||
_ => panic!("expected positional members"),
|
||||
};
|
||||
let [JsonSchema::Object(first), JsonSchema::Boolean(true)] = members.as_slice() else {
|
||||
panic!("expected string schema then true");
|
||||
};
|
||||
assert_eq!(
|
||||
first.schema_type,
|
||||
Some(JsonSchemaType::Name("string".into()))
|
||||
);
|
||||
assert!(schema.extra.is_empty());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn empty_schema_omits_null_optionals_and_preserves_arbitrary_keywords() {
|
||||
let schema: JsonSchemaObject = serde_json::from_value(
|
||||
json!({"type":null,"properties":null,"const":{"arbitrary":[1,null]},"future":null}),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(schema.schema_type.is_none());
|
||||
assert!(schema.properties.is_none());
|
||||
assert_eq!(
|
||||
serde_json::to_value(schema).unwrap(),
|
||||
json!({"const":{"arbitrary":[1,null]},"future":null})
|
||||
);
|
||||
}
|
||||
|
|
@ -33,6 +33,15 @@ fn content_block_type_schema_remains_a_string() {
|
|||
#[case::compaction("compaction", ContentBlockType::Compaction)]
|
||||
#[case::advisor_result("advisor_tool_result", ContentBlockType::AdvisorToolResult)]
|
||||
#[case::web_search_result("web_search_tool_result", ContentBlockType::WebSearchToolResult)]
|
||||
#[case::image("image", ContentBlockType::Other("image".into()))]
|
||||
#[case::document("document", ContentBlockType::Other("document".into()))]
|
||||
#[case::tool_addition("tool_addition", ContentBlockType::Other("tool_addition".into()))]
|
||||
#[case::tool_removal("tool_removal", ContentBlockType::Other("tool_removal".into()))]
|
||||
#[case::advisor("advisor_result", ContentBlockType::Other("advisor_result".into()))]
|
||||
#[case::web_search_error(
|
||||
"web_search_tool_result_error",
|
||||
ContentBlockType::Other("web_search_tool_result_error".into())
|
||||
)]
|
||||
#[case::future_block("future_block", ContentBlockType::Other("future_block".into()))]
|
||||
#[case::case_sensitive("Tool_Use", ContentBlockType::Other("Tool_Use".into()))]
|
||||
#[case::empty("", ContentBlockType::Other(String::new()))]
|
||||
|
|
|
|||
1512
litellm-rust/crates/llms-types/tests/messages_types.rs
Normal file
1512
litellm-rust/crates/llms-types/tests/messages_types.rs
Normal file
File diff suppressed because it is too large
Load diff
82
litellm-rust/crates/llms-types/tests/minimax.rs
Normal file
82
litellm-rust/crates/llms-types/tests/minimax.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use litellm_llms_types::providers::minimax::{
|
||||
MinimaxMediaDetail, MinimaxMediaSource, MinimaxMessagesContentBlock,
|
||||
};
|
||||
use rstest::rstest;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
#[rstest]
|
||||
#[case::image(json!({"type":"image","source":{"type":"base64","media_type":"image/png","data":"AA==","detail":"low"}}))]
|
||||
#[case::video(json!({"type":"video","source":{"type":"url","url":"https://example.test/video","detail":"high","fps":1,"max_long_side_pixel":1024,"future":null},"cache_control":{"type":"ephemeral"}}))]
|
||||
#[case::mid_conversation_system(json!({"type":"mid_conv_system","text":"instruction","future":null}))]
|
||||
fn provider_content_blocks_round_trip(#[case] wire: Value) {
|
||||
let block: MinimaxMessagesContentBlock = serde_json::from_value(wire.clone()).unwrap();
|
||||
match &block {
|
||||
MinimaxMessagesContentBlock::Image(image) => {
|
||||
let MinimaxMediaSource::Base64 {
|
||||
media_type,
|
||||
data,
|
||||
options,
|
||||
} = &image.source
|
||||
else {
|
||||
panic!("expected base64 image source");
|
||||
};
|
||||
assert_eq!((media_type.as_str(), data.as_str()), ("image/png", "AA=="));
|
||||
assert_eq!(options.detail, Some(MinimaxMediaDetail::Low));
|
||||
assert!(options.extra.is_empty());
|
||||
}
|
||||
MinimaxMessagesContentBlock::Video(video) => {
|
||||
let MinimaxMediaSource::Url { url, options } = &video.source else {
|
||||
panic!("expected URL video source");
|
||||
};
|
||||
assert_eq!(url, "https://example.test/video");
|
||||
assert_eq!(options.detail, Some(MinimaxMediaDetail::High));
|
||||
assert_eq!(options.fps, Some(1.into()));
|
||||
assert_eq!(options.max_long_side_pixel, Some(1024));
|
||||
assert_eq!(options.extra["future"], Value::Null);
|
||||
assert_eq!(
|
||||
video.cache_control.as_ref().unwrap().cache_type.as_deref(),
|
||||
Some("ephemeral")
|
||||
);
|
||||
}
|
||||
MinimaxMessagesContentBlock::MidConvSystem { text, extra } => {
|
||||
assert_eq!(text, "instruction");
|
||||
assert_eq!(extra["future"], Value::Null);
|
||||
}
|
||||
}
|
||||
assert_eq!(serde_json::to_value(block).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::missing_source(json!({"type":"video"}))]
|
||||
#[case::bad_source_tag(json!({"type":"image","source":{"type":"future"}}))]
|
||||
#[case::url_without_url(json!({"type":"video","source":{"type":"url"}}))]
|
||||
#[case::base64_without_data(json!({"type":"image","source":{"type":"base64","media_type":"image/png"}}))]
|
||||
#[case::bad_detail(json!({"type":"image","source":{"type":"url","url":"u","detail":7}}))]
|
||||
#[case::bad_fps(json!({"type":"video","source":{"type":"url","url":"u","fps":"fast"}}))]
|
||||
#[case::missing_text(json!({"type":"mid_conv_system"}))]
|
||||
fn provider_content_rejects_malformed_fields(#[case] wire: Value) {
|
||||
assert!(serde_json::from_value::<MinimaxMessagesContentBlock>(wire).is_err());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn partial_media_options_omit_null_optionals() {
|
||||
let block: MinimaxMessagesContentBlock = serde_json::from_value(json!({
|
||||
"type":"video",
|
||||
"source":{"type":"url","url":"u","detail":null,"fps":null,"future":null},
|
||||
"cache_control":null
|
||||
}))
|
||||
.unwrap();
|
||||
let MinimaxMessagesContentBlock::Video(video) = &block else {
|
||||
panic!("expected video")
|
||||
};
|
||||
let MinimaxMediaSource::Url { options, .. } = &video.source else {
|
||||
panic!("expected URL source");
|
||||
};
|
||||
assert!(options.detail.is_none());
|
||||
assert!(options.fps.is_none());
|
||||
assert!(video.cache_control.is_none());
|
||||
assert_eq!(
|
||||
serde_json::to_value(block).unwrap(),
|
||||
json!({"type":"video","source":{"type":"url","url":"u","future":null}})
|
||||
);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use litellm_llms_types::formats::ocr::{LiteLLMOcrResponse, OcrDocument, OcrPage};
|
||||
use litellm_llms_types::formats::ocr::{LiteLLMOcrResponse, OcrBoundingBox, OcrDocument, OcrPage};
|
||||
use rstest::rstest;
|
||||
use serde_json::{Map, Value, json};
|
||||
|
||||
|
|
@ -102,3 +102,39 @@ fn response_serialization_preserves_extensions_and_native_presence(
|
|||
assert_eq!(decoded.provider_native_response, native);
|
||||
assert_eq!(decoded.into_json(), serialized);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn bounding_box_exposes_corner_coordinates_and_keeps_extensions() {
|
||||
let wire = json!({"top_left_x":1,"top_left_y":2.5,"bottom_right_x":30,"bottom_right_y":40,"future":true});
|
||||
let bounds: OcrBoundingBox = serde_json::from_value(wire.clone()).unwrap();
|
||||
assert_eq!(bounds.top_left_x, Some(1.into()));
|
||||
assert_eq!(
|
||||
bounds
|
||||
.top_left_y
|
||||
.as_ref()
|
||||
.and_then(serde_json::Number::as_f64),
|
||||
Some(2.5)
|
||||
);
|
||||
assert_eq!(bounds.bottom_right_x, Some(30.into()));
|
||||
assert_eq!(bounds.bottom_right_y, Some(40.into()));
|
||||
assert_eq!(Value::Object(bounds.extra.clone()), json!({"future":true}));
|
||||
assert_eq!(serde_json::to_value(bounds).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn partial_bounding_box_omits_null_corners() {
|
||||
let bounds: OcrBoundingBox =
|
||||
serde_json::from_value(json!({"top_left_x":null,"bottom_right_y":4})).unwrap();
|
||||
assert!(bounds.top_left_x.is_none());
|
||||
assert_eq!(
|
||||
serde_json::to_value(bounds).unwrap(),
|
||||
json!({"bottom_right_y":4})
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::string_corner(json!({"top_left_x":"1"}))]
|
||||
#[case::array_corner(json!({"bottom_right_y":[4]}))]
|
||||
fn bounding_box_rejects_non_numeric_corners(#[case] wire: Value) {
|
||||
assert!(serde_json::from_value::<OcrBoundingBox>(wire).is_err());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,31 @@
|
|||
use litellm_llms_types::formats::responses::streaming_websocket::ResponsesWsEventType;
|
||||
use litellm_llms_types::formats::chat_completions::{PromptCacheBreakpoint, PromptCacheMode};
|
||||
use litellm_llms_types::formats::responses::{
|
||||
ResponsesAdditionalTools, ResponsesApplyPatchCall, ResponsesApplyPatchCallOutput,
|
||||
ResponsesApplyPatchOperation, ResponsesCodeOutput, ResponsesCompaction,
|
||||
ResponsesComputerAction, ResponsesComputerCall, ResponsesComputerCallOutput,
|
||||
ResponsesComputerOutput, ResponsesContentPart, ResponsesCoordinate, ResponsesCustomToolCall,
|
||||
ResponsesCustomToolCallOutput, ResponsesFunctionCall, ResponsesFunctionCallOutput,
|
||||
ResponsesImageGenerationCall, ResponsesInputContent, ResponsesLocalShellAction,
|
||||
ResponsesLocalShellCall, ResponsesLocalShellCallOutput, ResponsesMcpApprovalRequest,
|
||||
ResponsesMcpApprovalResponse, ResponsesMcpError, ResponsesMcpErrorDetail, ResponsesOutputItem,
|
||||
ResponsesProgram, ResponsesProgramOutput, ResponsesSafetyCheck, ResponsesShellAction,
|
||||
ResponsesShellCall, ResponsesShellCallOutput, ResponsesShellEnvironment, ResponsesShellOutcome,
|
||||
ResponsesShellOutputChunk, ResponsesToolCaller, ResponsesToolOutput, ResponsesToolSearchCall,
|
||||
ResponsesToolSearchOutput, ResponsesWebSearchAction,
|
||||
streaming_websocket::{ResponsesEventResponse, ResponsesWsEventType},
|
||||
};
|
||||
use litellm_llms_types::recognized::Recognized;
|
||||
use rstest::rstest;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
fn round_trip<T>(wire: Value)
|
||||
where
|
||||
T: DeserializeOwned + Serialize,
|
||||
{
|
||||
let parsed: T = serde_json::from_value(wire.clone()).unwrap();
|
||||
assert_eq!(serde_json::to_value(parsed).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::create("response.create", ResponsesWsEventType::ResponseCreate)]
|
||||
|
|
@ -46,3 +72,646 @@ fn websocket_event_type_schema_is_open_string() {
|
|||
Some(&serde_json::json!("string"))
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::message(json!({"type":"message","role":"assistant","content":[{"type":"output_text","text":"answer","annotations":[{"type":"url_citation","url":"https://example.test","start_index":0,"end_index":6}]}]}))]
|
||||
#[case::function_call(json!({"type":"function_call","call_id":"call_1","name":"lookup","arguments":"{\"query\":\"q\"}"}))]
|
||||
#[case::custom_tool(json!({"type":"custom_tool_call","name":"lookup","input":"q"}))]
|
||||
#[case::reasoning(json!({"type":"reasoning","summary":[{"type":"summary_text","text":"summary"}],"encrypted_content":"opaque"}))]
|
||||
#[case::web_search(json!({"type":"web_search_call","action":{"type":"search","queries":["q"],"sources":[{"type":"url","url":"https://example.test"}]}}))]
|
||||
#[case::file_search(json!({"type":"file_search_call","queries":["q"],"results":[{"file_id":"file_1","score":1,"attributes":{"custom":[1,null]}}]}))]
|
||||
#[case::code(json!({"type":"code_interpreter_call","outputs":[{"type":"logs","logs":"done"},{"type":"image","url":"https://example.test"}]}))]
|
||||
#[case::image(json!({"type":"image_generation_call","result":"generated"}))]
|
||||
#[case::mcp(json!({"type":"mcp_call","server_label":"server","name":"lookup","arguments":"{}","output":"done"}))]
|
||||
fn output_items_round_trip(#[case] wire: Value) {
|
||||
let item: ResponsesOutputItem = serde_json::from_value(wire.clone()).unwrap();
|
||||
match &item {
|
||||
ResponsesOutputItem::Message(message) => {
|
||||
assert_eq!(message.role.as_deref(), Some("assistant"));
|
||||
let Some(content) = &message.content else {
|
||||
panic!("expected content")
|
||||
};
|
||||
let [
|
||||
ResponsesContentPart::OutputText {
|
||||
text,
|
||||
annotations: Some(annotations),
|
||||
..
|
||||
},
|
||||
] = content.as_slice()
|
||||
else {
|
||||
panic!("expected output text and annotations")
|
||||
};
|
||||
assert_eq!(text, "answer");
|
||||
let [
|
||||
litellm_llms_types::formats::responses::ResponsesAnnotation::UrlCitation(citation),
|
||||
] = annotations.as_slice()
|
||||
else {
|
||||
panic!("expected URL citation")
|
||||
};
|
||||
assert_eq!(citation.url.as_deref(), Some("https://example.test"));
|
||||
assert_eq!(citation.start_index, Some(0));
|
||||
}
|
||||
ResponsesOutputItem::FunctionCall(call) => {
|
||||
assert_eq!(call.call_id.as_deref(), Some("call_1"));
|
||||
assert_eq!(call.name.as_deref(), Some("lookup"));
|
||||
assert_eq!(call.arguments.as_deref(), Some("{\"query\":\"q\"}"));
|
||||
}
|
||||
ResponsesOutputItem::CustomToolCall(call) => {
|
||||
assert_eq!(call.name.as_deref(), Some("lookup"));
|
||||
assert_eq!(call.input.as_deref(), Some("q"));
|
||||
}
|
||||
ResponsesOutputItem::Reasoning(reasoning) => {
|
||||
assert_eq!(reasoning.encrypted_content.as_deref(), Some("opaque"));
|
||||
let Some(summary) = &reasoning.summary else {
|
||||
panic!("expected summary")
|
||||
};
|
||||
let [ResponsesContentPart::SummaryText { text, .. }] = summary.as_slice() else {
|
||||
panic!("expected summary text")
|
||||
};
|
||||
assert_eq!(text, "summary");
|
||||
}
|
||||
ResponsesOutputItem::WebSearchCall(call) => {
|
||||
let Some(ResponsesWebSearchAction::Search {
|
||||
queries: Some(queries),
|
||||
sources: Some(sources),
|
||||
..
|
||||
}) = &call.action
|
||||
else {
|
||||
panic!("expected search action")
|
||||
};
|
||||
assert_eq!(queries, &["q"]);
|
||||
assert_eq!(sources[0].url.as_deref(), Some("https://example.test"));
|
||||
}
|
||||
ResponsesOutputItem::FileSearchCall(call) => {
|
||||
assert_eq!(call.queries.as_deref(), Some(["q".to_owned()].as_slice()));
|
||||
let Some(results) = &call.results else {
|
||||
panic!("expected search results")
|
||||
};
|
||||
assert_eq!(results[0].file_id.as_deref(), Some("file_1"));
|
||||
assert_eq!(results[0].score, Some(1.into()));
|
||||
assert_eq!(
|
||||
results[0].attributes.as_ref().unwrap()["custom"],
|
||||
json!([1, null])
|
||||
);
|
||||
}
|
||||
ResponsesOutputItem::CodeInterpreterCall(call) => {
|
||||
let Some(outputs) = &call.outputs else {
|
||||
panic!("expected code outputs")
|
||||
};
|
||||
let [
|
||||
ResponsesCodeOutput::Logs { logs, .. },
|
||||
ResponsesCodeOutput::Image { url, .. },
|
||||
] = outputs.as_slice()
|
||||
else {
|
||||
panic!("expected logs and image")
|
||||
};
|
||||
assert_eq!(logs, "done");
|
||||
assert_eq!(url, "https://example.test");
|
||||
}
|
||||
ResponsesOutputItem::ImageGenerationCall(call) => {
|
||||
assert_eq!(call.result.as_deref(), Some("generated"))
|
||||
}
|
||||
ResponsesOutputItem::McpCall(call) => {
|
||||
assert_eq!(call.server_label.as_deref(), Some("server"));
|
||||
assert_eq!(call.name.as_deref(), Some("lookup"));
|
||||
assert_eq!(call.arguments.as_deref(), Some("{}"));
|
||||
assert_eq!(call.output.as_deref(), Some("done"));
|
||||
}
|
||||
other => panic!("unexpected variant {other:?}"),
|
||||
}
|
||||
assert_eq!(serde_json::to_value(item).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn nested_event_response_exposes_typed_output_and_preserves_extensions() {
|
||||
let wire = json!({
|
||||
"id":"response_1",
|
||||
"model":"example-model",
|
||||
"status":"completed",
|
||||
"output":[{"type":"function_call","call_id":"call_1","name":"lookup","arguments":"{}","extension":true}],
|
||||
"extension":{"nested":[1,null]}
|
||||
});
|
||||
let response: ResponsesEventResponse = serde_json::from_value(wire.clone()).unwrap();
|
||||
assert_eq!(response.id.as_deref(), Some("response_1"));
|
||||
assert_eq!(response.model.as_deref(), Some("example-model"));
|
||||
let Some(output) = &response.output else {
|
||||
panic!("expected typed output");
|
||||
};
|
||||
let [Recognized::Known(ResponsesOutputItem::FunctionCall(call))] = output.as_slice() else {
|
||||
panic!("expected function call");
|
||||
};
|
||||
assert_eq!(call.name.as_deref(), Some("lookup"));
|
||||
assert_eq!(call.arguments.as_deref(), Some("{}"));
|
||||
assert_eq!(serde_json::to_value(response).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::empty(json!({}))]
|
||||
#[case::partial(json!({"id":"response_1","output":[]}))]
|
||||
fn nested_event_response_accepts_partial_metadata(#[case] wire: Value) {
|
||||
round_trip::<ResponsesEventResponse>(wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::wrong_model(json!({"model":7}))]
|
||||
#[case::wrong_status(json!({"status":false}))]
|
||||
#[case::wrong_output(json!({"output":{}}))]
|
||||
fn event_response_rejects_malformed_typed_fields(#[case] wire: Value) {
|
||||
assert!(serde_json::from_value::<ResponsesEventResponse>(wire).is_err());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::unknown_type(json!({"type":"future_item","id":"item_1","payload":[1,null]}))]
|
||||
#[case::missing_tag(json!({"id":"item_1"}))]
|
||||
#[case::malformed_known(json!({"type":"message","content":[{"type":"output_text","text":7}]}))]
|
||||
fn event_response_keeps_unmodeled_output_items_beside_typed_ones(#[case] item: Value) {
|
||||
let wire = json!({"output":[{"type":"function_call","name":"lookup"}, item.clone()]});
|
||||
let response: ResponsesEventResponse = serde_json::from_value(wire.clone()).unwrap();
|
||||
let Some(
|
||||
[
|
||||
Recognized::Known(ResponsesOutputItem::FunctionCall(call)),
|
||||
Recognized::Unrecognized(kept),
|
||||
],
|
||||
) = response.output.as_deref()
|
||||
else {
|
||||
panic!("expected one typed item and one preserved item");
|
||||
};
|
||||
assert_eq!(call.name.as_deref(), Some("lookup"));
|
||||
assert_eq!(kept, &item);
|
||||
assert_eq!(serde_json::to_value(response).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::find_in_page(
|
||||
json!({"type":"find_in_page","url":"https://example.test","pattern":"needle"}),
|
||||
json!({"type":"find_in_page","url":"https://example.test","pattern":"needle"})
|
||||
)]
|
||||
#[case::legacy_find(
|
||||
json!({"type":"find","url":"https://example.test","pattern":"needle"}),
|
||||
json!({"type":"find_in_page","url":"https://example.test","pattern":"needle"})
|
||||
)]
|
||||
fn web_search_find_action_exposes_url_and_pattern(#[case] wire: Value, #[case] serialized: Value) {
|
||||
let action: ResponsesWebSearchAction = serde_json::from_value(wire).unwrap();
|
||||
let ResponsesWebSearchAction::FindInPage {
|
||||
url,
|
||||
pattern,
|
||||
extra,
|
||||
} = &action
|
||||
else {
|
||||
panic!("expected find_in_page action");
|
||||
};
|
||||
assert_eq!(url, "https://example.test");
|
||||
assert_eq!(pattern, "needle");
|
||||
assert!(extra.is_empty());
|
||||
assert_eq!(serde_json::to_value(action).unwrap(), serialized);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::with_url(json!({"type":"open_page","url":"https://example.test"}), Some("https://example.test"))]
|
||||
#[case::without_url(json!({"type":"open_page"}), None)]
|
||||
fn web_search_open_page_url_is_optional(#[case] wire: Value, #[case] expected: Option<&str>) {
|
||||
let action: ResponsesWebSearchAction = serde_json::from_value(wire.clone()).unwrap();
|
||||
let ResponsesWebSearchAction::OpenPage { url, .. } = &action else {
|
||||
panic!("expected open_page action");
|
||||
};
|
||||
assert_eq!(url.as_deref(), expected);
|
||||
assert_eq!(serde_json::to_value(action).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::find_pattern(json!({"type":"find_in_page","url":"u","pattern":false}))]
|
||||
#[case::find_missing_url(json!({"type":"find_in_page","pattern":"p"}))]
|
||||
#[case::open_page_url(json!({"type":"open_page","url":7}))]
|
||||
fn web_search_action_rejects_malformed_fields(#[case] wire: Value) {
|
||||
assert!(serde_json::from_value::<ResponsesWebSearchAction>(wire).is_err());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn event_response_optional_fields_omit_missing_and_null() {
|
||||
let response: ResponsesEventResponse = serde_json::from_value(json!({
|
||||
"id":null,"model":null,"status":null,"output":null,"future":null
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(response.id.is_none());
|
||||
assert!(response.model.is_none());
|
||||
assert!(response.status.is_none());
|
||||
assert!(response.output.is_none());
|
||||
assert_eq!(
|
||||
serde_json::to_value(response).unwrap(),
|
||||
json!({"future":null})
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::refusal(json!({"type":"refusal","refusal":"refused","future":null}), ResponsesContentPart::Refusal { refusal:"refused".into(), extra:serde_json::Map::from_iter([("future".into(), Value::Null)]) })]
|
||||
#[case::reasoning(json!({"type":"reasoning_text","text":"reason"}), ResponsesContentPart::ReasoningText { text:"reason".into(), extra:Default::default() })]
|
||||
fn content_parts_expose_typed_variants(
|
||||
#[case] wire: Value,
|
||||
#[case] expected: ResponsesContentPart,
|
||||
) {
|
||||
let content: ResponsesContentPart = serde_json::from_value(wire.clone()).unwrap();
|
||||
assert_eq!(content, expected);
|
||||
assert_eq!(serde_json::to_value(content).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn mcp_discovery_exposes_tools_and_nested_schemas() {
|
||||
let wire = json!({
|
||||
"type":"mcp_list_tools","id":"item_1","server_label":"tools",
|
||||
"tools":[{"name":"lookup","description":"Look up a value","input_schema":{"type":"object","properties":{"query":{"type":"string"}}},"annotations":{"read_only":false},"future":null}],
|
||||
"extension":[1,null]
|
||||
});
|
||||
let item: ResponsesOutputItem = serde_json::from_value(wire.clone()).unwrap();
|
||||
let ResponsesOutputItem::McpListTools(discovery) = &item else {
|
||||
panic!("expected discovery")
|
||||
};
|
||||
assert_eq!(discovery.server_label.as_deref(), Some("tools"));
|
||||
let tool = &discovery.tools.as_ref().unwrap()[0];
|
||||
assert_eq!(tool.name.as_deref(), Some("lookup"));
|
||||
let Some(litellm_llms_types::json_schema::JsonSchema::Object(schema)) = &tool.input_schema
|
||||
else {
|
||||
panic!("expected tool schema")
|
||||
};
|
||||
assert!(schema.properties.as_ref().unwrap().contains_key("query"));
|
||||
assert_eq!(tool.annotations, Some(json!({"read_only":false})));
|
||||
assert_eq!(tool.extra["future"], Value::Null);
|
||||
assert_eq!(serde_json::to_value(item).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::partial(json!({"server_label":"tools","tools":[]}))]
|
||||
#[case::failure(json!({"server_label":"tools","error":"unavailable"}))]
|
||||
fn mcp_discovery_accepts_partial_payloads(#[case] wire: Value) {
|
||||
let discovery: litellm_llms_types::formats::responses::ResponsesMcpListTools =
|
||||
serde_json::from_value(wire.clone()).unwrap();
|
||||
assert_eq!(discovery.server_label.as_deref(), Some("tools"));
|
||||
assert!(discovery.id.is_none());
|
||||
assert_eq!(serde_json::to_value(discovery).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::message(json!("unavailable"), ResponsesMcpError::Message("unavailable".into()))]
|
||||
#[case::protocol(json!({"type":"mcp_protocol_error","code":-32600,"message":"invalid","extension":null}), ResponsesMcpError::Detail(ResponsesMcpErrorDetail::McpProtocolError {code:-32600,message:"invalid".into(),extra:serde_json::Map::from_iter([("extension".into(), Value::Null)])}))]
|
||||
#[case::http(json!({"type":"http_error","code":503,"message":"unavailable"}), ResponsesMcpError::Detail(ResponsesMcpErrorDetail::HttpError {code:503,message:"unavailable".into(),extra:Default::default()}))]
|
||||
#[case::tool(json!({"type":"mcp_tool_execution_error","content":{"arbitrary":[1,null]}}), ResponsesMcpError::Detail(ResponsesMcpErrorDetail::McpToolExecutionError {content:json!({"arbitrary":[1,null]}),extra:Default::default()}))]
|
||||
fn mcp_call_errors_expose_typed_variants(#[case] wire: Value, #[case] expected: ResponsesMcpError) {
|
||||
let error: ResponsesMcpError = serde_json::from_value(wire.clone()).unwrap();
|
||||
assert_eq!(error, expected);
|
||||
assert_eq!(serde_json::to_value(&error).unwrap(), wire);
|
||||
let call: ResponsesOutputItem =
|
||||
serde_json::from_value(json!({"type":"mcp_call","error":wire})).unwrap();
|
||||
let ResponsesOutputItem::McpCall(call) = call else {
|
||||
panic!("expected call")
|
||||
};
|
||||
assert_eq!(call.error, Some(error));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::tools_not_array(json!({"type":"mcp_list_tools","tools":{}}))]
|
||||
#[case::invalid_name(json!({"type":"mcp_list_tools","tools":[{"name":7}]}))]
|
||||
#[case::invalid_schema(json!({"type":"mcp_list_tools","tools":[{"input_schema":{"properties":{"query":7}}}]}))]
|
||||
#[case::invalid_error_code(json!({"type":"mcp_call","error":{"type":"http_error","code":"503","message":"unavailable"}}))]
|
||||
#[case::missing_error_content(json!({"type":"mcp_call","error":{"type":"mcp_tool_execution_error"}}))]
|
||||
#[case::unknown_error_tag(json!({"type":"mcp_call","error":{"type":"future"}}))]
|
||||
fn mcp_output_rejects_malformed_known_fields(#[case] wire: Value) {
|
||||
assert!(serde_json::from_value::<ResponsesOutputItem>(wire).is_err());
|
||||
}
|
||||
|
||||
fn text(value: &str) -> Option<String> {
|
||||
Some(value.to_owned())
|
||||
}
|
||||
|
||||
fn program_caller() -> Option<ResponsesToolCaller> {
|
||||
Some(ResponsesToolCaller::Program {
|
||||
caller_id: "prog_1".into(),
|
||||
extra: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn direct_caller() -> Option<ResponsesToolCaller> {
|
||||
Some(ResponsesToolCaller::Direct {
|
||||
extra: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn safety_check() -> ResponsesSafetyCheck {
|
||||
ResponsesSafetyCheck {
|
||||
id: text("sc_1"),
|
||||
code: text("malicious_instructions"),
|
||||
message: text("check"),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::function_call(
|
||||
json!({"type":"function_call","id":"fc_1","status":"completed","call_id":"call_1","name":"lookup","arguments":"{}","namespace":"ns","async":true,"caller":{"type":"program","caller_id":"prog_1"}}),
|
||||
ResponsesOutputItem::FunctionCall(ResponsesFunctionCall {
|
||||
id: text("fc_1"), status: text("completed"), call_id: text("call_1"), name: text("lookup"),
|
||||
arguments: text("{}"), namespace: text("ns"), r#async: Some(true), caller: program_caller(), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::custom_tool_call(
|
||||
json!({"type":"custom_tool_call","call_id":"call_1","name":"lookup","input":"q","namespace":"ns","async":false,"caller":{"type":"direct"}}),
|
||||
ResponsesOutputItem::CustomToolCall(ResponsesCustomToolCall {
|
||||
call_id: text("call_1"), name: text("lookup"), input: text("q"), namespace: text("ns"),
|
||||
r#async: Some(false), caller: direct_caller(), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::image_generation_call(
|
||||
json!({"type":"image_generation_call","id":"ig_1","status":"completed","result":"b64","action":"edit","background":"opaque","output_format":"webp","quality":"high","revised_prompt":"a cat","size":"1536x864"}),
|
||||
ResponsesOutputItem::ImageGenerationCall(ResponsesImageGenerationCall {
|
||||
id: text("ig_1"), status: text("completed"), result: text("b64"), action: text("edit"), background: text("opaque"),
|
||||
output_format: text("webp"), quality: text("high"), revised_prompt: text("a cat"), size: text("1536x864"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::function_call_output_text(
|
||||
json!({"type":"function_call_output","id":"fco_1","status":"completed","call_id":"call_1","output":"done","caller":{"type":"direct"},"created_by":"user_1","name":"lookup","namespace":"ns"}),
|
||||
ResponsesOutputItem::FunctionCallOutput(ResponsesFunctionCallOutput {
|
||||
id: text("fco_1"), status: text("completed"), call_id: text("call_1"), output: Some(ResponsesToolOutput::Text("done".into())),
|
||||
caller: direct_caller(), created_by: text("user_1"), name: text("lookup"), namespace: text("ns"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::function_call_output_content(
|
||||
json!({"type":"function_call_output","call_id":"call_1","output":[
|
||||
{"type":"input_text","text":"t","prompt_cache_breakpoint":{"mode":"explicit"}},
|
||||
{"type":"input_image","detail":"low","file_id":"file_1","image_url":"https://example.test/i.png"},
|
||||
{"type":"input_file","detail":"high","file_data":"data","file_id":"file_2","file_url":"https://example.test/f","filename":"f.pdf"}
|
||||
]}),
|
||||
ResponsesOutputItem::FunctionCallOutput(ResponsesFunctionCallOutput {
|
||||
call_id: text("call_1"),
|
||||
output: Some(ResponsesToolOutput::Content(vec![
|
||||
ResponsesInputContent::InputText {
|
||||
text: "t".into(),
|
||||
prompt_cache_breakpoint: Some(PromptCacheBreakpoint { mode: PromptCacheMode::Explicit, extra: Default::default() }),
|
||||
extra: Default::default(),
|
||||
},
|
||||
ResponsesInputContent::InputImage {
|
||||
detail: "low".into(), file_id: text("file_1"), image_url: text("https://example.test/i.png"),
|
||||
prompt_cache_breakpoint: None, extra: Default::default(),
|
||||
},
|
||||
ResponsesInputContent::InputFile {
|
||||
detail: text("high"), file_data: text("data"), file_id: text("file_2"), file_url: text("https://example.test/f"),
|
||||
filename: text("f.pdf"), prompt_cache_breakpoint: None, extra: Default::default(),
|
||||
},
|
||||
])),
|
||||
..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::custom_tool_call_output(
|
||||
json!({"type":"custom_tool_call_output","id":"cto_1","status":"completed","call_id":"call_1","output":[{"type":"input_text","text":"t"}],"caller":{"type":"program","caller_id":"prog_1"},"created_by":"user_1"}),
|
||||
ResponsesOutputItem::CustomToolCallOutput(ResponsesCustomToolCallOutput {
|
||||
id: text("cto_1"), status: text("completed"), call_id: text("call_1"),
|
||||
output: Some(ResponsesToolOutput::Content(vec![ResponsesInputContent::InputText {
|
||||
text: "t".into(), prompt_cache_breakpoint: None, extra: Default::default(),
|
||||
}])),
|
||||
caller: program_caller(), created_by: text("user_1"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::computer_call(
|
||||
json!({"type":"computer_call","id":"cu_1","status":"completed","call_id":"call_1",
|
||||
"pending_safety_checks":[{"id":"sc_1","code":"malicious_instructions","message":"check"}],
|
||||
"action":{"type":"click","button":"left","x":1,"y":2,"keys":["shift"]},
|
||||
"actions":[
|
||||
{"type":"double_click","x":3,"y":4},
|
||||
{"type":"drag","path":[{"x":5,"y":6},{"x":7,"y":8}],"keys":["ctrl"]},
|
||||
{"type":"keypress","keys":["enter"]},
|
||||
{"type":"move","x":-1,"y":9},
|
||||
{"type":"screenshot"},
|
||||
{"type":"scroll","scroll_x":0,"scroll_y":-10,"x":11,"y":12},
|
||||
{"type":"type","text":"hello"},
|
||||
{"type":"wait"}
|
||||
]}),
|
||||
ResponsesOutputItem::ComputerCall(ResponsesComputerCall {
|
||||
id: text("cu_1"), status: text("completed"), call_id: text("call_1"), pending_safety_checks: Some(vec![safety_check()]),
|
||||
action: Some(ResponsesComputerAction::Click { button: "left".into(), x: 1, y: 2, keys: Some(vec!["shift".into()]), extra: Default::default() }),
|
||||
actions: Some(vec![
|
||||
ResponsesComputerAction::DoubleClick { x: 3, y: 4, keys: None, extra: Default::default() },
|
||||
ResponsesComputerAction::Drag {
|
||||
path: vec![
|
||||
ResponsesCoordinate { x: 5, y: 6, extra: Default::default() },
|
||||
ResponsesCoordinate { x: 7, y: 8, extra: Default::default() },
|
||||
],
|
||||
keys: Some(vec!["ctrl".into()]),
|
||||
extra: Default::default(),
|
||||
},
|
||||
ResponsesComputerAction::Keypress { keys: vec!["enter".into()], extra: Default::default() },
|
||||
ResponsesComputerAction::Move { x: -1, y: 9, keys: None, extra: Default::default() },
|
||||
ResponsesComputerAction::Screenshot { extra: Default::default() },
|
||||
ResponsesComputerAction::Scroll { scroll_x: 0, scroll_y: -10, x: 11, y: 12, keys: None, extra: Default::default() },
|
||||
ResponsesComputerAction::Type { text: "hello".into(), extra: Default::default() },
|
||||
ResponsesComputerAction::Wait { extra: Default::default() },
|
||||
]),
|
||||
..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::computer_call_output(
|
||||
json!({"type":"computer_call_output","id":"cuo_1","status":"completed","call_id":"call_1","output":{"type":"computer_screenshot","file_id":"file_1","image_url":"https://example.test/s.png"},"acknowledged_safety_checks":[{"id":"sc_1","code":"malicious_instructions","message":"check"}],"created_by":"user_1"}),
|
||||
ResponsesOutputItem::ComputerCallOutput(ResponsesComputerCallOutput {
|
||||
id: text("cuo_1"), status: text("completed"), call_id: text("call_1"),
|
||||
output: Some(ResponsesComputerOutput::ComputerScreenshot { file_id: text("file_1"), image_url: text("https://example.test/s.png"), extra: Default::default() }),
|
||||
acknowledged_safety_checks: Some(vec![safety_check()]), created_by: text("user_1"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::program(
|
||||
json!({"type":"program","id":"prog_item","call_id":"prog_1","code":"run()","fingerprint":"fp"}),
|
||||
ResponsesOutputItem::Program(ResponsesProgram {
|
||||
id: text("prog_item"), call_id: text("prog_1"), code: text("run()"), fingerprint: text("fp"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::program_output(
|
||||
json!({"type":"program_output","id":"po_1","status":"completed","call_id":"prog_1","result":"42"}),
|
||||
ResponsesOutputItem::ProgramOutput(ResponsesProgramOutput {
|
||||
id: text("po_1"), status: text("completed"), call_id: text("prog_1"), result: text("42"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::tool_search_call(
|
||||
json!({"type":"tool_search_call","id":"ts_1","status":"completed","call_id":"call_1","arguments":{"query":["weather",null]},"execution":"server","created_by":"user_1"}),
|
||||
ResponsesOutputItem::ToolSearchCall(ResponsesToolSearchCall {
|
||||
id: text("ts_1"), status: text("completed"), call_id: text("call_1"), arguments: Some(json!({"query":["weather",null]})),
|
||||
execution: text("server"), created_by: text("user_1"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::tool_search_output(
|
||||
json!({"type":"tool_search_output","id":"tso_1","status":"completed","call_id":"call_1","execution":"client","tools":[{"type":"function","name":"lookup","parameters":null,"strict":true}],"created_by":"user_1"}),
|
||||
ResponsesOutputItem::ToolSearchOutput(ResponsesToolSearchOutput {
|
||||
id: text("tso_1"), status: text("completed"), call_id: text("call_1"), execution: text("client"),
|
||||
tools: Some(vec![serde_json::Map::from_iter([
|
||||
("type".into(), json!("function")), ("name".into(), json!("lookup")),
|
||||
("parameters".into(), Value::Null), ("strict".into(), json!(true)),
|
||||
])]),
|
||||
created_by: text("user_1"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::additional_tools(
|
||||
json!({"type":"additional_tools","id":"at_1","role":"developer","tools":[{"type":"local_shell"}]}),
|
||||
ResponsesOutputItem::AdditionalTools(ResponsesAdditionalTools {
|
||||
id: text("at_1"), role: text("developer"),
|
||||
tools: Some(vec![serde_json::Map::from_iter([("type".into(), json!("local_shell"))])]), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::compaction(
|
||||
json!({"type":"compaction","id":"cmp_1","encrypted_content":"opaque","created_by":"user_1"}),
|
||||
ResponsesOutputItem::Compaction(ResponsesCompaction {
|
||||
id: text("cmp_1"), encrypted_content: text("opaque"), created_by: text("user_1"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::local_shell_call(
|
||||
json!({"type":"local_shell_call","id":"ls_1","status":"completed","call_id":"call_1","action":{"type":"exec","command":["ls","-a"],"env":{"HOME":"/home/u"},"timeout_ms":1000,"user":"u","working_directory":"/tmp"}}),
|
||||
ResponsesOutputItem::LocalShellCall(ResponsesLocalShellCall {
|
||||
id: text("ls_1"), status: text("completed"), call_id: text("call_1"),
|
||||
action: Some(ResponsesLocalShellAction::Exec {
|
||||
command: vec!["ls".into(), "-a".into()], env: [("HOME".into(), "/home/u".into())].into(),
|
||||
timeout_ms: Some(1000), user: text("u"), working_directory: text("/tmp"), extra: Default::default(),
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::local_shell_call_output(
|
||||
json!({"type":"local_shell_call_output","id":"lso_1","status":"completed","output":"{\"stdout\":\"x\"}"}),
|
||||
ResponsesOutputItem::LocalShellCallOutput(ResponsesLocalShellCallOutput {
|
||||
id: text("lso_1"), status: text("completed"), output: text("{\"stdout\":\"x\"}"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::shell_call_local(
|
||||
json!({"type":"shell_call","id":"sh_1","status":"in_progress","call_id":"call_1","action":{"commands":["ls"],"max_output_length":100,"timeout_ms":500},"environment":{"type":"local"},"caller":{"type":"direct"},"created_by":"user_1"}),
|
||||
ResponsesOutputItem::ShellCall(ResponsesShellCall {
|
||||
id: text("sh_1"), status: text("in_progress"), call_id: text("call_1"),
|
||||
action: Some(ResponsesShellAction { commands: Some(vec!["ls".into()]), max_output_length: Some(100), timeout_ms: Some(500), ..Default::default() }),
|
||||
environment: Some(ResponsesShellEnvironment::Local { extra: Default::default() }),
|
||||
caller: direct_caller(), created_by: text("user_1"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::shell_call_container(
|
||||
json!({"type":"shell_call","call_id":"call_1","environment":{"type":"container_reference","container_id":"cntr_1"}}),
|
||||
ResponsesOutputItem::ShellCall(ResponsesShellCall {
|
||||
call_id: text("call_1"),
|
||||
environment: Some(ResponsesShellEnvironment::ContainerReference { container_id: "cntr_1".into(), extra: Default::default() }),
|
||||
..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::shell_call_output(
|
||||
json!({"type":"shell_call_output","id":"sho_1","status":"completed","call_id":"call_1","max_output_length":100,"output":[
|
||||
{"outcome":{"type":"exit","exit_code":2},"stdout":"out","stderr":"err","created_by":"user_1"},
|
||||
{"outcome":{"type":"timeout"},"stdout":"","stderr":""}
|
||||
],"caller":{"type":"program","caller_id":"prog_1"},"created_by":"user_1"}),
|
||||
ResponsesOutputItem::ShellCallOutput(ResponsesShellCallOutput {
|
||||
id: text("sho_1"), status: text("completed"), call_id: text("call_1"), max_output_length: Some(100),
|
||||
output: Some(vec![
|
||||
ResponsesShellOutputChunk {
|
||||
outcome: Some(ResponsesShellOutcome::Exit { exit_code: 2, extra: Default::default() }),
|
||||
stdout: text("out"), stderr: text("err"), created_by: text("user_1"), ..Default::default()
|
||||
},
|
||||
ResponsesShellOutputChunk {
|
||||
outcome: Some(ResponsesShellOutcome::Timeout { extra: Default::default() }),
|
||||
stdout: text(""), stderr: text(""), ..Default::default()
|
||||
},
|
||||
]),
|
||||
caller: program_caller(), created_by: text("user_1"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::apply_patch_create(
|
||||
json!({"type":"apply_patch_call","id":"ap_1","status":"completed","call_id":"call_1","operation":{"type":"create_file","path":"a.txt","diff":"+a"},"caller":{"type":"direct"},"created_by":"user_1"}),
|
||||
ResponsesOutputItem::ApplyPatchCall(ResponsesApplyPatchCall {
|
||||
id: text("ap_1"), status: text("completed"), call_id: text("call_1"),
|
||||
operation: Some(ResponsesApplyPatchOperation::CreateFile { path: "a.txt".into(), diff: "+a".into(), extra: Default::default() }),
|
||||
caller: direct_caller(), created_by: text("user_1"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::apply_patch_delete(
|
||||
json!({"type":"apply_patch_call","call_id":"call_1","operation":{"type":"delete_file","path":"a.txt"}}),
|
||||
ResponsesOutputItem::ApplyPatchCall(ResponsesApplyPatchCall {
|
||||
call_id: text("call_1"),
|
||||
operation: Some(ResponsesApplyPatchOperation::DeleteFile { path: "a.txt".into(), extra: Default::default() }),
|
||||
..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::apply_patch_update(
|
||||
json!({"type":"apply_patch_call","call_id":"call_1","operation":{"type":"update_file","path":"a.txt","diff":"-a\n+b"}}),
|
||||
ResponsesOutputItem::ApplyPatchCall(ResponsesApplyPatchCall {
|
||||
call_id: text("call_1"),
|
||||
operation: Some(ResponsesApplyPatchOperation::UpdateFile { path: "a.txt".into(), diff: "-a\n+b".into(), extra: Default::default() }),
|
||||
..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::apply_patch_call_output(
|
||||
json!({"type":"apply_patch_call_output","id":"apo_1","status":"failed","call_id":"call_1","output":"conflict","caller":{"type":"direct"},"created_by":"user_1"}),
|
||||
ResponsesOutputItem::ApplyPatchCallOutput(ResponsesApplyPatchCallOutput {
|
||||
id: text("apo_1"), status: text("failed"), call_id: text("call_1"), output: text("conflict"),
|
||||
caller: direct_caller(), created_by: text("user_1"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::mcp_approval_request(
|
||||
json!({"type":"mcp_approval_request","id":"apr_1","server_label":"s","name":"n","arguments":"{}"}),
|
||||
ResponsesOutputItem::McpApprovalRequest(ResponsesMcpApprovalRequest {
|
||||
id: text("apr_1"), server_label: text("s"), name: text("n"), arguments: text("{}"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
#[case::mcp_approval_response(
|
||||
json!({"type":"mcp_approval_response","id":"aprr_1","approval_request_id":"apr_1","approve":false,"reason":"denied"}),
|
||||
ResponsesOutputItem::McpApprovalResponse(ResponsesMcpApprovalResponse {
|
||||
id: text("aprr_1"), approval_request_id: text("apr_1"), approve: Some(false), reason: text("denied"), ..Default::default()
|
||||
})
|
||||
)]
|
||||
fn documented_output_items_parse_into_typed_variants(
|
||||
#[case] wire: Value,
|
||||
#[case] expected: ResponsesOutputItem,
|
||||
) {
|
||||
let item: ResponsesOutputItem = serde_json::from_value(wire.clone()).unwrap();
|
||||
assert_eq!(item, expected);
|
||||
assert_eq!(serde_json::to_value(item).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::compaction(json!({"type":"compaction","id":"cmp_1","encrypted_content":"opaque"}))]
|
||||
#[case::approval(json!({"type":"mcp_approval_request","id":"apr_1","server_label":"s","name":"n","arguments":"{}"}))]
|
||||
#[case::shell(json!({"type":"shell_call","call_id":"call_1","action":{"commands":["ls"]}}))]
|
||||
fn event_response_types_documented_output_items(#[case] item: Value) {
|
||||
let wire = json!({"output":[item.clone()]});
|
||||
let response: ResponsesEventResponse = serde_json::from_value(wire.clone()).unwrap();
|
||||
let Some([Recognized::Known(known)]) = response.output.as_deref() else {
|
||||
panic!("expected one typed item");
|
||||
};
|
||||
assert_eq!(
|
||||
known,
|
||||
&serde_json::from_value::<ResponsesOutputItem>(item).unwrap()
|
||||
);
|
||||
assert_eq!(serde_json::to_value(response).unwrap(), wire);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::unknown_caller(json!({"type":"function_call","caller":{"type":"future"}}))]
|
||||
#[case::program_caller_missing_id(json!({"type":"shell_call","caller":{"type":"program"}}))]
|
||||
#[case::untagged_caller(json!({"type":"custom_tool_call","caller":{}}))]
|
||||
#[case::async_not_bool(json!({"type":"function_call","async":"yes"}))]
|
||||
#[case::output_not_text_or_list(json!({"type":"function_call_output","output":{"text":"t"}}))]
|
||||
#[case::unknown_input_content(json!({"type":"function_call_output","output":[{"type":"input_audio"}]}))]
|
||||
#[case::input_text_missing_text(json!({"type":"custom_tool_call_output","output":[{"type":"input_text"}]}))]
|
||||
#[case::input_image_missing_detail(json!({"type":"custom_tool_call_output","output":[{"type":"input_image","file_id":"f"}]}))]
|
||||
#[case::bad_cache_mode(json!({"type":"custom_tool_call_output","output":[{"type":"input_text","text":"t","prompt_cache_breakpoint":{"mode":"implicit"}}]}))]
|
||||
#[case::unknown_computer_action(json!({"type":"computer_call","action":{"type":"teleport"}}))]
|
||||
#[case::click_missing_button(json!({"type":"computer_call","action":{"type":"click","x":1,"y":2}}))]
|
||||
#[case::click_fractional_x(json!({"type":"computer_call","action":{"type":"click","button":"left","x":1.5,"y":2}}))]
|
||||
#[case::drag_point_missing_y(json!({"type":"computer_call","actions":[{"type":"drag","path":[{"x":1}]}]}))]
|
||||
#[case::keypress_keys_not_list(json!({"type":"computer_call","actions":[{"type":"keypress","keys":"enter"}]}))]
|
||||
#[case::safety_check_not_object(json!({"type":"computer_call","pending_safety_checks":["sc_1"]}))]
|
||||
#[case::unknown_screenshot_tag(json!({"type":"computer_call_output","output":{"type":"screenshot"}}))]
|
||||
#[case::untagged_screenshot(json!({"type":"computer_call_output","output":{"file_id":"f"}}))]
|
||||
#[case::tool_not_object(json!({"type":"tool_search_output","tools":["lookup"]}))]
|
||||
#[case::unknown_local_shell_action(json!({"type":"local_shell_call","action":{"type":"spawn","command":[],"env":{}}}))]
|
||||
#[case::local_shell_missing_env(json!({"type":"local_shell_call","action":{"type":"exec","command":["ls"]}}))]
|
||||
#[case::local_shell_env_value(json!({"type":"local_shell_call","action":{"type":"exec","command":["ls"],"env":{"A":1}}}))]
|
||||
#[case::shell_commands_not_list(json!({"type":"shell_call","action":{"commands":"ls"}}))]
|
||||
#[case::unknown_shell_environment(json!({"type":"shell_call","environment":{"type":"container_auto"}}))]
|
||||
#[case::container_missing_id(json!({"type":"shell_call","environment":{"type":"container_reference"}}))]
|
||||
#[case::unknown_shell_outcome(json!({"type":"shell_call_output","output":[{"outcome":{"type":"killed"}}]}))]
|
||||
#[case::exit_missing_code(json!({"type":"shell_call_output","output":[{"outcome":{"type":"exit"}}]}))]
|
||||
#[case::negative_max_output(json!({"type":"shell_call_output","max_output_length":-1}))]
|
||||
#[case::unknown_patch_operation(json!({"type":"apply_patch_call","operation":{"type":"rename_file","path":"a"}}))]
|
||||
#[case::update_missing_diff(json!({"type":"apply_patch_call","operation":{"type":"update_file","path":"a"}}))]
|
||||
#[case::approve_not_bool(json!({"type":"mcp_approval_response","approve":"true"}))]
|
||||
#[case::compaction_content_not_string(json!({"type":"compaction","encrypted_content":7}))]
|
||||
#[case::program_code_not_string(json!({"type":"program","code":["run()"]}))]
|
||||
fn output_items_reject_malformed_typed_fields(#[case] wire: Value) {
|
||||
assert!(serde_json::from_value::<ResponsesOutputItem>(wire).is_err());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue