mod audio;
mod batches;
mod beta;
mod chat;
mod common;
mod containers;
mod conversations;
mod core;
mod evals;
mod files;
mod fine_tuning;
mod images;
mod longtail;
mod responses;
mod skills;
mod uploads;
mod vector_stores;
mod videos;
mod webhooks;
use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::Client;
use crate::error::{Error, Result};
#[cfg(feature = "tool-runner")]
use crate::helpers::ToolDefinition;
use crate::json_payload::JsonPayload;
pub use beta::{
BetaAssistant, BetaAssistantTool, BetaRealtimeSession, BetaRealtimeTranscriptionSession,
BetaThread, BetaThreadMessage, BetaThreadMessageContent, BetaThreadRun,
BetaThreadRunIncompleteDetails, BetaThreadRunLastError, BetaThreadRunRequiredAction,
BetaThreadRunRequiredActionFunction, BetaThreadRunRequiredActionFunctionToolCall,
BetaThreadRunRequiredActionSubmitToolOutputs, BetaThreadRunStep, BetaThreadRunStepDetails,
BetaThreadRunTool, BetaThreadRunUsage, BetaThreadToolResources, ChatKitConfiguration,
ChatKitRateLimits, ChatKitSession, ChatKitThread, ChatKitThreadContent, ChatKitThreadItem,
ChatKitThreadStatus, ChatKitWorkflow,
};
#[cfg(feature = "structured-output")]
pub use chat::ChatCompletionParseRequestBuilder;
pub use chat::{
AssistantStreamRequestBuilder, ChatCompletionCreateRequestBuilder, ChatCompletionStoreMessage,
ChatCompletionStreamRequestBuilder,
};
#[cfg(feature = "tool-runner")]
pub use chat::{
ChatCompletionRunToolsRequestBuilder, ChatCompletionRunner, ChatCompletionStreamingRunner,
ChatCompletionToolResult,
};
pub use common::{
BytesRequestBuilder, JsonRequestBuilder, ListRequestBuilder, NoContentRequestBuilder,
};
pub(crate) use common::{
TypedJsonRequestState, encode_path_segment, metadata_is_empty, value_from,
};
pub use core::{
Completion, CompletionChoice, CompletionLogProbs, CompletionUsage,
CompletionUsageCompletionTokensDetails, CompletionUsagePromptTokensDetails,
ModerationCreateResponse, ModerationResult,
};
pub use fine_tuning::{
GraderModel, GraderModelCatalog, GraderRunErrors, GraderRunMetadata, GraderRunResponse,
GraderValidateResponse,
};
pub use longtail::{
AudioSpeechCreateParams, AudioSpeechRequestBuilder, AudioTranscription,
AudioTranscriptionRequestBuilder, AudioTranscriptionSegment, AudioTranscriptionSegmentId,
AudioTranscriptionWord, AudioTranslation, AudioTranslationRequestBuilder, Batch,
BatchCreateParams, BatchCreateRequestBuilder, BatchError, BatchErrors, BatchRequestCounts,
BatchUsage, BatchUsageInputTokensDetails, BatchUsageOutputTokensDetails, Container,
ContainerCreateParams, ContainerExpiresAfter, ContainerFile, ContainerFileCreateParams,
Conversation, ConversationContentPart, ConversationCreateParams, ConversationInputItem,
ConversationItem, ConversationItemCreateParams, ConversationUpdateParams, Eval,
EvalCreateParams, EvalDataSourceConfig, EvalOutput, EvalOutputItem, EvalRun,
EvalRunCreateParams, EvalRunInput, EvalTestingCriterion, EvalUpdateParams,
FineTuningCheckpoint, FineTuningCheckpointPermission, FineTuningHyperparameterValue,
FineTuningJob, FineTuningJobCreateParams, FineTuningJobCreateRequestBuilder,
FineTuningJobError, FineTuningJobEvent, FineTuningJobHyperparameters, FineTuningJobIntegration,
FineTuningMetrics, FineTuningWandbIntegration, ImageData, ImageGenerateParams,
ImageGenerateRequestBuilder, ImageGenerationResponse, Skill, SkillCreateParams,
SkillUpdateParams, SkillVersion, SkillVersionContent, SkillVersionCreateParams, Video,
VideoCharacter, VideoCharacterCreateParams, VideoCreateParams,
};
#[cfg(feature = "realtime")]
pub use responses::RealtimeSocketRequestBuilder;
#[cfg(feature = "structured-output")]
pub use responses::ResponseParseRequestBuilder;
#[cfg(feature = "responses-ws")]
pub use responses::ResponsesSocketRequestBuilder;
pub use responses::{
RealtimeClientSecretCreateResponse, RealtimeSessionClientSecret, ResponseCreateRequestBuilder,
ResponseStreamRequestBuilder,
};
pub use uploads::UploadPart;
pub use vector_stores::{
VectorStore, VectorStoreAttributeValue, VectorStoreAttributes, VectorStoreExpiresAfter,
VectorStoreFile, VectorStoreFileBatch, VectorStoreFileChunkingStrategy, VectorStoreFileContent,
VectorStoreFileCounts, VectorStoreFileLastError, VectorStoreMetadata, VectorStoreSearchContent,
VectorStoreSearchResponse, VectorStoreSearchResult, VectorStoreStaticFileChunkingStrategy,
};
macro_rules! json_payload_wrapper {
($(#[$meta:meta])* $name:ident) => {
$(#[$meta])*
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct $name(Value);
impl Default for $name {
fn default() -> Self {
Self(Value::Null)
}
}
impl From<Value> for $name {
fn from(value: Value) -> Self {
Self(value)
}
}
impl From<$name> for Value {
fn from(value: $name) -> Self {
value.0
}
}
impl $name {
pub fn as_raw(&self) -> &Value {
&self.0
}
pub fn into_raw(self) -> Value {
self.0
}
pub fn kind(&self) -> Option<&str> {
self.0.get("type").and_then(Value::as_str)
}
}
};
}
macro_rules! handle {
($(#[$meta:meta])* $name:ident) => {
$(#[$meta])*
#[derive(Debug, Clone)]
pub struct $name {
client: Client,
}
impl $name {
pub(crate) fn new(client: Client) -> Self {
Self { client }
}
}
};
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DeleteResponse {
pub id: Option<String>,
#[serde(default)]
pub deleted: bool,
pub object: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Model {
pub id: String,
#[serde(default)]
pub object: String,
pub owned_by: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FileObject {
pub id: String,
#[serde(default)]
pub object: String,
pub filename: Option<String>,
pub purpose: Option<String>,
pub bytes: Option<u64>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct UploadObject {
pub id: String,
#[serde(default)]
pub object: String,
pub status: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmbeddingResponse {
#[serde(default)]
pub object: String,
#[serde(default)]
pub data: Vec<EmbeddingData>,
pub usage: Option<EmbeddingUsage>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmbeddingData {
#[serde(default)]
pub embedding: Vec<f64>,
pub index: Option<u32>,
#[serde(default)]
pub object: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmbeddingUsage {
#[serde(default)]
pub prompt_tokens: u64,
#[serde(default)]
pub total_tokens: u64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct InputTokenCount {
pub total_tokens: u64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
json_payload_wrapper!(
ChatCompletionStoreContentPart
);
json_payload_wrapper!(
ChatReasoningDetail
);
json_payload_wrapper!(
ChatToolChoice
);
json_payload_wrapper!(
ResponseInputPayload
);
json_payload_wrapper!(
ResponseInputItemPayload
);
json_payload_wrapper!(
RealtimeSessionPayload
);
json_payload_wrapper!(
ResponseOutputItemRaw
);
json_payload_wrapper!(
ResponseOutputContentPartRaw
);
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ChatToolChoiceMode {
None,
Auto,
Required,
}
impl From<ChatToolChoiceMode> for ChatToolChoice {
fn from(mode: ChatToolChoiceMode) -> Self {
let value = match mode {
ChatToolChoiceMode::None => Value::String("none".into()),
ChatToolChoiceMode::Auto => Value::String("auto".into()),
ChatToolChoiceMode::Required => Value::String("required".into()),
};
Self::from(value)
}
}
impl From<String> for ResponseInputPayload {
fn from(value: String) -> Self {
Self::from(Value::String(value))
}
}
impl From<&str> for ResponseInputPayload {
fn from(value: &str) -> Self {
Self::from(Value::String(value.into()))
}
}
impl From<Vec<ResponseInputItemPayload>> for ResponseInputPayload {
fn from(items: Vec<ResponseInputItemPayload>) -> Self {
Self::from(Value::Array(items.into_iter().map(Value::from).collect()))
}
}
impl ChatToolChoice {
pub fn none() -> Self {
ChatToolChoiceMode::None.into()
}
pub fn auto() -> Self {
ChatToolChoiceMode::Auto.into()
}
pub fn required() -> Self {
ChatToolChoiceMode::Required.into()
}
pub fn function(name: impl Into<String>) -> Self {
serde_json::json!({
"type": "function",
"function": {
"name": name.into(),
},
})
.into()
}
pub fn custom(name: impl Into<String>) -> Self {
serde_json::json!({
"type": "custom",
"custom": {
"name": name.into(),
},
})
.into()
}
pub fn mode_name(&self) -> Option<&str> {
self.0.as_str()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionFunctionCall {
pub name: String,
#[serde(default)]
pub arguments: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionToolCall {
pub id: String,
#[serde(rename = "type", default = "default_function_type")]
pub call_type: String,
pub function: ChatCompletionFunctionCall,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct ChatCompletionTokenTopLogprob {
#[serde(default)]
pub token: String,
pub bytes: Option<Vec<u8>>,
#[serde(default)]
pub logprob: f64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct ChatCompletionTokenLogprob {
#[serde(default)]
pub token: String,
pub bytes: Option<Vec<u8>>,
#[serde(default)]
pub logprob: f64,
#[serde(default)]
pub top_logprobs: Vec<ChatCompletionTokenTopLogprob>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct ChatCompletionChoiceLogprobs {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub content: Vec<ChatCompletionTokenLogprob>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub refusal: Vec<ChatCompletionTokenLogprob>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
impl ChatCompletionChoiceLogprobs {
pub fn values(&self, field_name: &str) -> Option<&[ChatCompletionTokenLogprob]> {
match field_name {
"content" if !self.content.is_empty() => Some(&self.content),
"refusal" if !self.refusal.is_empty() => Some(&self.refusal),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionFunctionCallDelta {
pub name: Option<String>,
pub arguments: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionToolCallDelta {
pub index: Option<u32>,
pub id: Option<String>,
#[serde(rename = "type")]
pub call_type: Option<String>,
pub function: Option<ChatCompletionFunctionCallDelta>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionMessage {
pub role: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tool_calls: Vec<ChatCompletionToolCall>,
#[serde(skip_serializing_if = "Option::is_none")]
pub refusal: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning_content: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub reasoning_details: Vec<ChatReasoningDetail>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
impl ChatCompletionMessage {
pub fn system(content: impl Into<String>) -> Self {
Self {
role: "system".into(),
content: Some(content.into()),
..Self::default()
}
}
pub fn user(content: impl Into<String>) -> Self {
Self {
role: "user".into(),
content: Some(content.into()),
..Self::default()
}
}
pub fn assistant(content: impl Into<String>) -> Self {
Self {
role: "assistant".into(),
content: Some(content.into()),
..Self::default()
}
}
pub fn tool(tool_call_id: impl Into<String>, content: impl Into<String>) -> Self {
Self {
role: "tool".into(),
content: Some(content.into()),
tool_call_id: Some(tool_call_id.into()),
..Self::default()
}
}
pub fn parse_content<T>(&self) -> Result<Option<T>>
where
T: serde::de::DeserializeOwned,
{
self.content
.as_deref()
.map(parse_jsonish_payload)
.transpose()
}
pub fn parse_tool_arguments<T>(&self) -> Result<Option<T>>
where
T: serde::de::DeserializeOwned,
{
self.tool_calls
.first()
.map(|tool_call| parse_json_arguments(&tool_call.function.arguments))
.transpose()
}
pub fn parse_tool_arguments_by_id<T>(&self, tool_call_id: &str) -> Result<Option<T>>
where
T: serde::de::DeserializeOwned,
{
self.tool_calls
.iter()
.find(|tool_call| tool_call.id == tool_call_id)
.map(|tool_call| parse_json_arguments(&tool_call.function.arguments))
.transpose()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionChoice {
pub index: u32,
pub finish_reason: Option<String>,
pub message: ChatCompletionMessage,
pub logprobs: Option<ChatCompletionChoiceLogprobs>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletion {
pub id: String,
#[serde(default)]
pub object: String,
pub created: Option<i64>,
#[serde(default)]
pub model: String,
#[serde(default)]
pub choices: Vec<ChatCompletionChoice>,
pub usage: Option<CompletionUsage>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
impl ChatCompletion {
pub fn ensure_not_truncated(&self) -> Result<&Self> {
for choice in &self.choices {
match choice.finish_reason.as_deref() {
Some("length") => return Err(crate::LengthFinishReasonError.into()),
Some("content_filter") => return Err(crate::ContentFilterFinishReasonError.into()),
_ => {}
}
}
Ok(self)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionChunkDelta {
pub role: Option<String>,
pub content: Option<String>,
pub refusal: Option<String>,
pub reasoning_content: Option<String>,
#[serde(default)]
pub reasoning_details: Vec<ChatReasoningDetail>,
#[serde(default)]
pub tool_calls: Vec<ChatCompletionToolCallDelta>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionChunkChoice {
pub index: u32,
pub delta: ChatCompletionChunkDelta,
pub finish_reason: Option<String>,
pub logprobs: Option<ChatCompletionChoiceLogprobs>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ChatContentDeltaEvent {
pub choice_index: u32,
pub delta: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ChatRefusalDeltaEvent {
pub choice_index: u32,
pub delta: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ChatToolArgumentsDeltaEvent {
pub choice_index: u32,
pub tool_call_index: u32,
pub name: Option<String>,
pub delta: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ChatLogProbsDeltaEvent {
pub choice_index: u32,
pub values: Vec<ChatCompletionTokenLogprob>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionChunk {
pub id: String,
#[serde(default)]
pub object: String,
pub created: Option<i64>,
#[serde(default)]
pub model: String,
#[serde(default)]
pub choices: Vec<ChatCompletionChunkChoice>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
impl ChatCompletionChunk {
pub fn content_deltas(&self) -> Vec<ChatContentDeltaEvent> {
self.choices
.iter()
.filter_map(|choice| {
choice
.delta
.content
.as_ref()
.map(|delta| ChatContentDeltaEvent {
choice_index: choice.index,
delta: delta.clone(),
})
})
.collect()
}
pub fn refusal_deltas(&self) -> Vec<ChatRefusalDeltaEvent> {
self.choices
.iter()
.filter_map(|choice| {
choice
.delta
.refusal
.as_ref()
.map(|delta| ChatRefusalDeltaEvent {
choice_index: choice.index,
delta: delta.clone(),
})
})
.collect()
}
pub fn tool_argument_deltas(&self) -> Vec<ChatToolArgumentsDeltaEvent> {
self.choices
.iter()
.flat_map(|choice| {
choice.delta.tool_calls.iter().filter_map(|tool_call| {
let delta = tool_call.function.as_ref()?.arguments.clone()?;
Some(ChatToolArgumentsDeltaEvent {
choice_index: choice.index,
tool_call_index: tool_call.index.unwrap_or_default(),
name: tool_call
.function
.as_ref()
.and_then(|function| function.name.clone()),
delta,
})
})
})
.collect()
}
pub fn logprobs_content_deltas(&self) -> Vec<ChatLogProbsDeltaEvent> {
extract_logprobs(self, "content")
}
pub fn logprobs_refusal_deltas(&self) -> Vec<ChatLogProbsDeltaEvent> {
extract_logprobs(self, "refusal")
}
}
fn extract_logprobs(chunk: &ChatCompletionChunk, field_name: &str) -> Vec<ChatLogProbsDeltaEvent> {
chunk
.choices
.iter()
.filter_map(|choice| {
let values = choice
.logprobs
.as_ref()
.and_then(|logprobs| logprobs.values(field_name))?
.to_vec();
Some(ChatLogProbsDeltaEvent {
choice_index: choice.index,
values,
})
})
.collect()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatToolDefinition {
#[serde(rename = "type")]
pub tool_type: String,
pub function: ChatToolFunction,
}
impl ChatToolDefinition {
#[cfg(feature = "tool-runner")]
fn from_tool(tool: &ToolDefinition) -> Self {
Self {
tool_type: "function".into(),
function: ChatToolFunction {
name: tool.name.clone(),
description: tool.description.clone(),
parameters: tool.parameters.clone(),
},
}
}
fn as_response_tool_value(&self) -> Value {
serde_json::json!({
"type": self.tool_type,
"name": self.function.name,
"description": self.function.description,
"parameters": self.function.parameters,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatToolFunction {
pub name: String,
pub description: Option<String>,
pub parameters: JsonPayload,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ChatCompletionCreateParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub messages: Vec<ChatCompletionMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub n: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tools: Vec<ChatToolDefinition>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<ChatToolChoice>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Response {
pub id: String,
pub created_at: Option<u64>,
#[serde(default)]
pub object: String,
pub model: Option<String>,
pub status: Option<String>,
pub error: Option<ResponseError>,
pub incomplete_details: Option<ResponseIncompleteDetails>,
pub metadata: Option<BTreeMap<String, String>>,
#[serde(default)]
pub output: Vec<ResponseOutputItem>,
pub usage: Option<ResponseUsage>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
impl Response {
pub fn output_text(&self) -> Option<String> {
for item in &self.output {
if let Some(text) = item.output_text() {
return Some(text.to_owned());
}
}
self.extra
.get("output_text")
.and_then(Value::as_str)
.map(str::to_owned)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseError {
pub code: Option<String>,
pub message: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseIncompleteDetails {
pub reason: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseUsage {
#[serde(default)]
pub input_tokens: u64,
pub input_tokens_details: Option<ResponseInputTokensDetails>,
#[serde(default)]
pub output_tokens: u64,
pub output_tokens_details: Option<ResponseOutputTokensDetails>,
#[serde(default)]
pub total_tokens: u64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseInputTokensDetails {
pub cached_tokens: Option<u64>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseOutputTokensDetails {
pub reasoning_tokens: Option<u64>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseOutputItem {
Known(KnownResponseOutputItem),
Raw(ResponseOutputItemRaw),
}
impl Default for ResponseOutputItem {
fn default() -> Self {
Self::Raw(ResponseOutputItemRaw::default())
}
}
impl ResponseOutputItem {
pub fn as_message(&self) -> Option<&ResponseOutputMessage> {
match self {
Self::Known(KnownResponseOutputItem::Message(message)) => Some(message),
_ => None,
}
}
pub fn as_function_call(&self) -> Option<&ResponseFunctionToolCall> {
match self {
Self::Known(KnownResponseOutputItem::FunctionCall(call)) => Some(call),
_ => None,
}
}
pub fn as_raw(&self) -> Option<&Value> {
match self {
Self::Raw(value) => Some(value.as_raw()),
_ => None,
}
}
pub fn output_text(&self) -> Option<&str> {
match self {
Self::Known(KnownResponseOutputItem::OutputText(text)) => Some(text.text.as_str()),
Self::Known(KnownResponseOutputItem::Message(message)) => message
.content
.iter()
.find_map(ResponseOutputContentPart::text),
Self::Raw(value) => {
let value = value.as_raw();
if let Some(text) = value.get("text").and_then(Value::as_str) {
return Some(text);
}
value
.get("content")
.and_then(Value::as_array)
.and_then(|content| {
content
.iter()
.find_map(|item| item.get("text").and_then(Value::as_str))
})
}
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum KnownResponseOutputItem {
Message(ResponseOutputMessage),
FunctionCall(ResponseFunctionToolCall),
OutputText(ResponseOutputText),
Refusal(ResponseOutputRefusal),
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseOutputMessage {
pub id: String,
#[serde(default)]
pub content: Vec<ResponseOutputContentPart>,
pub role: Option<String>,
pub status: Option<String>,
pub phase: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseFunctionToolCall {
pub id: String,
pub call_id: Option<String>,
pub name: Option<String>,
#[serde(default)]
pub arguments: String,
pub status: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseOutputContentPart {
Known(KnownResponseOutputContentPart),
Raw(ResponseOutputContentPartRaw),
}
impl Default for ResponseOutputContentPart {
fn default() -> Self {
Self::Raw(ResponseOutputContentPartRaw::default())
}
}
impl ResponseOutputContentPart {
pub fn as_output_text(&self) -> Option<&ResponseOutputText> {
match self {
Self::Known(KnownResponseOutputContentPart::OutputText(text)) => Some(text),
_ => None,
}
}
pub fn text(&self) -> Option<&str> {
match self {
Self::Known(KnownResponseOutputContentPart::OutputText(text)) => {
Some(text.text.as_str())
}
Self::Raw(value) => value.as_raw().get("text").and_then(Value::as_str),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum KnownResponseOutputContentPart {
OutputText(ResponseOutputText),
Refusal(ResponseOutputRefusal),
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ResponseOutputTextFileCitation {
pub file_id: String,
pub filename: String,
pub index: u64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ResponseOutputTextUrlCitation {
pub end_index: u64,
pub start_index: u64,
pub title: String,
pub url: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ResponseOutputTextContainerFileCitation {
pub container_id: String,
pub end_index: u64,
pub file_id: String,
pub filename: String,
pub start_index: u64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ResponseOutputTextFilePath {
pub file_id: String,
pub index: u64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum KnownResponseOutputTextAnnotation {
FileCitation(ResponseOutputTextFileCitation),
UrlCitation(ResponseOutputTextUrlCitation),
ContainerFileCitation(ResponseOutputTextContainerFileCitation),
FilePath(ResponseOutputTextFilePath),
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct ResponseOutputTextAnnotationUnknown {
#[serde(rename = "type")]
pub annotation_type: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum ResponseOutputTextAnnotation {
Known(KnownResponseOutputTextAnnotation),
Unknown(ResponseOutputTextAnnotationUnknown),
}
impl Default for ResponseOutputTextAnnotation {
fn default() -> Self {
Self::Unknown(ResponseOutputTextAnnotationUnknown::default())
}
}
impl ResponseOutputTextAnnotation {
pub fn kind(&self) -> Option<&str> {
match self {
Self::Known(KnownResponseOutputTextAnnotation::FileCitation(_)) => {
Some("file_citation")
}
Self::Known(KnownResponseOutputTextAnnotation::UrlCitation(_)) => Some("url_citation"),
Self::Known(KnownResponseOutputTextAnnotation::ContainerFileCitation(_)) => {
Some("container_file_citation")
}
Self::Known(KnownResponseOutputTextAnnotation::FilePath(_)) => Some("file_path"),
Self::Unknown(annotation) => annotation.annotation_type.as_deref(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct ResponseOutputTextTopLogprob {
#[serde(default)]
pub token: String,
#[serde(default)]
pub bytes: Vec<u8>,
#[serde(default)]
pub logprob: f64,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct ResponseOutputTextLogprob {
#[serde(default)]
pub token: String,
#[serde(default)]
pub bytes: Vec<u8>,
#[serde(default)]
pub logprob: f64,
#[serde(default)]
pub top_logprobs: Vec<ResponseOutputTextTopLogprob>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseOutputText {
#[serde(default)]
pub annotations: Vec<ResponseOutputTextAnnotation>,
#[serde(default)]
pub text: String,
pub logprobs: Option<Vec<ResponseOutputTextLogprob>>,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseOutputRefusal {
#[serde(default)]
pub refusal: String,
#[serde(flatten)]
pub extra: BTreeMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResponseCreateParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input: Option<ResponseInputPayload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tools: Vec<ChatToolDefinition>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
}
fn default_function_type() -> String {
"function".into()
}
fn parse_jsonish_payload<T>(payload: &str) -> Result<T>
where
T: serde::de::DeserializeOwned,
{
let trimmed = payload.trim();
let normalized = trimmed
.strip_prefix("```json")
.or_else(|| trimmed.strip_prefix("```"))
.map(|value| value.trim())
.and_then(|value| value.strip_suffix("```"))
.map_or(trimmed, str::trim);
serde_json::from_str(normalized).map_err(|error| {
Error::Serialization(crate::SerializationError::new(format!(
"结构化 JSON 解析失败: {error}"
)))
})
}
fn parse_json_arguments<T>(arguments: &str) -> Result<T>
where
T: serde::de::DeserializeOwned,
{
serde_json::from_str(arguments).map_err(|error| {
Error::Serialization(crate::SerializationError::new(format!(
"工具参数 JSON 解析失败: {error}"
)))
})
}
handle!(
CompletionsResource
);
handle!(
ChatResource
);
handle!(
ChatCompletionsResource
);
handle!(
ChatCompletionMessagesResource
);
handle!(
EmbeddingsResource
);
handle!(
FilesResource
);
handle!(
ImagesResource
);
handle!(
AudioResource
);
handle!(
AudioSpeechResource
);
handle!(
AudioTranscriptionsResource
);
handle!(
AudioTranslationsResource
);
handle!(
ModerationsResource
);
handle!(
ModelsResource
);
handle!(
FineTuningResource
);
handle!(
FineTuningJobsResource
);
handle!(
FineTuningJobCheckpointsResource
);
handle!(
FineTuningCheckpointPermissionsResource
);
handle!(
FineTuningAlphaResource
);
handle!(
FineTuningAlphaGradersResource
);
handle!(
GradersResource
);
handle!(
VectorStoresResource
);
handle!(
VectorStoreFilesResource
);
handle!(
VectorStoreFileBatchesResource
);
handle!(
BatchesResource
);
handle!(
UploadsResource
);
handle!(
UploadPartsResource
);
handle!(
ResponsesResource
);
handle!(
ResponseInputItemsResource
);
handle!(
ResponseInputTokensResource
);
handle!(
RealtimeResource
);
handle!(
RealtimeClientSecretsResource
);
handle!(
RealtimeCallsResource
);
handle!(
ConversationsResource
);
handle!(
ConversationItemsResource
);
handle!(
EvalsResource
);
handle!(
EvalRunsResource
);
handle!(
EvalRunOutputItemsResource
);
handle!(
ContainersResource
);
handle!(
ContainerFilesResource
);
handle!(
ContainerFilesContentResource
);
handle!(
SkillsResource
);
handle!(
SkillsContentResource
);
handle!(
SkillVersionsResource
);
handle!(
SkillVersionsContentResource
);
handle!(
VideosResource
);
handle!(
WebhooksResource
);
handle!(
BetaResource
);
handle!(
BetaAssistantsResource
);
handle!(
BetaThreadsResource
);
handle!(
BetaThreadMessagesResource
);
handle!(
BetaThreadRunsResource
);
handle!(
BetaThreadRunStepsResource
);
handle!(
BetaChatkitResource
);
handle!(
BetaChatkitSessionsResource
);
handle!(
BetaChatkitThreadsResource
);
handle!(
BetaRealtimeResource
);
handle!(
BetaRealtimeSessionsResource
);
handle!(
BetaRealtimeTranscriptionSessionsResource
);