openai

package
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 11, 2025 License: MIT Imports: 10 Imported by: 0

README

OpenAI Compatibility Package

This package provides types and utilities for building OpenAI-compatible APIs that use MCP tools. It bridges the gap between MCP's tool format and OpenAI's function calling format.

Installation

import "github.com/paularlott/mcp/openai"

Features

  • OpenAI Types: Complete request/response types for chat completions
  • Tool Conversion: Convert MCP tools to OpenAI function format
  • Response Extraction: Extract string results from MCP tool responses
  • Streaming Support: Iterator and accumulator for streaming responses
  • Tool Handlers: Event notifications during tool execution

Quick Start

Converting MCP Tools to OpenAI Format
import (
    "github.com/paularlott/mcp"
    "github.com/paularlott/mcp/openai"
)

// Create your MCP server
server := mcp.NewServer("my-server", "1.0.0")
server.RegisterTool(/* ... */)

// Convert MCP tools to OpenAI format
mcpTools := server.ListTools()
openAITools := openai.MCPToolsToOpenAI(mcpTools)

// Or filter tools by name
filteredTools := openai.MCPToolsToOpenAIFiltered(mcpTools, openai.ToolsByName("search", "calculate"))

// Or exclude certain tools
excludedTools := openai.MCPToolsToOpenAIFiltered(mcpTools, openai.ExcludeTools("dangerous_tool"))
Extracting Tool Results
// After executing an MCP tool
response, err := server.CallTool(ctx, toolName, args)
if err != nil {
    return err
}

// Extract the result as a string for OpenAI
result, err := openai.ExtractToolResult(response)
if err != nil {
    return err
}

// Create tool result message
toolResultMessage := openai.Message{
    Role:       "tool",
    ToolCallID: toolCall.ID,
}
toolResultMessage.SetContentAsString(result)
Streaming with ChatStream
// Assuming you have response and error channels from your streaming implementation
stream := openai.NewChatStream(ctx, responseChan, errorChan)

for stream.Next() {
    chunk := stream.Current()

    // Process each chunk
    for _, choice := range chunk.Choices {
        if choice.Delta.Content != "" {
            fmt.Print(choice.Delta.Content)
        }
    }
}

if err := stream.Err(); err != nil {
    log.Printf("Stream error: %v", err)
}
Accumulating Streaming Responses
acc := &openai.CompletionAccumulator{}

for stream.Next() {
    chunk := stream.Current()
    acc.AddChunk(chunk)
}

// Check what we got
if content, ok := acc.FinishedContent(); ok {
    fmt.Println("Content:", content)
}

if toolCalls, ok := acc.FinishedToolCalls(); ok {
    for _, tc := range toolCalls {
        fmt.Printf("Tool call: %s(%v)\n", tc.Function.Name, tc.Function.Arguments)
    }
}

if refusal, ok := acc.FinishedRefusal(); ok {
    fmt.Println("Refusal:", refusal)
}
Using Tool Handlers

Tool handlers receive events during tool execution, useful for sending SSE events or logging.

Important: The expected call order is:

  1. OnToolCall() - called BEFORE executing the tool
  2. Execute the tool
  3. OnToolResult() - called AFTER the tool completes
type MyToolHandler struct{}

func (h *MyToolHandler) OnToolCall(toolCall openai.ToolCall) error {
    log.Printf("Starting tool: %s", toolCall.Function.Name)
    return nil
}

func (h *MyToolHandler) OnToolResult(toolCallID, toolName, result string) error {
    log.Printf("Tool %s completed with result: %s", toolName, result)
    return nil
}

// Attach handler to context
ctx = openai.WithToolHandler(ctx, &MyToolHandler{})

// Later, retrieve and use it during tool processing
if handler := openai.ToolHandlerFromContext(ctx); handler != nil {
    // 1. Notify BEFORE execution
    handler.OnToolCall(toolCall)

    // 2. Execute the tool
    result, err := mcpServer.CallTool(ctx, toolCall.Function.Name, toolCall.Function.Arguments)

    // 3. Notify AFTER execution with result
    handler.OnToolResult(toolCall.ID, toolCall.Function.Name, result)
}

Types Reference

Request/Response Types
Type Description
ChatCompletionRequest OpenAI chat completion request
ChatCompletionResponse OpenAI chat completion response
Message Chat message with role, content, tool calls
Choice Response choice with message or delta
Delta Streaming delta content
Usage Token usage statistics
Tool Types
Type Description
Tool OpenAI tool definition
ToolFunction Function schema for a tool
ToolCall Tool call from assistant
ToolCallFunction Function name and arguments
DeltaToolCall Streaming tool call delta
DeltaFunction Streaming function delta
Message Content Helpers
// Get content as string (handles both string and array formats)
content := message.GetContentAsString()

// Set content as string
message.SetContentAsString("Hello, world!")
Tool Call JSON Handling

The ToolCallFunction type includes custom JSON marshaling/unmarshaling to handle OpenAI's format where arguments are a JSON string rather than an object:

