Forge documentation
Library referenceRust

forge-provider-openai

OpenAI provider for the Forge SDK — implements the LanguageModel trait for OpenAI ChatCompletion API

OpenAI provider for the Forge SDK — implements the LanguageModel trait for OpenAI ChatCompletion API

Package contract

FieldValue
Languagerust
Source version0.2.0
Manifestforge-rs/crates/forge-provider-openai/Cargo.toml
Source files7
EvidenceSource reference; registry publication and runtime conformance are separate checks

Import boundary

use forge_provider_openai;

Use a source checkout or your verified private registry. Manifest coordinates identify the package; they do not establish that a public registry release exists.

Crate boundary

The following entries are taken from src/lib.rs. Feature conditions in the exact source still apply.

#[cfg(not(target_arch = "wasm32"))]
pub mod client;

#[cfg(not(target_arch = "wasm32"))]
pub mod config;

pub mod error;

#[cfg(not(target_arch = "wasm32"))]
pub mod model;

#[cfg(not(target_arch = "wasm32"))]
pub mod sse;

pub mod types;

#[cfg(not(target_arch = "wasm32"))]
pub use config::{OpenAiConfig, OpenAiConfigBuilder};

pub use error::OpenAiError;

#[cfg(not(target_arch = "wasm32"))]
pub use model::OpenAiLanguageModel;

Source reference

Download package reference JSON. Each original source file and generated declaration artifact has its own SHA-256 digest. Function bodies and constant values are omitted from downloads. These are source declaration inventories, not compiler-resolved rustdoc, TypeDoc, DocC, or Dokka output. Private modules can contain public declarations that are not reachable through the package boundary; consult the entry point before importing.

config.rs

Read declaration text · 12 declaration entries

#[derive(Clone)]
pub struct OpenAiConfig {

}

pub fn builder(api_key: impl Into<String>) -> OpenAiConfigBuilder;

pub fn base_url(&self) -> &str;

pub fn organization(&self) -> Option<&str>;

pub fn timeout_seconds(&self) -> u64;

pub fn max_retries(&self) -> u32;

pub struct OpenAiConfigBuilder {

}

pub fn base_url(mut self, url: impl Into<String>) -> Self;

pub fn organization(mut self, org: impl Into<String>) -> Self;

pub fn timeout_seconds(mut self, seconds: u64) -> Self;

pub fn max_retries(mut self, retries: u32) -> Self;

pub fn build(self) -> OpenAiConfig;

error.rs

Read declaration text · 1 declaration entries

#[derive(Debug, Error)]
pub enum OpenAiError {
    /// The OpenAI API returned an HTTP error response.
    ///
    /// Common status codes:
    /// - 400: malformed request (check message format)
    /// - 401: invalid API key
    /// - 403: insufficient permissions
    /// - 404: model not found
    /// - 429: rate limited
    /// - 500+: server error (retryable)
    #[error(
        "OpenAI API returned HTTP {status}: {body} (check request format and API key permissions)"
    )]
    HttpError {
        /// The HTTP status code.
        status: u16,
        /// The response body (may contain OpenAI error details).
        body: String,
    },

    /// Failed to connect to the OpenAI API endpoint.
    ///
    /// Check network connectivity, firewall rules, and the configured base URL.
    #[error("failed to connect to OpenAI API at '{url}': {reason} (check network connectivity and base_url configuration)")]
    ConnectionFailed {
        /// The URL that was being connected to.
        url: String,
        /// The underlying connection error.
        reason: String,
    },

    /// The request timed out waiting for a response.
    ///
    /// Consider increasing `timeout_seconds` in `OpenAiConfig` or reducing
    /// `max_tokens` to speed up generation.
    #[error("OpenAI API request to '{url}' timed out after {timeout_seconds}s (consider increasing timeout_seconds in OpenAiConfig or reducing max_tokens)")]
    Timeout {
        /// The URL that timed out.
        url: String,
        /// The configured timeout in seconds.
        timeout_seconds: u64,
    },

    /// The API response could not be parsed.
    ///
    /// This typically indicates an API version mismatch or an unexpected
    /// response format. Check the OpenAI API changelog for breaking changes.
    #[error("invalid response from OpenAI API: {reason} (this may indicate an API version mismatch; check the OpenAI API changelog)")]
    InvalidResponse {
        /// Description of what was wrong with the response.
        reason: String,
    },

    /// The API returned a rate limit error (HTTP 429).
    ///
    /// The provider will automatically retry with exponential backoff. If this
    /// error surfaces, all retries have been exhausted.
    #[error("OpenAI API rate limited; all retries exhausted{}", match .retry_after_ms {
        Some(ms) => format!(" (server suggested retry after {ms}ms)"),
        None => String::new(),
    })]
    RateLimited {
        /// Milliseconds to wait before retrying, if provided by the server.
        retry_after_ms: Option<u64>,
    },

    /// Authentication failed (HTTP 401).
    ///
    /// The API key is invalid, expired, or missing.
    #[error("OpenAI API authentication failed: {hint}")]
    AuthenticationFailed {
        /// Actionable hint for resolving the auth issue.
        hint: String,
    },

    /// An error occurred while parsing the SSE stream.
    ///
    /// This may indicate a network interruption during streaming or an
    /// unexpected stream format.
    #[error("OpenAI streaming error: {reason}")]
    StreamError {
        /// Description of the stream parsing failure.
        reason: String,
    },

    /// JSON serialization or deserialization failed.
    ///
    /// This typically means a request or response struct is malformed.
    #[error("JSON serialization error: {0}")]
    SerializationError(#[from] serde_json::Error),

    /// The request could not be constructed.
    ///
    /// This indicates an internal error in request building.
    #[error("failed to build HTTP request: {reason}")]
    RequestBuildError {
        /// Description of the request construction failure.
        reason: String,
    },
}

