aimux-core 0.2.0

Core abstractions for aimux: LanguageModel trait, message types, and user-facing API
Documentation
//! Result types for `do_generate` and `do_stream`.

use std::collections::HashMap;

use futures::Stream;
use serde::{Deserialize, Serialize};
use std::pin::Pin;
use ts_rs::TS;

use crate::error::AiMuxError;
use crate::shared::FileData;
use crate::stream_part::StreamPart;
use crate::types::{FinishReason, ProviderMetadata, ResponseMetadata, Usage, Warning};

use serde_json::Value;

/// A content item in the generation result.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TS)]
#[ts(export)]
pub enum GenerateContent {
    /// Generated text.
    Text {
        text: String,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        provider_metadata: Option<ProviderMetadata>,
    },
    /// A tool call requested by the model.
    ToolCall {
        tool_call_id: String,
        tool_name: String,
        input: serde_json::Value,
        /// Whether the tool call will be executed by the provider.
        /// If false/unset, the tool call is executed by the client.
        #[serde(default, skip_serializing_if = "Option::is_none")]
        provider_executed: Option<bool>,
        /// Whether the tool is dynamic (defined at runtime, e.g. MCP tools).
        #[serde(default, skip_serializing_if = "Option::is_none")]
        dynamic: Option<bool>,
        /// Provider-assigned thought signature (e.g. Google Gemini
        /// `thoughtSignature`). Must be echoed back verbatim on the follow-up
        /// turn when the tool result is sent.
        #[serde(default, skip_serializing_if = "Option::is_none")]
        thought_signature: Option<String>,
        /// Additional provider-specific metadata for the tool call.
        #[serde(default, skip_serializing_if = "Option::is_none")]
        provider_metadata: Option<ProviderMetadata>,
    },
    /// A source / citation (e.g. URL citation from search-preview models).
    Source {
        id: String,
        source_type: String,
        url: Option<String>,
        title: Option<String>,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        provider_metadata: Option<ProviderMetadata>,
    },
    /// A reasoning / thinking segment produced by the model.
    ///
    /// Mirrors the Vercel AI SDK `reasoning` content type. `provider_metadata`
    /// carries provider-specific data such as the Anthropic thinking-block
    /// signature (`providerMetadata.anthropic.signature`).
    Reasoning {
        text: String,
        provider_metadata: Option<ProviderMetadata>,
    },
    /// A file generated by the model (e.g. an image or document).
    ///
    /// Mirrors the Vercel AI SDK `file` content type. `data` is a tagged union
    /// covering base64 data, URL, or provider reference.
    File {
        data: FileData,
        media_type: String,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        provider_metadata: Option<ProviderMetadata>,
    },
    /// A tool result from a provider-executed tool (e.g. xAI file_search,
    /// web_search). Emitted alongside the preceding `ToolCall` when the
    /// provider executes the tool server-side.
    ToolResult {
        tool_call_id: String,
        tool_name: String,
        result: Value,
        /// Whether the result is an error or error message.
        #[serde(default, skip_serializing_if = "Option::is_none")]
        is_error: Option<bool>,
        /// Whether the result is preliminary (replaces prior, e.g. image previews).
        #[serde(default, skip_serializing_if = "Option::is_none")]
        preliminary: Option<bool>,
        /// Whether the tool is dynamic (defined at runtime, e.g. MCP tools).
        #[serde(default, skip_serializing_if = "Option::is_none")]
        dynamic: Option<bool>,
        /// Additional provider-specific metadata for the tool result.
        #[serde(default, skip_serializing_if = "Option::is_none")]
        provider_metadata: Option<ProviderMetadata>,
    },
}

/// Result of `LanguageModel::do_generate` (non-streaming).
#[derive(Debug, Serialize, Deserialize, TS)]
#[ts(export)]
pub struct GenerateResult {
    /// Generated content items (text, tool calls, etc.).
    pub content: Vec<GenerateContent>,
    /// Why generation stopped.
    pub finish_reason: FinishReason,
    /// Token usage.
    pub usage: Usage,
    /// Warnings issued by the provider.
    pub warnings: Vec<Warning>,
    /// Provider-specific metadata.
    pub provider_metadata: Option<ProviderMetadata>,
    /// Response metadata (id, timestamp, model_id).
    pub response: ResponseMetadata,
    /// The request body that was sent (for debugging).
    pub request_body: Option<serde_json::Value>,
    /// Response headers.
    pub response_headers: Option<HashMap<String, String>>,
}

/// Result of `LanguageModel::do_stream` (streaming).
pub struct StreamResult {
    /// The stream of `StreamPart` items.
    pub stream: Pin<Box<dyn Stream<Item = Result<StreamPart, AiMuxError>> + Send>>,
    /// The request body that was sent (for debugging).
    pub request_body: Option<serde_json::Value>,
    /// Response headers.
    pub response_headers: Option<HashMap<String, String>>,
}

impl std::fmt::Debug for StreamResult {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("StreamResult")
            .field("request_body", &self.request_body)
            .field("response_headers", &self.response_headers)
            .field("stream", &"<stream>")
            .finish()
    }
}