// When marshaling to JSON, arguments become a string:
// {"name": "search", "arguments": "{\"query\": \"hello\"}"}

// When unmarshaling, the string is parsed back to map[string]any

Tool Filtering

Several filter helpers are provided:

// Include all tools
openai.AllTools()

// Include only specific tools
openai.ToolsByName("tool1", "tool2")

// Exclude specific tools
openai.ExcludeTools("admin_tool", "debug_tool")

// Custom filter
customFilter := func(name string) bool {
    return strings.HasPrefix(name, "public_")
}

Generating Tool Call IDs

When streaming, some LLMs don't provide tool call IDs. Generate one:

id := openai.GenerateToolCallID(index)
// Returns something like "call_a1b2c3d4e5f6..."

SSE Tool Status Events

When streaming chat completions with server-side tool processing, you can send tool execution status events to clients. These events are sent as SSE comments (prefixed with :) so standard SSE clients ignore them, but custom clients can parse them to show tool execution progress in the UI.

Event Format

Tool events are sent as SSE comments in the format :eventType:jsonData:

:tool_start:{"tool_call_id":"call_abc123","tool_name":"search","status":"running"}

:tool_end:{"tool_call_id":"call_abc123","tool_name":"search","status":"complete","result":"Search found 5 results..."}

The tool_end event includes the tool's result, allowing clients to display what the tool returned.

Using SSEToolHandler

The SSEToolHandler implements ToolHandler to automatically send tool events during execution:

// Create an SSE event writer (adapt to your HTTP framework)
sseWriter := openai.NewSimpleSSEWriter(responseWriter, func() {
    if f, ok := responseWriter.(http.Flusher); ok {
        f.Flush()
    }
})

// Create the tool handler with optional error logging
toolHandler := openai.NewSSEToolHandler(sseWriter, func(err error, eventType, toolName string) {
    log.Printf("Failed to write %s event for %s: %v", eventType, toolName, err)
})

// Attach to context for use during tool execution
ctx = openai.WithToolHandler(ctx, toolHandler)

// During tool processing, call in order:
// 1. toolHandler.OnToolCall(toolCall)  <- sends :tool_start: event
// 2. Execute the tool
// 3. toolHandler.OnToolResult(...)     <- sends :tool_end: event with result
Custom SSEEventWriter

For production use with your HTTP framework, implement the SSEEventWriter interface:

type SSEEventWriter interface {
    WriteEvent(eventType string, data any) error
}
Complete Stream Example

A streaming response with tool execution looks like:

data: {"id":"chatcmpl-xxx","choices":[{"delta":{"role":"assistant","content":"Let me look that up..."}}]}

:tool_start:{"tool_call_id":"call_abc","tool_name":"search","status":"running"}

:tool_end:{"tool_call_id":"call_abc","tool_name":"search","status":"complete","result":"Found 3 results for your query..."}

data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"Based on the search results..."}}]}

data: {"id":"chatcmpl-xxx","choices":[{"delta":{},"finish_reason":"stop"}]}

data: [DONE]
Parsing Tool Events (JavaScript)
const response = await fetch(url, options);
const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const text = decoder.decode(value);
  for (const line of text.split('\n')) {
    if (line.startsWith(':tool_start:')) {
      const event = JSON.parse(line.slice(':tool_start:'.length));
      showSpinner(`Running ${event.tool_name}...`);
    } else if (line.startsWith(':tool_end:')) {
      const event = JSON.parse(line.slice(':tool_end:'.length));
      hideSpinner(event.tool_name);
      // Optionally display the result
      if (event.result) {
        showToolResult(event.tool_name, event.result);
      }
    } else if (line.startsWith('data: ') && line !== 'data: [DONE]') {
      const chunk = JSON.parse(line.slice(6));
      // Handle OpenAI chunk...
    }
  }
}

Complete Example

Here's a complete example of an OpenAI-compatible endpoint using MCP tools:

func handleChatCompletion(w http.ResponseWriter, r *http.Request) {
    var req openai.ChatCompletionRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Add MCP tools to the request
    mcpTools := mcpServer.ListTools()
    req.Tools = append(req.Tools, openai.MCPToolsToOpenAI(mcpTools)...)

    // Forward to upstream LLM...
    response := callUpstreamLLM(req)

    // Process tool calls if any
    for _, choice := range response.Choices {
        for _, toolCall := range choice.Message.ToolCalls {
            // Execute MCP tool
            mcpResponse, err := mcpServer.CallTool(r.Context(),
                toolCall.Function.Name,
                toolCall.Function.Arguments)
            if err != nil {
                // Handle error
                continue
            }

            // Extract result
            result, _ := openai.ExtractToolResult(mcpResponse)

            // Add to messages for next iteration...
        }
    }

    json.NewEncoder(w).Encode(response)
}

License

This package is part of the MCP library and is licensed under the same terms.

Token Estimation

When the upstream LLM doesn't provide token usage in its response, you can use the TokenCounter to estimate tokens:

// Initialize counter and estimate prompt tokens from initial messages
tokenCounter := openai.NewTokenCounter()
tokenCounter.AddPromptTokensFromMessages(req.Messages)

// After receiving streaming deltas, track completion tokens
tokenCounter.AddCompletionTokensFromDelta(&delta)

// Or for non-streaming, track from the full message
tokenCounter.AddCompletionTokensFromMessage(&response.Choices[0].Message)

// When tools are used, track tool result tokens (they become prompt tokens for next iteration)
toolResultMsg := openai.BuildToolResultMessage(toolCall.ID, result)
tokenCounter.AddPromptTokensFromMessages([]openai.Message{toolResultMsg})

// Get the estimated usage
usage := tokenCounter.GetUsage()
// usage.PromptTokens, usage.CompletionTokens, usage.TotalTokens

// Or inject into a response if it's missing
tokenCounter.InjectUsageIfMissing(response)
Token Estimation Algorithm

The EstimateTokens function provides a fast, reproducible approximation based on:

  • Word boundaries (using strings.Fields)
  • Punctuation counting (each punctuation mark = 1 token)
  • Chat template overhead (~4 tokens for conversation structure)
  • Per-message overhead (~3 tokens for role markers and special tokens)

This is suitable for billing estimates and UI display, but not for exact token limit calculations.

Documentation

Index

Constants

View Source
const (
	// EventToolStart is sent when a tool execution begins
	EventToolStart = "tool_start"
	// EventToolEnd is sent when a tool execution completes
	EventToolEnd = "tool_end"
)

SSE event types for tool status notifications Standard OpenAI clients ignore these; custom clients can use them for UI feedback

Variables

This section is empty.

Functions

func EstimateTokens added in v0.5.0

func EstimateTokens(text string) int

EstimateTokens returns a rough token count for a given input string. This uses a simple heuristic based on word boundaries and punctuation.

func ExtractAllTextContent

func ExtractAllTextContent(response *mcp.ToolResponse) string

ExtractAllTextContent extracts all text content from an MCP ToolResponse, concatenating multiple text parts with newlines.

func ExtractToolResult

func ExtractToolResult(response *mcp.ToolResponse) (string, error)

ExtractToolResult extracts a string result from an MCP ToolResponse for use in OpenAI tool result messages.

Priority order:

  1. StructuredContent - serialized to JSON
  2. First text content in Content array
  3. Default success message

func GenerateToolCallID

func GenerateToolCallID(index int) string

GenerateToolCallID creates a unique ID for tool calls. This is useful when LLMs don't provide an ID in streaming responses. The format matches OpenAI's tool call ID format: "call_" followed by random characters.

func GetToolNames added in v0.4.0

func GetToolNames(toolCalls []ToolCall) []string

GetToolNames returns the names of all tools in the provided tool calls.

func HasToolCalls added in v0.4.0

func HasToolCalls(msg Message) bool

HasToolCalls returns true if the message contains tool calls.

func MustParseToolArguments added in v0.4.0

func MustParseToolArguments(arguments map[string]any, target interface{})

MustParseToolArguments is like ParseToolArguments but panics on error. Use only in situations where you're certain the arguments are valid.

func ParseToolArguments added in v0.4.0

func ParseToolArguments(arguments map[string]any, target interface{}) error

ParseToolArguments parses the tool call arguments map into the provided struct. The target should be a pointer to the struct.

func WithToolHandler

func WithToolHandler(ctx context.Context, h ToolHandler) context.Context

WithToolHandler attaches a ToolHandler to the context. The handler will receive events during tool processing.

Types

type APIError added in v0.4.0

type APIError struct {
	StatusCode int    `json:"-"`
	Type       string `json:"type"`
	Message    string `json:"message"`
	Param      string `json:"param,omitempty"`
	Code       string `json:"code,omitempty"`
}

APIError represents an error returned by the OpenAI API.

func NewAuthenticationError added in v0.4.0

func NewAuthenticationError(message string) *APIError

NewAuthenticationError creates an authentication error.

func NewInvalidRequestError added in v0.4.0

func NewInvalidRequestError(message string) *APIError

NewInvalidRequestError creates an invalid request error.

func NewRateLimitError added in v0.4.0

func NewRateLimitError(message string) *APIError

NewRateLimitError creates a rate limit error.

func NewServerError added in v0.4.0

func NewServerError(message string) *APIError

NewServerError creates a server error.

func NewTokenLimitError added in v0.4.0

func NewTokenLimitError(message string) *APIError

NewTokenLimitError creates a token limit error.

func (*APIError) Error added in v0.4.0

func (e *APIError) Error() string

func (*APIError) IsAuthentication added in v0.4.0

func (e *APIError) IsAuthentication() bool

IsAuthentication returns true if this is an authentication error (401).

func (*APIError) IsInvalidRequest added in v0.4.0

func (e *APIError) IsInvalidRequest() bool

IsInvalidRequest returns true if this is an invalid request error (400).