lib.rs

Read declaration text · 9 declaration entries

#[cfg(not(target_arch = "wasm32"))]
pub mod client;

#[cfg(not(target_arch = "wasm32"))]
pub mod config;

pub mod error;

#[cfg(not(target_arch = "wasm32"))]
pub mod model;

#[cfg(not(target_arch = "wasm32"))]
pub mod sse;

pub mod types;

#[cfg(not(target_arch = "wasm32"))]
pub use config::{OpenAiConfig, OpenAiConfigBuilder};

pub use error::OpenAiError;

#[cfg(not(target_arch = "wasm32"))]
pub use model::OpenAiLanguageModel;

model.rs

Read declaration text · 2 declaration entries

pub struct OpenAiLanguageModel {

}

pub fn new(model_id: String, config: OpenAiConfig) -> ForgeResult<Self>;

sse.rs

Read declaration text · 8 declaration entries

pub fn parse_sse_response(body: &str) -> Result<Vec<ChatCompletionChunk>, OpenAiError>;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecoderState {
    /// Stream is ongoing; more data can be fed.
    Continue,
    /// Terminal `[DONE]` sentinel observed; further bytes will be ignored.
    Done,
}

pub struct OpenAiSseDecoder {

}

pub fn new() -> Self;

pub fn feed(&mut self, bytes: &[u8]) -> Result<DecoderState, OpenAiError>;

pub fn drain(&mut self) -> Vec<ChatCompletionChunk>;

pub fn is_done(&self) -> bool;

pub fn finish(&mut self) -> Result<Vec<ChatCompletionChunk>, OpenAiError>;

types.rs