func (*APIError) IsNotFound added in v0.4.0

func (e *APIError) IsNotFound() bool

IsNotFound returns true if this is a not found error (404).

func (*APIError) IsPermission added in v0.4.0

func (e *APIError) IsPermission() bool

IsPermission returns true if this is a permission error (403).

func (*APIError) IsRateLimit added in v0.4.0

func (e *APIError) IsRateLimit() bool

IsRateLimit returns true if this is a rate limit error (429).

func (*APIError) IsRetryable added in v0.4.0

func (e *APIError) IsRetryable() bool

IsRetryable returns true if this error is likely to succeed on retry.

func (*APIError) IsServerError added in v0.4.0

func (e *APIError) IsServerError() bool

IsServerError returns true if this is a server error (5xx).

func (*APIError) IsTokenLimit added in v0.4.0

func (e *APIError) IsTokenLimit() bool

IsTokenLimit returns true if this is a token limit error.

type ChatCompletionRequest

type ChatCompletionRequest struct {
	Model           string    `json:"model"`
	Messages        []Message `json:"messages"`
	Tools           []Tool    `json:"tools,omitempty"`
	MaxTokens       int       `json:"max_tokens,omitempty"`
	Temperature     float32   `json:"temperature,omitempty"`
	ReasoningEffort string    `json:"reasoning_effort,omitempty"`
	Stream          bool      `json:"stream"`
}

ChatCompletionRequest represents an OpenAI chat completion request

type ChatCompletionResponse

type ChatCompletionResponse struct {
	ID                string   `json:"id"`
	Object            string   `json:"object"`
	Created           int64    `json:"created"`
	Model             string   `json:"model"`
	SystemFingerprint string   `json:"system_fingerprint,omitempty"`
	Choices           []Choice `json:"choices"`
	Usage             *Usage   `json:"usage,omitempty"`
}

ChatCompletionResponse represents an OpenAI chat completion response

type ChatStream

type ChatStream struct {
	// contains filtered or unexported fields
}

ChatStream provides an iterator interface for streaming chat completion responses. It is designed to be used in a for loop pattern:

stream := openai.NewChatStream(ctx, responseChan, errorChan)
for stream.Next() {
    chunk := stream.Current()
    // process chunk
}
if err := stream.Err(); err != nil {
    // handle error
}

func NewChatStream

func NewChatStream(ctx context.Context, responseChan <-chan ChatCompletionResponse, errorChan <-chan error) *ChatStream

NewChatStream creates a new ChatStream from response and error channels.

func (*ChatStream) Current

func (s *ChatStream) Current() ChatCompletionResponse

Current returns the current response chunk. Must be called after Next returns true.

func (*ChatStream) Done

func (s *ChatStream) Done() bool

Done returns true if the stream has completed.

func (*ChatStream) Err

func (s *ChatStream) Err() error

Err returns any error that occurred during streaming. Should be checked after Next returns false.

func (*ChatStream) Next

func (s *ChatStream) Next() bool

Next advances to the next response chunk. Returns true if a chunk is available, false if the stream is done or an error occurred.

type Choice

type Choice struct {
	Index        int     `json:"index"`
	Message      Message `json:"message,omitempty"`
	Delta        Delta   `json:"delta,omitempty"`
	FinishReason string  `json:"finish_reason,omitempty"`
}

Choice represents a completion choice

type CompletionAccumulator

type CompletionAccumulator struct {
	Choices []accumulatorChoice
}

CompletionAccumulator accumulates streaming chat completion chunks into complete responses. It handles the incremental building of content, tool calls, and refusals.

func (*CompletionAccumulator) AddChunk

func (acc *CompletionAccumulator) AddChunk(chunk ChatCompletionResponse)

AddChunk processes a streaming chunk and accumulates its content.

func (*CompletionAccumulator) Content

func (acc *CompletionAccumulator) Content() string

Content returns the current accumulated content for the first choice.

func (*CompletionAccumulator) FinishReason

func (acc *CompletionAccumulator) FinishReason() string

FinishReason returns the finish reason for the first choice.

func (*CompletionAccumulator) FinishedContent

func (acc *CompletionAccumulator) FinishedContent() (string, bool)

FinishedContent returns the accumulated content for the first choice if complete. Returns the content and true if finish_reason is "stop", otherwise empty string and false.

func (*CompletionAccumulator) FinishedRefusal

func (acc *CompletionAccumulator) FinishedRefusal() (string, bool)

FinishedRefusal returns the accumulated refusal for the first choice if present. Returns the refusal and true if there is refusal content, otherwise empty string and false.

func (*CompletionAccumulator) FinishedToolCall

func (acc *CompletionAccumulator) FinishedToolCall() (*ToolCall, bool)

FinishedToolCall returns the first accumulated tool call for the first choice if complete. Returns the tool call and true if finish_reason is "tool_calls", otherwise nil and false.

func (*CompletionAccumulator) FinishedToolCalls

func (acc *CompletionAccumulator) FinishedToolCalls() ([]ToolCall, bool)

FinishedToolCalls returns all accumulated tool calls for the first choice if complete. Returns the tool calls and true if finish_reason is "tool_calls", otherwise nil and false.

func (*CompletionAccumulator) IsComplete

func (acc *CompletionAccumulator) IsComplete() bool

IsComplete returns true if the first choice has a finish reason.

func (*CompletionAccumulator) Reset

func (acc *CompletionAccumulator) Reset()

Reset clears the accumulator for reuse.

type CompletionTokensDetails

type CompletionTokensDetails struct {
	ReasoningTokens          int `json:"reasoning_tokens"`
	AudioTokens              int `json:"audio_tokens"`
	AcceptedPredictionTokens int `json:"accepted_prediction_tokens"`
	RejectedPredictionTokens int `json:"rejected_prediction_tokens"`
}

CompletionTokensDetails represents detailed completion token usage

type ContentPart

type ContentPart struct {
	Type     string    `json:"type"`
	Text     string    `json:"text,omitempty"`
	ImageURL *ImageURL `json:"image_url,omitempty"`
}

ContentPart represents a multi-modal content part

func ImageBase64ContentPart added in v0.4.0

func ImageBase64ContentPart(base64Data string, mediaType string, detail string) ContentPart

ImageBase64ContentPart creates a ContentPart with a base64-encoded image. The mediaType should be something like "image/png" or "image/jpeg".

func ImageURLContentPart added in v0.4.0

func ImageURLContentPart(url string, detail string) ContentPart

ImageURLContentPart creates a ContentPart with an image URL.

func TextContentPart added in v0.4.0

func TextContentPart(text string) ContentPart

TextContentPart creates a ContentPart with text content.

type Delta

type Delta struct {
	ReasoningContent string          `json:"reasoning_content,omitempty"`
	Role             string          `json:"role,omitempty"`
	Content          string          `json:"content,omitempty"`
	Refusal          string          `json:"refusal,omitempty"`
	ToolCalls        []DeltaToolCall `json:"tool_calls,omitempty"`
}

Delta represents a streaming delta

type DeltaFunction

type DeltaFunction struct {
	Name      string `json:"name,omitempty"`
	Arguments string `json:"arguments,omitempty"`
}

DeltaFunction represents a streaming function delta

type DeltaToolCall

type DeltaToolCall struct {
	Index    int           `json:"index"`
	ID       string        `json:"id,omitempty"`
	Type     string        `json:"type,omitempty"`
	Function DeltaFunction `json:"function,omitempty"`
}

DeltaToolCall represents a streaming tool call delta

type ErrorResponse added in v0.4.0

type ErrorResponse struct {
	Error *APIError `json:"error"`
}

ErrorResponse represents the error response structure from OpenAI API.

type ImageURL

type ImageURL struct {
	URL    string `json:"url"`
	Detail string `json:"detail,omitempty"`
}

ImageURL represents an image URL in content

type MaxToolIterationsError added in v0.4.0

type MaxToolIterationsError struct {
	Iterations int
}

MaxToolIterationsError is returned when the maximum number of tool call iterations is reached without completing the conversation.

func NewMaxToolIterationsError added in v0.4.0

func NewMaxToolIterationsError(iterations int) *MaxToolIterationsError

NewMaxToolIterationsError creates a new MaxToolIterationsError.

func (*MaxToolIterationsError) Error added in v0.4.0

func (e *MaxToolIterationsError) Error() string

type Message

type Message struct {
	Role       string     `json:"role,omitempty"`
	Content    any        `json:"content,omitempty"`
	Refusal    string     `json:"refusal,omitempty"`
	ToolCalls  []ToolCall `json:"tool_calls,omitempty"`
	ToolCallID string     `json:"tool_call_id,omitempty"`
}

Message represents a chat message

func BuildAssistantMessage added in v0.4.0

func BuildAssistantMessage(content string) Message

BuildAssistantMessage creates an assistant message with the given content.

func BuildAssistantToolCallMessage added in v0.4.0

func BuildAssistantToolCallMessage(content string, toolCalls []ToolCall) Message

BuildAssistantToolCallMessage creates an assistant message with tool calls. This is useful when reconstructing a conversation that included tool calls.

func BuildMultimodalMessage added in v0.4.0

func BuildMultimodalMessage(parts ...ContentPart) Message

BuildMultimodalMessage creates a user message with multiple content parts.

func BuildSystemMessage added in v0.4.0

func BuildSystemMessage(content string) Message

BuildSystemMessage creates a system message with the given content.

func BuildToolResultMessage added in v0.4.0

func BuildToolResultMessage(toolCallID string, result string) Message

BuildToolResultMessage creates a tool result message for the given tool call ID.

func BuildUserMessage added in v0.4.0

func BuildUserMessage(content string) Message

BuildUserMessage creates a user message with the given content.

func ExecuteToolCall added in v0.4.0

func ExecuteToolCall(tc ToolCall, executor ToolExecutor) (Message, error)