Read declaration text · 22 declaration entries

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionRequest {
/// The model ID (e.g., "gpt-4o").

pub model: String,
/// The conversation messages.

pub messages: Vec<ChatMessage>,
/// Tool definitions available to the model.

#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<ApiToolDefinition>>,
/// Sampling temperature (0.0 to 2.0).

#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f64>,
/// Maximum tokens to generate.

#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
/// Top-p (nucleus) sampling threshold.

#[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f64>,
/// Up to 4 stop sequences.

#[serde(skip_serializing_if = "Option::is_none")]
pub stop: Option<Vec<String>>,
/// Frequency penalty (-2.0 to 2.0).

#[serde(skip_serializing_if = "Option::is_none")]
pub frequency_penalty: Option<f64>,
/// Presence penalty (-2.0 to 2.0).

#[serde(skip_serializing_if = "Option::is_none")]
pub presence_penalty: Option<f64>,
/// Seed for deterministic generation.

#[serde(skip_serializing_if = "Option::is_none")]
pub seed: Option<u64>,
/// Response format for structured output.

#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<ResponseFormat>,
/// Whether to stream the response.

#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
/// Stream options (e.g., include usage in stream).

#[serde(skip_serializing_if = "Option::is_none")]
pub stream_options: Option<StreamOptions>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamOptions {
/// Whether to include usage statistics in the final stream chunk.

#[serde(skip_serializing_if = "Option::is_none")]
pub include_usage: Option<bool>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessage {
/// The role: "system", "user", "assistant", or "tool".

pub role: String,
/// The message content (text or multi-part).

#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<ChatContent>,
/// Tool calls made by the assistant.

#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ApiToolCall>>,
/// The tool call ID this message responds to (for tool role).

#[serde(skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
/// The tool name (for tool role messages).

#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ChatContent {
    /// Plain text content.
    Text(String),
    /// Array of content parts (text and/or images).
    Parts(Vec<ContentPart>),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentPart {
    /// A text content part.
    Text {
        /// The text content.
        text: String,
    },
    /// An image URL content part.
    ImageUrl {
        /// The image URL object.
        image_url: ImageUrlObject,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageUrlObject {
/// The image URL. For base64, use `data:<media_type>;base64,<data>`.

pub url: String
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiToolCall {
/// Unique identifier for this tool call.

pub id: String,
/// The type of tool call — always "function" for now.

#[serde(rename = "type")]
pub call_type: String,
/// The function being called.

pub function: FunctionCall
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionCall {
/// The function name.

pub name: String,
/// The function arguments as a JSON string.

pub arguments: String
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiToolDefinition {
/// The type — always "function".

#[serde(rename = "type")]
pub tool_type: String,
/// The function definition.

pub function: FunctionDef
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionDef {
/// The function name.

pub name: String,
/// A human-readable description of what the function does.

#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// JSON Schema for the function's parameters.

#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<serde_json::Value>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseFormat {
/// The format type: "text", "json_object", or "json_schema".

#[serde(rename = "type")]
pub format_type: String,
/// JSON schema definition (only for `json_schema` type).

#[serde(skip_serializing_if = "Option::is_none")]
pub json_schema: Option<JsonSchemaFormat>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonSchemaFormat {
/// Name for the schema (required by OpenAI).

pub name: String,
/// The JSON Schema object.

pub schema: serde_json::Value,
/// Whether to enforce strict schema adherence.

#[serde(skip_serializing_if = "Option::is_none")]
pub strict: Option<bool>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionResponse {
/// Unique response identifier.

pub id: String,
/// The list of completion choices.

pub choices: Vec<ChatCompletionChoice>,
/// Token usage statistics.

#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<ApiUsage>,
/// The model that generated the response.

pub model: String
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionChoice {
/// The choice index.

pub index: u32,
/// The generated message.

pub message: ChatMessage,
/// Why generation stopped.

pub finish_reason: Option<String>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiUsage {
/// Tokens consumed by the prompt.

pub prompt_tokens: u64,
/// Tokens generated in the completion.

pub completion_tokens: u64,
/// Total tokens (prompt + completion).

pub total_tokens: u64
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatCompletionChunk {
/// Unique response identifier (same across all chunks).

pub id: String,
/// The list of chunk choices.

pub choices: Vec<ChunkChoice>,
/// Usage statistics (only present in the final chunk when `stream_options.include_usage` is true).

#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<ApiUsage>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChunkChoice {
/// The choice index.

pub index: u32,
/// The content delta for this chunk.

pub delta: ChunkDelta,
/// Why generation stopped (present in the final chunk).

pub finish_reason: Option<String>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChunkDelta {
/// The role (typically only in the first chunk).

#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
/// Text content delta.

#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
/// Tool call deltas.

#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ChunkToolCall>>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChunkToolCall {
/// The tool call index (for correlating deltas).

pub index: u32,
/// The tool call ID (may only be present in the first delta).

#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
/// The tool call type (may only be present in the first delta).

#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
pub call_type: Option<String>,
/// The function call delta.

#[serde(skip_serializing_if = "Option::is_none")]
pub function: Option<ChunkFunctionCall>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChunkFunctionCall {
/// The function name (may only be present in the first delta).

#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
/// Partial function arguments (streamed incrementally).

#[serde(skip_serializing_if = "Option::is_none")]
pub arguments: Option<String>
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiErrorResponse {
/// The error detail object.

pub error: ApiErrorDetail
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiErrorDetail {
/// The error message.

pub message: String,
/// The error type (e.g., "invalid_request_error").

#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
pub error_type: Option<String>,
/// The parameter that caused the error.

#[serde(skip_serializing_if = "Option::is_none")]
pub param: Option<String>,
/// The error code (e.g., "model_not_found").

#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>
}

Continue

On this page