ExecuteToolCall executes a single tool call using the provided executor. Returns a Message with the tool result that can be appended to the conversation.

func ExecuteToolCalls added in v0.4.0

func ExecuteToolCalls(toolCalls []ToolCall, executor ToolExecutor, stopOnError bool) ([]Message, error)

ExecuteToolCalls executes multiple tool calls using the provided executor and returns messages containing the results. If a tool call fails, the error message is included in the result and the execution continues (unless stopOnError is true).

func (*Message) GetContentAsString

func (m *Message) GetContentAsString() string

GetContentAsString returns the content as a string, handling both string and array formats

func (*Message) SetContentAsString

func (m *Message) SetContentAsString(content string)

SetContentAsString sets the content as a string

type Model

type Model struct {
	ID      string `json:"id"`
	Object  string `json:"object"`
	Created int64  `json:"created"`
	OwnedBy string `json:"owned_by"`
}

Model represents an individual model

type ModelsResponse

type ModelsResponse struct {
	Object string  `json:"object"`
	Data   []Model `json:"data"`
}

ModelsResponse represents the response from the /models endpoint

type NoOpToolHandler

type NoOpToolHandler struct{}

NoOpToolHandler is a ToolHandler that does nothing. Useful as a default or for testing.

func (NoOpToolHandler) OnToolCall

func (NoOpToolHandler) OnToolCall(toolCall ToolCall) error

func (NoOpToolHandler) OnToolResult

func (NoOpToolHandler) OnToolResult(toolCallID, toolName, result string) error

type PromptTokensDetails

type PromptTokensDetails struct {
	CachedTokens int `json:"cached_tokens"`
	AudioTokens  int `json:"audio_tokens"`
}

PromptTokensDetails represents detailed prompt token usage

type SSEEventWriter added in v0.5.0

type SSEEventWriter interface {
	// WriteEvent writes an SSE comment event (prefixed with ":")
	// The event type and data are formatted as ":eventType:jsonData\n\n"
	WriteEvent(eventType string, data any) error
}

SSEEventWriter is an interface for writing SSE events. This allows integration with various HTTP frameworks' streaming implementations.

type SSEToolHandler added in v0.5.0

type SSEToolHandler struct {
	// contains filtered or unexported fields
}

SSEToolHandler implements ToolHandler to send tool events via SSE streaming. It wraps an SSEEventWriter and sends tool_start/tool_end events as SSE comments.

Usage pattern:

  1. Call OnToolCall() BEFORE executing the tool (sends tool_start with "running" status)
  2. Execute the tool
  3. Call OnToolResult() AFTER execution completes (sends tool_end with "complete" status and result)

Example SSE output:

:tool_start:{"tool_call_id":"call_abc123","tool_name":"search","status":"running"}

:tool_end:{"tool_call_id":"call_abc123","tool_name":"search","status":"complete","result":"..."}

func NewSSEToolHandler added in v0.5.0

func NewSSEToolHandler(writer SSEEventWriter, errorLogger func(err error, eventType, toolName string)) *SSEToolHandler

NewSSEToolHandler creates a new SSEToolHandler that sends tool events to the given writer. The optional errorLogger is called when write failures occur (errors are logged but not returned since tool events are just status notifications and shouldn't block tool execution).

func (*SSEToolHandler) OnToolCall added in v0.5.0

func (h *SSEToolHandler) OnToolCall(toolCall ToolCall) error

OnToolCall sends a tool_start event when a tool execution begins. This should be called BEFORE executing the tool. Write failures are logged but not returned as errors since they're just status notifications.

func (*SSEToolHandler) OnToolResult added in v0.5.0

func (h *SSEToolHandler) OnToolResult(toolCallID, toolName, result string) error

OnToolResult sends a tool_end event when a tool execution completes. This should be called AFTER the tool has finished executing. The result parameter contains the tool's output, which is included in the event so clients can display tool results in the UI. Write failures are logged but not returned as errors since they're just status notifications.

type SimpleSSEWriter added in v0.5.0

type SimpleSSEWriter struct {
	// contains filtered or unexported fields
}

SimpleSSEWriter is a basic implementation of SSEEventWriter that writes to an io.Writer. For production use, you may want to implement your own SSEEventWriter with proper flushing, error handling, and client disconnect detection.

func NewSimpleSSEWriter added in v0.5.0

func NewSimpleSSEWriter(w io.Writer, flusher func()) *SimpleSSEWriter

NewSimpleSSEWriter creates a SimpleSSEWriter that writes to the given io.Writer. If the writer implements http.Flusher, pass a flush function to flush after each write.

func (*SimpleSSEWriter) WriteEvent added in v0.5.0

func (s *SimpleSSEWriter) WriteEvent(eventType string, data any) error

WriteEvent writes an SSE comment event in the format ":eventType:jsonData\n\n"

type StreamError added in v0.4.0

type StreamError struct {
	Err error
}

StreamError is returned when an error occurs during streaming.

func NewStreamError added in v0.4.0

func NewStreamError(err error) *StreamError

NewStreamError creates a new StreamError.

func (*StreamError) Error added in v0.4.0

func (e *StreamError) Error() string

func (*StreamError) Unwrap added in v0.4.0

func (e *StreamError) Unwrap() error

type StreamingToolCallAccumulator added in v0.4.0

type StreamingToolCallAccumulator struct {
	// contains filtered or unexported fields
}

StreamingToolCallAccumulator handles the complex task of accumulating streaming tool call deltas into complete ToolCall objects. It buffers arguments that come in chunks, generates IDs when missing, and parses the final JSON arguments.

Usage:

acc := NewStreamingToolCallAccumulator()
for each streaming chunk {
    acc.ProcessDelta(chunk.Choices[0].Delta)
}
toolCalls := acc.Finalize()

func NewStreamingToolCallAccumulator added in v0.4.0

func NewStreamingToolCallAccumulator() *StreamingToolCallAccumulator

NewStreamingToolCallAccumulator creates a new accumulator for streaming tool calls.

func (*StreamingToolCallAccumulator) Count added in v0.4.0

func (acc *StreamingToolCallAccumulator) Count() int

Count returns the number of tool calls being accumulated.

func (*StreamingToolCallAccumulator) Finalize added in v0.4.0

func (acc *StreamingToolCallAccumulator) Finalize() []ToolCall

Finalize parses accumulated arguments and returns complete ToolCall objects. Tool calls with empty names are skipped. Returns tool calls sorted by index.

func (*StreamingToolCallAccumulator) GetToolCall added in v0.4.0

func (acc *StreamingToolCallAccumulator) GetToolCall(index int) *ToolCall

GetToolCall returns a specific tool call by index without finalizing. Returns nil if the index doesn't exist.

func (*StreamingToolCallAccumulator) HasToolCalls added in v0.4.0

func (acc *StreamingToolCallAccumulator) HasToolCalls() bool

HasToolCalls returns true if any tool calls are being accumulated.

func (*StreamingToolCallAccumulator) ProcessDelta added in v0.4.0

func (acc *StreamingToolCallAccumulator) ProcessDelta(delta Delta) []string

ProcessDelta processes a streaming delta and accumulates tool call data. Returns the list of tool call IDs that were updated (useful for tracking progress).

func (*StreamingToolCallAccumulator) ProcessDeltaWithIDCallback added in v0.4.0

func (acc *StreamingToolCallAccumulator) ProcessDeltaWithIDCallback(delta Delta, onNewID func(index int, id string)) []string

ProcessDeltaWithIDCallback processes a streaming delta and calls the callback with any newly generated tool call IDs. This is useful when you need to update the original delta with generated IDs for forwarding to clients.

func (*StreamingToolCallAccumulator) Reset added in v0.4.0

func (acc *StreamingToolCallAccumulator) Reset()

Reset clears the accumulator for reuse.

type TokenCounter added in v0.5.0

type TokenCounter struct {
	// contains filtered or unexported fields
}

TokenCounter estimates token usage for OpenAI API requests and responses. It provides a fast, reproducible approximation of token counts when exact counts are not available from the API.

func NewTokenCounter added in v0.5.0

func NewTokenCounter() *TokenCounter

NewTokenCounter creates a new TokenCounter

func (*TokenCounter) AddCompletionTokensFromDelta added in v0.5.0

func (tc *TokenCounter) AddCompletionTokensFromDelta(delta *Delta)

AddCompletionTokensFromDelta adds estimated completion tokens from a streaming delta

func (*TokenCounter) AddCompletionTokensFromMessage added in v0.5.0

func (tc *TokenCounter) AddCompletionTokensFromMessage(msg *Message)

AddCompletionTokensFromMessage adds estimated completion tokens from a chat message

func (*TokenCounter) AddCompletionTokensFromText added in v0.5.0

func (tc *TokenCounter) AddCompletionTokensFromText(text string)

AddCompletionTokensFromText adds estimated completion tokens from text

func (*TokenCounter) AddPromptTokensFromMessages added in v0.5.0

func (tc *TokenCounter) AddPromptTokensFromMessages(messages []Message)

AddPromptTokensFromMessages estimates and adds prompt tokens from chat messages

func (*TokenCounter) AddPromptTokensFromText added in v0.5.0

func (tc *TokenCounter) AddPromptTokensFromText(text string)

AddPromptTokensFromText adds estimated prompt tokens from text

func (*TokenCounter) GetUsage added in v0.5.0

func (tc *TokenCounter) GetUsage() Usage

GetUsage returns the current usage statistics

func (*TokenCounter) InjectUsageIfMissing added in v0.5.0

func (tc *TokenCounter) InjectUsageIfMissing(resp *ChatCompletionResponse)

InjectUsageIfMissing injects estimated usage into a chat completion response if it's missing or zero

func (*TokenCounter) Reset added in v0.5.0

func (tc *TokenCounter) Reset()

Reset resets the token counters to zero

type Tool

type Tool struct {
	Type     string       `json:"type"`
	Function ToolFunction `json:"function"`
}

Tool represents an OpenAI tool definition

func MCPToolsToOpenAI

func MCPToolsToOpenAI(tools []mcp.MCPTool) []Tool

MCPToolsToOpenAI converts MCP tools to OpenAI function calling format

func MCPToolsToOpenAIFiltered

func MCPToolsToOpenAIFiltered(tools []mcp.MCPTool, filter func(name string) bool) []Tool

MCPToolsToOpenAIFiltered converts MCP tools to OpenAI format with optional filtering. If filter is nil, all tools are included. Otherwise, only tools where filter(name) returns true are included.

func NewTool added in v0.4.0

func NewTool(name, description string, parameters map[string]any) Tool

NewTool creates a tool definition for a function. The parameters should be a JSON Schema object describing the function parameters.

type ToolCall

type ToolCall struct {
	Index    int              `json:"index,omitempty"`
	ID       string           `json:"id"`
	Type     string           `json:"type"`
	Function ToolCallFunction `json:"function"`
}

ToolCall represents a tool call from the assistant

type ToolCallFunction

type ToolCallFunction struct {
	Name      string         `json:"name"`
	Arguments map[string]any `json:"arguments"`
}

ToolCallFunction represents the function details of a tool call

func (ToolCallFunction) MarshalJSON

func (tcf ToolCallFunction) MarshalJSON() ([]byte, error)

MarshalJSON implements custom JSON marshaling for ToolCallFunction. OpenAI expects arguments as a JSON string, not an object.

func (*ToolCallFunction) UnmarshalJSON

func (tcf *ToolCallFunction) UnmarshalJSON(data []byte) error

UnmarshalJSON implements custom JSON unmarshaling for ToolCallFunction. OpenAI sends arguments as a JSON string, not an object.

type ToolExecutionError added in v0.4.0

type ToolExecutionError struct {
	ToolName string
	ToolID   string
	Err      error
}

ToolExecutionError is returned when a tool call fails.

func NewToolExecutionError added in v0.4.0

func NewToolExecutionError(toolName, toolID string, err error) *ToolExecutionError

NewToolExecutionError creates a new ToolExecutionError.

func (*ToolExecutionError) Error added in v0.4.0

func (e *ToolExecutionError) Error() string

func (*ToolExecutionError) Unwrap added in v0.4.0

func (e *ToolExecutionError) Unwrap() error

type ToolExecutor added in v0.4.0

type ToolExecutor func(name string, arguments map[string]any) (string, error)

ToolExecutor is a function that executes tool calls and returns the result. The function receives the tool name and arguments (as a map) and returns the result string and any error.

type ToolFilter

type ToolFilter func(name string) bool

ToolFilter is a function type for filtering tools by name

func AllTools

func AllTools() ToolFilter

AllTools returns a filter that includes all tools

func ExcludeTools

func ExcludeTools(names ...string) ToolFilter

ExcludeTools returns a filter that excludes tools with the specified names

func ToolsByName

func ToolsByName(names ...string) ToolFilter

ToolsByName returns a filter that includes only tools with the specified names

type ToolFunction

type ToolFunction struct {
	Name        string         `json:"name"`
	Description string         `json:"description"`
	Parameters  map[string]any `json:"parameters,omitempty"`
}

ToolFunction represents a function definition for a tool

type ToolHandler

type ToolHandler interface {
	// OnToolCall is called when a tool call is about to be executed.
	OnToolCall(toolCall ToolCall) error

	// OnToolResult is called when a tool call has completed.
	OnToolResult(toolCallID, toolName, result string) error
}

ToolHandler receives events during tool processing. Implement this interface to receive notifications when tools are called and when results are received.

func ToolHandlerFromContext

func ToolHandlerFromContext(ctx context.Context) ToolHandler

ToolHandlerFromContext retrieves a ToolHandler from the context. Returns nil if no handler is attached.

type ToolStatusEvent added in v0.5.0

type ToolStatusEvent struct {
	ToolCallID string `json:"tool_call_id"`
	ToolName   string `json:"tool_name"`
	Status     string `json:"status"` // "running" or "complete"
	Result     string `json:"result,omitempty"`
	Error      string `json:"error,omitempty"`
}

ToolStatusEvent represents a tool execution status for SSE streaming. This is sent as an SSE comment (prefixed with ":") so standard SSE clients ignore it, but custom clients can parse it to show tool execution progress.

type Usage

type Usage struct {
	PromptTokens            int                      `json:"prompt_tokens"`
	CompletionTokens        int                      `json:"completion_tokens"`
	TotalTokens             int                      `json:"total_tokens"`
	PromptTokensDetails     *PromptTokensDetails     `json:"prompt_tokens_details,omitempty"`
	CompletionTokensDetails *CompletionTokensDetails `json:"completion_tokens_details,omitempty"`
}

Usage represents token usage

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL