# Go AI SDK: core documentation > Go AI SDK is a Go toolkit for building AI applications and agents: text and structured-output generation, streaming, tools, MCP, agents, embeddings and many model providers behind one interface. It tracks the Vercel AI SDK (TypeScript) for server-side features. Module: github.com/digitallysavvy/go-ai. Install: go get github.com/digitallysavvy/go-ai@latest. Go 1.26 or later. This file holds the most useful pages from the introduction, getting started, foundations, core, agents and reference sections, in full. Pages that did not fit are listed at the end as links. Providers, migration guides and troubleshooting are not included; find them in https://goaisdk.com/llms.txt. Orientation: https://goaisdk.com/agents.md --- # Foundations > A section that covers foundational knowledge around LLMs and concepts crucial to the Go AI SDK Canonical URL: https://goaisdk.com/docs/foundations/ Documentation index: https://goaisdk.com/llms.txt This section covers the foundational concepts you need to understand when working with Large Language Models and the Go AI SDK. ## Topics - **[Overview](https://goaisdk.com/docs/foundations/overview.md)** - Learn about foundational concepts around AI and LLMs. - **[Providers and Models](https://goaisdk.com/docs/foundations/providers-and-models.md)** - Learn about the providers and models that you can use with the Go AI SDK. - **[Prompts](https://goaisdk.com/docs/foundations/prompts.md)** - Learn about how prompts are used and defined in the Go AI SDK. - **[Tools](https://goaisdk.com/docs/foundations/tools.md)** - Learn about tools in the Go AI SDK and how to integrate external functionality. - **[Streaming](https://goaisdk.com/docs/foundations/streaming.md)** - Learn why streaming is used for AI applications and how it works in Go. - **[Provider Options](https://goaisdk.com/docs/foundations/provider-options.md)** - Learn how to pass provider-specific configuration beyond standard settings. ## Next Steps After understanding these foundational concepts, explore: - [AI SDK Core](https://goaisdk.com/docs/ai-sdk-core.md) for detailed API documentation - [Agents](https://goaisdk.com/docs/agents.md) to build autonomous AI agents - [Advanced Topics](https://goaisdk.com/docs/advanced.md) for production-ready patterns --- # Overview > An overview of foundational concepts critical to understanding the Go AI SDK Canonical URL: https://goaisdk.com/docs/foundations/overview Documentation index: https://goaisdk.com/llms.txt > **Note:** This page is a beginner-friendly introduction to high-level artificial intelligence (AI) concepts. To dive right into implementing the Go AI SDK, feel free to skip ahead to the [Generating Text guide](https://goaisdk.com/docs/ai-sdk-core/generating-text.md) or learn about our [supported models and providers](https://goaisdk.com/docs/foundations/providers-and-models.md). The Go AI SDK standardizes integrating artificial intelligence (AI) models across [supported providers](https://goaisdk.com/docs/foundations/providers-and-models.md). This enables developers to focus on building great AI applications in Go, not waste time on technical details. For example, here's how you can generate text with various models using the Go AI SDK: ```go package main import ( "context" "fmt" "log" "os" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider" "github.com/digitallysavvy/go-ai/pkg/providers/openai" "github.com/digitallysavvy/go-ai/pkg/providers/anthropic" "github.com/digitallysavvy/go-ai/pkg/providers/google" ) func main() { ctx := context.Background() // OpenAI openaiProvider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) gpt, _ := openaiProvider.LanguageModel("gpt-6-astra") // Anthropic anthropicProvider := anthropic.New(anthropic.Config{APIKey: os.Getenv("ANTHROPIC_API_KEY")}) claude, _ := anthropicProvider.LanguageModel("claude-sonnet-5-5") // Google googleProvider := google.New(google.Config{APIKey: os.Getenv("GOOGLE_GENERATIVE_AI_API_KEY")}) gemini, _ := googleProvider.LanguageModel("gemini-2.5-flash") // Same API for all providers for _, model := range []provider.LanguageModel{gpt, claude, gemini} { result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "What is love?", }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) } } ``` To effectively leverage the Go AI SDK, it helps to familiarize yourself with the following concepts: ## Generative Artificial Intelligence **Generative artificial intelligence** refers to models that predict and generate various types of outputs (such as text, images, or audio) based on what's statistically likely, pulling from patterns they've learned from their training data. For example: - Given a photo, a generative model can generate a caption. - Given an audio file, a generative model can generate a transcription. - Given a text description, a generative model can generate an image. ## Large Language Models A **large language model (LLM)** is a subset of generative models focused primarily on **text**. An LLM takes a sequence of words as input and aims to predict the most likely sequence to follow. It assigns probabilities to potential next sequences and then selects one. The model continues to generate sequences until it meets a specified stopping criterion. LLMs learn by training on massive collections of written text, which means they will be better suited to some use cases than others. For example, a model trained on GitHub data would understand the probabilities of sequences in source code particularly well. However, it's crucial to understand LLMs' limitations. When asked about less known or absent information, like the birthday of a personal relative, LLMs might "hallucinate" or make up information. It's essential to consider how well-represented the information you need is in the model. ## Embedding Models An **embedding model** is used to convert complex data (like words or images) into a dense vector (a list of numbers) representation, known as an embedding. Unlike generative models, embedding models do not generate new text or data. Instead, they provide representations of semantic and syntactic relationships between entities that can be used as input for other models or other natural language processing tasks. ## Reranking Models A **reranking model** is used to reorder a list of documents based on their relevance to a query. Unlike embedding models that generate vector representations, reranking models directly score document-query pairs for relevance. This is particularly useful for search and retrieval-augmented generation (RAG) applications where you need to rank multiple candidates by their relevance to a specific query. ## Go-Specific Advantages The Go AI SDK brings the power of AI to Go developers with several advantages: - **Idiomatic Go**: Uses Go conventions like `context.Context`, channels, and error returns - **Type Safety**: Strong typing with Go's type system - **Performance**: Compiled language performance for production workloads - **Concurrency**: Built-in support for concurrent operations with goroutines - **Easy Deployment**: Single binary deployment without runtime dependencies - **Standard Library**: Integrates seamlessly with Go's standard library --- In the next section, you will learn about the difference between model providers and models, and which ones are available in the Go AI SDK. ## See Also - [Providers and Models](https://goaisdk.com/docs/foundations/providers-and-models.md) - [Prompts](https://goaisdk.com/docs/foundations/prompts.md) - [Tools](https://goaisdk.com/docs/foundations/tools.md) - [Streaming](https://goaisdk.com/docs/foundations/streaming.md) --- # Getting Started > Entry point for Go AI SDK docs, pointing to the quick start, installation, a first example application, and guidance for API servers and frameworks. Canonical URL: https://goaisdk.com/docs/getting-started/ Documentation index: https://goaisdk.com/llms.txt The following guides are intended to provide you with an introduction to some of the core features provided by the Go AI SDK. ## Quick Start Get started quickly with our comprehensive tutorial: - **[Go Quick Start](https://goaisdk.com/docs/getting-started/golang.md)** - Build your first AI agent with the Go AI SDK in 5 minutes ## Learn the Library - **[Navigating the Library](https://goaisdk.com/docs/getting-started/navigating-the-library.md)** - Understand the structure and choose the right tools for your needs ## What You'll Learn These guides will walk you through: 1. **Setting up your environment** - Install the SDK and configure your API keys 2. **Core concepts** - Understand providers, models, and streaming 3. **Streaming** - Print text as the model produces it 4. **Using tools** - Extend your agent with external capabilities and build a chat loop 5. **Production patterns** - Best practices for real-world applications ## Prerequisites To follow these guides, you'll need: - **Go 1.26+** installed on your development machine - **An API key** from at least one AI provider (OpenAI, Anthropic, Google, etc.) - **Basic Go knowledge** - Understanding of packages, error handling, and goroutines ## Quick Installation ```bash go get github.com/digitallysavvy/go-ai ``` ## Example: Your First AI Application Set your API key in the shell, then run a minimal program: ```bash export OPENAI_API_KEY=sk-... ``` ```go package main import ( "context" "fmt" "log" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { model, err := openai.New(openai.Config{}).LanguageModel(openai.ModelGPT6Astra) if err != nil { log.Fatal(err) } result, err := ai.GenerateText(context.Background(), ai.GenerateTextOptions{ Model: model, Prompt: "Explain what makes Go great for AI applications.", }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) } ``` The [Go Quick Start](https://goaisdk.com/docs/getting-started/golang.md) builds on this program with streaming, a tool, and a chat loop. ## API Servers and Frameworks You can use the Go AI SDK with any Go web framework or server: - **net/http** - Standard library HTTP server - **Gin** - High-performance web framework - **Echo** - Minimalist web framework - **Fiber** - Express-inspired framework - **Chi** - Lightweight router - **Gorilla** - Web toolkit - **gRPC** - For high-performance RPC services Example with net/http: ```go func streamHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() stream, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Write a story...", }) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") flusher, _ := w.(http.Flusher) for chunk := range stream.Chunks() { fmt.Fprintf(w, "data: %s\n\n", chunk.Text) flusher.Flush() } } ``` ## Next Steps After completing the getting started guide, explore: - **[Build a chat app](https://goaisdk.com/docs/build-a-chat-app.md)** - Serve a `useChat` frontend from Go, add tool approval and run coding agents, based on the [Shipyard demo](https://github.com/digitallysavvy/go-ai-demo) - **[Recipes](https://goaisdk.com/docs/recipes.md)** - Short, complete programs for common tasks - **[Foundations](https://goaisdk.com/docs/foundations.md)** - Deep dive into core concepts - **[AI SDK Core](https://goaisdk.com/docs/ai-sdk-core.md)** - Complete API reference - **[Agents](https://goaisdk.com/docs/agents.md)** - Build autonomous agents - **[Advanced Topics](https://goaisdk.com/docs/advanced.md)** - Production-ready patterns ## Need Help? - **Documentation**: You're reading it! - **GitHub Issues**: [Report bugs or ask questions](https://github.com/digitallysavvy/go-ai/issues) - **Examples**: Check the `examples/` directory in the repository --- # Go Quick Start > Step-by-step quick start for building a first AI agent in Go, covering setup, provider choice, adding tools, and enabling multi-step tool calls. Canonical URL: https://goaisdk.com/docs/getting-started/golang Documentation index: https://goaisdk.com/llms.txt In this quickstart, you'll make a first text generation call, stream the response, give the model a tool, and wrap it all in a chat loop. Each step is a complete program you can run. ## Prerequisites - **Go 1.26+** installed on your local development machine - An **API key** from a supported provider (OpenAI, Anthropic, Google, etc.) If you haven't obtained your API key, sign up at your chosen provider's website. ## Set up your application Create a new directory and initialize a Go module: ```bash mkdir my-ai-app cd my-ai-app go mod init my-ai-app go get github.com/digitallysavvy/go-ai ``` > **Note:** The Go AI SDK is a unified interface to large language models. You can change > model and provider with a couple of lines of code. Learn more about > [available providers](https://goaisdk.com/docs/providers.md). ### Set your API key The OpenAI provider reads `OPENAI_API_KEY` from the environment. Export it in the shell you will run the program from: ```bash export OPENAI_API_KEY=sk-... ``` Other providers read their own variable, for example `ANTHROPIC_API_KEY` or `GOOGLE_GENERATIVE_AI_API_KEY`. If you prefer a `.env` file, Go does not load it on its own. Put `OPENAI_API_KEY=sk-...` in `.env`, install the loader with `go get github.com/joho/godotenv`, and add a blank import of its `autoload` package to `main.go`: ```go skip-compile import _ "github.com/joho/godotenv/autoload" ``` The loader reads `.env` from the working directory when the program starts. Don't commit the file. ## Generate text Create `main.go`: ```go filename="main.go" package main import ( "context" "fmt" "log" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { model, err := openai.New(openai.Config{}).LanguageModel(openai.ModelGPT6Astra) if err != nil { log.Fatal(err) } result, err := ai.GenerateText(context.Background(), ai.GenerateTextOptions{ Model: model, Prompt: "Explain what a goroutine is in one sentence.", }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) } ``` Run it: ```bash go run main.go ``` You should see one sentence about goroutines printed to your terminal. `openai.ModelGPT6Astra` is a constant for the model ID `gpt-6-astra`. Every provider package has constants like it in `model_ids.go`. ## Stream the response `ai.GenerateText` waits for the whole answer. For a chat interface you want text as it arrives. Replace `main.go` with: ```go filename="main.go" package main import ( "context" "fmt" "log" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { model, err := openai.New(openai.Config{}).LanguageModel(openai.ModelGPT6Astra) if err != nil { log.Fatal(err) } stream, err := ai.StreamText(context.Background(), ai.StreamTextOptions{ Model: model, Prompt: "Write a haiku about concurrency.", }) if err != nil { log.Fatal(err) } for chunk := range stream.Chunks() { if chunk.Type == provider.ChunkTypeText { fmt.Print(chunk.Text) } } fmt.Println() if err := stream.Err(); err != nil { log.Fatal(err) } } ``` `stream.Chunks()` returns a channel of chunks. Text chunks carry the next piece of the answer. After the channel closes, `stream.Err()` reports any error that ended the stream early. ## Switch providers To change providers, change the provider and model lines. The rest of your code stays the same. ### Anthropic ```go import "github.com/digitallysavvy/go-ai/pkg/providers/anthropic" p := anthropic.New(anthropic.Config{ APIKey: os.Getenv("ANTHROPIC_API_KEY"), }) model, err := p.LanguageModel(anthropic.ClaudeSonnet5_5) ``` ### Google ```go import "github.com/digitallysavvy/go-ai/pkg/providers/google" p := google.New(google.Config{ APIKey: os.Getenv("GOOGLE_GENERATIVE_AI_API_KEY"), }) model, err := p.LanguageModel(google.ModelGemini31FlashLitePreview) ``` ## Add a tool Language models are poor at exact tasks like arithmetic and cannot see the outside world. [Tools](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md) are Go functions the model can ask you to run. The SDK runs the function and sends the result back to the model. This program gives the model a weather tool. `StopWhen` lets the model take several steps: call the tool, read the result, then answer. ```go filename="main.go" package main import ( "context" "fmt" "log" "math/rand" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider/types" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { model, err := openai.New(openai.Config{}).LanguageModel(openai.ModelGPT6Astra) if err != nil { log.Fatal(err) } weather := types.Tool{ Name: "getWeather", Description: "Get the weather in a location (fahrenheit)", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{ "type": "string", "description": "The location to get the weather for", }, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, params map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { location, _ := params["location"].(string) return map[string]interface{}{ "location": location, "temperature": rand.Intn(59) + 32, // random 32-90°F }, nil }, } result, err := ai.GenerateText(context.Background(), ai.GenerateTextOptions{ Model: model, Prompt: "What's the weather in New York?", Tools: []types.Tool{weather}, StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) } ``` The tool returns a random temperature, so the answer changes on each run. Replace the body of `Execute` with a call to a real weather API when you are ready. The pieces of a tool: 1. **`Description`** helps the model decide when to use the tool. 2. **`Parameters`** is a JSON Schema describing the input. Here it requires a `location` string. 3. **`Execute`** runs your code and returns any JSON-serializable value. ## Build a chat loop Now put the pieces together: a terminal chat that streams answers, keeps history, and can call the tool. ```go filename="main.go" package main import ( "bufio" "context" "fmt" "log" "math/rand" "os" "strings" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider" "github.com/digitallysavvy/go-ai/pkg/provider/types" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { ctx := context.Background() model, err := openai.New(openai.Config{}).LanguageModel(openai.ModelGPT6Astra) if err != nil { log.Fatal(err) } tools := []types.Tool{ { Name: "getWeather", Description: "Get the weather in a location (fahrenheit)", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{ "type": "string", "description": "The location to get the weather for", }, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, params map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { location, _ := params["location"].(string) return map[string]interface{}{ "location": location, "temperature": rand.Intn(59) + 32, }, nil }, }, } messages := []types.Message{} reader := bufio.NewReader(os.Stdin) fmt.Println("AI chat (type 'exit' to quit)") for { fmt.Print("You: ") userInput, err := reader.ReadString('\n') userInput = strings.TrimSpace(userInput) if err != nil || userInput == "exit" { break } if userInput == "" { continue } messages = append(messages, types.Message{ Role: types.RoleUser, Content: []types.ContentPart{types.TextContent{Text: userInput}}, }) stream, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Messages: messages, Tools: tools, StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, }) if err != nil { log.Printf("Error: %v\n", err) messages = messages[:len(messages)-1] continue } fmt.Print("\nAssistant: ") var answer strings.Builder for chunk := range stream.Chunks() { if chunk.Type == provider.ChunkTypeText { fmt.Print(chunk.Text) answer.WriteString(chunk.Text) } } fmt.Print("\n\n") if err := stream.Err(); err != nil { log.Printf("Stream error: %v\n", err) messages = messages[:len(messages)-1] continue } messages = append(messages, types.Message{ Role: types.RoleAssistant, Content: []types.ContentPart{types.TextContent{Text: answer.String()}}, }) } } ``` Run it with `go run main.go` and ask "What's the weather in New York in celsius?". The model calls `getWeather`, reads the result, converts it, and streams the answer. What the loop does: 1. **Keeps history** in the `messages` slice. Each turn appends the user message, then the assistant's answer. 2. **Streams** each answer with `ai.StreamText` and prints text chunks as they arrive. 3. **Allows several steps** with `StopWhen`, so tool calls resolve before the final answer. 4. **Rolls back** the last user message if a call fails, so history stays consistent. To add more tools, append them to the `tools` slice. A model can chain tools, for example one that fetches a temperature and one that converts it. ## Where to Next? You've built an AI agent using the Go AI SDK! From here, you have several paths to explore: - **[Build a chat app](https://goaisdk.com/docs/build-a-chat-app.md)** - Serve a `useChat` frontend from Go, add tool approval and run coding agents, based on the [Shipyard demo](https://github.com/digitallysavvy/go-ai-demo) - **[Recipes](https://goaisdk.com/docs/recipes.md)** - Short, complete programs for common tasks - **[Foundations](https://goaisdk.com/docs/foundations.md)** - Learn about core concepts like providers, prompts, and streaming - **[AI SDK Core](https://goaisdk.com/docs/ai-sdk-core.md)** - Explore the complete API reference - **[Agents](https://goaisdk.com/docs/agents.md)** - Build more sophisticated autonomous agents - **[Advanced Topics](https://goaisdk.com/docs/advanced.md)** - Learn about production patterns like caching, rate limiting, and backpressure - **[Examples](https://github.com/digitallysavvy/go-ai/tree/main/examples)** - Check out more example applications ## Production Tips When moving to production, consider: 1. **Error Handling** - Implement proper error handling and retry logic 2. **Context Timeouts** - Set appropriate timeouts for your use case 3. **Rate Limiting** - Respect provider rate limits 4. **Monitoring** - Add telemetry and logging 5. **Cost Management** - Monitor token usage and implement caching Example with timeout and better error handling: ```go import providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() result, err := ai.GenerateText(ctx, options) if err != nil { var rateLimitErr *providererrors.RateLimitError var providerErr *providererrors.ProviderError switch { case errors.As(err, &rateLimitErr): log.Printf("Rate limited: %v", rateLimitErr) // Wait and retry, respecting rateLimitErr.RetryAfterSeconds if set case errors.As(err, &providerErr): log.Printf("Provider error (%d): %v", providerErr.StatusCode, providerErr) // Retry on 5xx, fail fast on 4xx case providererrors.IsValidationError(err): log.Printf("Invalid input: %v", err) // Fix the request default: log.Printf("Unknown error: %v", err) } return } ``` --- # Use Go AI SDK with coding agents > Point Claude Code, Cursor, Codex and other coding agents at the Go AI SDK docs, with a snippet for AGENTS.md or CLAUDE.md, an agent skill, and a docs MCP server. Canonical URL: https://goaisdk.com/docs/getting-started/using-go-ai-with-coding-agents Documentation index: https://goaisdk.com/llms.txt Coding agents write better Go AI SDK code when they read the docs instead of guessing from training data. The site publishes the docs in forms an agent can use directly. ## Agent entry points | URL | What it is | |---|---| | [`/agents.md`](https://goaisdk.com/agents.md) | A 3 KB orientation file: module path, install, the six most common calls and the usual mistakes. Start here. | | [`/llms.txt`](https://goaisdk.com/llms.txt) | An index of every page with a "Start here" block. Providers, migration guides and troubleshooting sit under "Optional". | | [`/sitemap.md`](https://goaisdk.com/sitemap.md) | Every page with its type, a one-line summary and prerequisites. | | [`/llms-core.txt`](https://goaisdk.com/llms-core.txt) | The core, agents and reference docs in one file, under 300 KB. | | `/docs/
/llms.txt` | An index for one section, for example `/docs/agents/llms.txt`. | | [`/llms-full.txt`](https://goaisdk.com/llms-full.txt) | All docs in one file. It is large; prefer the files above. | | `.md` | That page as markdown, with its title, description and canonical URL at the top. | ## Add the SDK to your project instructions Paste this into your project's `AGENTS.md` or `CLAUDE.md`: ```md ## Go AI SDK This project uses the Go AI SDK (`github.com/digitallysavvy/go-ai`, Go 1.26 or later). - Read https://goaisdk.com/agents.md before writing SDK code. It lists the common calls and mistakes. - Find a page with https://goaisdk.com/llms.txt, or search https://goaisdk.com/sitemap.md. Fetch any docs page as markdown by appending `.md` to its URL. - `LanguageModel(id)` returns `(model, error)`. Streams are `TextStream` or `Chunks()`, not `io.Reader`. - Provider option keys are camelCase. Use `ToolApproval`, not `NeedsApproval`. - `GenerateText` and `StreamText` run one step unless you set `StopWhen`. - For a useChat endpoint, use `ai.PipeUIMessageStreamToResponse` or `agent.PipeAgentUIStreamFromUIMessagesToResponse`. They set the stream headers. - Take model IDs from the provider package constants (for example `anthropic.ClaudeSonnet5_5`). - Reference app: https://github.com/digitallysavvy/go-ai-demo ``` ## Install the agent skill The repository ships the same guidance as an agent skill at [`skills/go-ai/SKILL.md`](https://github.com/digitallysavvy/go-ai/blob/main/skills/go-ai/SKILL.md). To use it in Claude Code, copy the folder into your project or your home directory: ```bash mkdir -p .claude/skills/go-ai curl -fsSL https://raw.githubusercontent.com/digitallysavvy/go-ai/main/skills/go-ai/SKILL.md \ -o .claude/skills/go-ai/SKILL.md ``` Use `~/.claude/skills/go-ai/` instead to make it available in every project. The skill loads when you work on code that imports the SDK. ## Search the docs from your editor The `goai-docs-mcp` server gives an agent two tools, `search_docs` and `read_doc`, over the full docs. It runs locally over stdio, and the docs are compiled into the binary, so it needs no network access and no API key. ```bash go install github.com/digitallysavvy/go-ai/cmd/goai-docs-mcp@latest ``` Add it to Claude Code: ```bash claude mcp add go-ai-docs -- goai-docs-mcp ``` Add it to Cursor in `.cursor/mcp.json` (or `~/.cursor/mcp.json` for every project): ```json { "mcpServers": { "go-ai-docs": { "command": "goai-docs-mcp" } } } ``` Make sure `$(go env GOPATH)/bin` is on your `PATH`, or use the absolute path to the binary as the command. Then ask your agent to "search the Go AI SDK docs for tool approval". The docs in the binary match the version you installed, so update it with the SDK. ## Next steps - [Quick start](https://goaisdk.com/docs/getting-started/golang.md) builds a first chat agent. - The [Shipyard demo](https://github.com/digitallysavvy/go-ai-demo) is a complete app with a useChat frontend, a Go backend, an approval-gated tool and a coding-agent harness. --- # Agents > Landing page for the agents section of the Go AI SDK docs, linking guides on building agents, workflows, callbacks, and call-option configuration. Canonical URL: https://goaisdk.com/docs/agents/ Documentation index: https://goaisdk.com/llms.txt The following section shows you how to build agents with the Go AI SDK - systems where large language models (LLMs) use tools in a loop to accomplish tasks. ## Contents ### [Overview](https://goaisdk.com/docs/agents/overview.md) Learn what agents are and why to use the ToolLoopAgent. ### [Building Agents](https://goaisdk.com/docs/agents/building-agents.md) Complete guide to creating agents with the ToolLoopAgent. ### [Workflow Patterns](https://goaisdk.com/docs/agents/workflows.md) Structured patterns using core functions for complex workflows. ### [Loop Control](https://goaisdk.com/docs/agents/loop-control.md) Advanced execution control with MaxSteps and custom loop patterns. ### [Configuring Call Options](https://goaisdk.com/docs/agents/configuring-call-options.md) Pass runtime inputs to dynamically configure agent behavior. ### [WorkflowAgent](https://goaisdk.com/docs/agents/workflow-agent.md) Durable and serializable workflow agent execution with resumable SSE streams. --- # Overview > Explains how agents combine LLMs and tools in a loop using ToolLoopAgent, covering execution flow, stopping conditions, callbacks, and structured workflows. Canonical URL: https://goaisdk.com/docs/agents/overview Documentation index: https://goaisdk.com/llms.txt Agents are **large language models (LLMs)** that use **tools** in a **loop** to accomplish tasks. These components work together: - **LLMs** process input and decide the next action - **Tools** extend capabilities beyond text generation (reading files, calling APIs, writing to databases) - **Loop** orchestrates execution through: - **Context management** - Maintaining conversation history and deciding what the model sees (input) at each step - **Stopping conditions** - Determining when the loop (task) is complete ## ToolLoopAgent The `ToolLoopAgent` handles these three components. Here's an agent that uses multiple tools in a loop to accomplish a task: ```go package main import ( "context" "fmt" "log" "math/rand" "os" "github.com/digitallysavvy/go-ai/pkg/agent" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider/types" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { ctx := context.Background() // Set up provider and model provider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) model, _ := provider.LanguageModel("gpt-6-astra") // Create agent with tools weatherAgent := agent.NewToolLoopAgent(agent.AgentConfig{ Model: model, Tools: []types.Tool{ { Name: "weather", Description: "Get the weather in a location (in Fahrenheit)", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{ "type": "string", "description": "The location to get the weather for", }, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { location := input["location"].(string) return map[string]interface{}{ "location": location, "temperature": 72 + rand.Intn(21) - 10, }, nil }, }, { Name: "convertFahrenheitToCelsius", Description: "Convert temperature from Fahrenheit to Celsius", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "temperature": map[string]interface{}{ "type": "number", "description": "Temperature in Fahrenheit", }, }, "required": []string{"temperature"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { temp := input["temperature"].(float64) celsius := int((temp - 32) * (5.0 / 9.0)) return map[string]interface{}{ "celsius": celsius, }, nil }, }, }, StopWhen: []ai.StopCondition{ai.IsStepCount(20)}, // Agent stops after maximum of 20 steps }) // Execute agent result, err := weatherAgent.Execute(ctx, "What is the weather in San Francisco in celsius?") if err != nil { log.Fatal(err) } fmt.Println("Final Answer:", result.Text) fmt.Printf("Steps taken: %d\n", len(result.Steps)) } ``` The agent automatically: 1. Calls the `weather` tool to get the temperature in Fahrenheit 2. Calls `convertFahrenheitToCelsius` to convert it 3. Generates a final text response with the result The `ToolLoopAgent` handles the loop, context management, and stopping conditions. ## Why Use Agents? Using agents provides several benefits: - **Reduces boilerplate** - Manages loops and message arrays automatically - **Improves reusability** - Define once, use throughout your application - **Simplifies maintenance** - Single place to update agent configuration - **Handles complexity** - Manages tool execution, context, and termination logic For most use cases, start with agents. Use core functions (`ai.GenerateText`, `ai.StreamText`) when you need explicit control over each step for complex structured workflows. ## Basic Agent Structure Every agent needs: ### 1. Model The language model that powers the agent: ```go provider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) model, _ := provider.LanguageModel("gpt-6-astra") ``` ### 2. Tools Functions the agent can call to perform actions: ```go tools := []types.Tool{ { Name: "search", Description: "Search the web for information", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "query": map[string]interface{}{"type": "string"}, }, "required": []string{"query"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { // Implementation return map[string]interface{}{"results": []string{}}, nil }, }, } ``` ### 3. Configuration Settings that control agent behavior: ```go config := agent.AgentConfig{ Model: model, Tools: tools, MaxSteps: 15, // Maximum iterations System: "You are a helpful assistant", // System prompt } ``` ## Agent Execution Flow When you call `agent.Execute()`, the following happens: 1. **Initial Call**: Agent receives user prompt and converts it to messages 2. **Generation**: Model generates response (text and/or tool calls) 3. **Tool Execution**: If tool calls are present, execute them 4. **Context Update**: Add assistant response and tool results to conversation 5. **Loop**: Repeat steps 2-4 until stopping condition is met 6. **Return**: Final result with text, steps, and metadata ``` ┌─────────────┐ │ User Prompt │ └──────┬──────┘ │ ▼ ┌─────────────────┐ │ Model Generate │◄────┐ └────────┬────────┘ │ │ │ ▼ │ ┌────────┐ │ │ Tools? │──No───►Stop └───┬────┘ │ │Yes │ ▼ │ ┌──────────────┐ │ │ Execute Tools│ │ └──────┬───────┘ │ │ │ └────────────────┘ ``` ## Stopping Conditions Agents stop when: 1. **StopWhen reached**: Agent hits the configured step limit (default: `ai.IsStepCount(20)`) 2. **No tool calls**: Model returns text without requesting tool calls 3. **Error occurs**: Tool execution or generation fails 4. **Context canceled**: Context deadline or cancellation ```go config := agent.AgentConfig{ Model: model, Tools: tools, StopWhen: []ai.StopCondition{ai.IsStepCount(20)}, // Stop after 20 iterations } ``` ## Callbacks Monitor agent execution with callbacks: ```go config := agent.AgentConfig{ Model: model, Tools: tools, OnStepStart: func(stepNum int) { fmt.Printf("Starting step %d\n", stepNum) }, OnStepFinish: func(step types.StepResult) { fmt.Printf("Step %d complete: %s\n", step.StepNumber, step.Text) }, OnToolCall: func(toolCall types.ToolCall) { fmt.Printf("Calling tool: %s\n", toolCall.ToolName) }, OnToolResult: func(toolResult types.ToolResult) { fmt.Printf("Tool %s returned: %v\n", toolResult.ToolName, toolResult.Result) }, OnFinish: func(result *agent.AgentResult) { fmt.Printf("Agent complete after %d steps\n", len(result.Steps)) }, } ``` ## Agent vs Core Functions ### When to use Agents Use agents when: - Task requires multiple tool calls - You want automatic loop management - You need reusable agent configurations - You want simplified context management ```go // Agent handles everything automatically agent := agent.NewToolLoopAgent(config) result, _ := agent.Execute(ctx, "Complex multi-step task") ``` ### When to use Core Functions Use core functions when: - You need explicit control over each step - Building complex structured workflows - Implementing custom loop logic - Requiring specific error handling ```go // Manual control over each step for step := 0; step < maxSteps; step++ { result, _ := ai.GenerateText(ctx, options) // Custom logic for each step if shouldStop(result) { break } } ``` ## Structured Workflows Agents are flexible and powerful, but non-deterministic. When you need reliable, repeatable outcomes with explicit control flow, use core functions with structured workflow patterns combining: - Conditional statements for explicit branching - Standard functions for reusable logic - Error handling for robustness - Explicit control flow for predictability [Explore workflow patterns](https://goaisdk.com/docs/agents/workflows.md) to learn more about building structured, reliable systems. ## Real-World Example Here's a practical agent that can search the web and summarize results: ```go package main import ( "context" "fmt" "log" "net/http" "os" "github.com/digitallysavvy/go-ai/pkg/agent" "github.com/digitallysavvy/go-ai/pkg/provider/types" "github.com/digitallysavvy/go-ai/pkg/providers/anthropic" ) func main() { ctx := context.Background() provider := anthropic.New(anthropic.Config{APIKey: os.Getenv("ANTHROPIC_API_KEY")}) model, _ := provider.LanguageModel("claude-sonnet-4-5") // Create research agent researchAgent := agent.NewToolLoopAgent(agent.AgentConfig{ Model: model, System: "You are a research assistant. Use the available tools to find and summarize information.", Tools: []types.Tool{ { Name: "search", Description: "Search the web for information", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "query": map[string]interface{}{ "type": "string", "description": "The search query", }, }, "required": []string{"query"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { query := input["query"].(string) // Actual implementation would call a search API return fmt.Sprintf("Search results for: %s", query), nil }, }, { Name: "fetchWebpage", Description: "Fetch and return the content of a webpage", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "url": map[string]interface{}{ "type": "string", "description": "The URL to fetch", }, }, "required": []string{"url"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { url := input["url"].(string) resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() // Actual implementation would parse and clean HTML return fmt.Sprintf("Content from %s", url), nil }, }, }, MaxSteps: 15, OnStepFinish: func(step types.StepResult) { fmt.Printf("[Step %d] %s\n", step.StepNumber, step.Text) }, }) result, err := researchAgent.Execute(ctx, "Research the latest developments in quantum computing and provide a summary") if err != nil { log.Fatal(err) } fmt.Println("\n=== Final Answer ===") fmt.Println(result.Text) fmt.Printf("\nCompleted in %d steps\n", len(result.Steps)) } ``` ## Next Steps - **[Building Agents](https://goaisdk.com/docs/agents/building-agents.md)** - Detailed guide to creating agents - **[Workflow Patterns](https://goaisdk.com/docs/agents/workflows.md)** - Structured patterns using core functions - **[Loop Control](https://goaisdk.com/docs/agents/loop-control.md)** - Advanced execution control - **[Configuring Call Options](https://goaisdk.com/docs/agents/configuring-call-options.md)** - Fine-tune agent behavior ## See Also - [Tools and Tool Calling](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md) - [Generating Text](https://goaisdk.com/docs/ai-sdk-core/generating-text.md) - [Error Handling](https://goaisdk.com/docs/ai-sdk-core/error-handling.md) --- # AI SDK Core > Learn about AI SDK Core and how to work with Large Language Models in Go Canonical URL: https://goaisdk.com/docs/ai-sdk-core/ Documentation index: https://goaisdk.com/llms.txt The core API provides a comprehensive set of functions for working with Large Language Models, embeddings, image generation, speech, and more. All functions are designed with Go's concurrency patterns and best practices in mind. ## Core Functions ### Text Generation - **[Overview](https://goaisdk.com/docs/ai-sdk-core/overview.md)** - Learn about AI SDK Core and how to work with Large Language Models (LLMs). - **[Generating Text](https://goaisdk.com/docs/ai-sdk-core/generating-text.md)** - Learn how to generate text using `GenerateText` and `StreamText`. - **[Generating Structured Data](https://goaisdk.com/docs/ai-sdk-core/generating-structured-data.md)** - Learn how to generate structured data with type-safe JSON output. - **[Tool Calling](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md)** - Learn how to do tool calling with AI SDK Core. ### Configuration - **[Settings](https://goaisdk.com/docs/ai-sdk-core/settings.md)** - Learn how to set up settings for language model generations. - **[Upload File and Upload Skill](https://goaisdk.com/docs/ai-sdk-core/upload-file-and-skill.md)** - Learn how to upload files and multi-file skills for provider references. ### Embeddings and Ranking - **[Embeddings](https://goaisdk.com/docs/ai-sdk-core/embeddings.md)** - Learn how to use embeddings with AI SDK Core. - **[Reranking](https://goaisdk.com/docs/ai-sdk-core/reranking.md)** - Learn how to rerank documents using reranking models. ### Media Generation - **[Image Generation](https://goaisdk.com/docs/ai-sdk-core/image-generation.md)** - Learn how to generate images with AI SDK Core. - **[Transcription](https://goaisdk.com/docs/ai-sdk-core/transcription.md)** - Learn how to transcribe audio with AI SDK Core. - **[Speech](https://goaisdk.com/docs/ai-sdk-core/speech.md)** - Learn how to generate speech with AI SDK Core. - **[Realtime](https://goaisdk.com/docs/ai-sdk-core/realtime.md)** - Learn how to build realtime voice conversations with Go. ### Advanced Features - **[Middleware](https://goaisdk.com/docs/ai-sdk-core/middleware.md)** - Learn how to use middleware to wrap AI provider calls. - **[Provider Management](https://goaisdk.com/docs/ai-sdk-core/provider-management.md)** - Learn how to work with multiple providers and implement fallbacks. - **[Error Handling](https://goaisdk.com/docs/ai-sdk-core/error-handling.md)** - Learn how to handle errors with AI SDK Core. - **[Testing](https://goaisdk.com/docs/ai-sdk-core/testing.md)** - Learn how to test your AI-powered Go applications. - **[Telemetry](https://goaisdk.com/docs/ai-sdk-core/telemetry.md)** - Learn how to use telemetry to monitor your AI applications. ## Next Steps - Explore [Advanced Topics](https://goaisdk.com/docs/advanced.md) for production patterns - Learn about [Building Agents](https://goaisdk.com/docs/agents.md) for autonomous workflows - Review [Foundations](https://goaisdk.com/docs/foundations.md) for core concepts --- # Generating Text > Shows how to generate and stream text with GenerateText and StreamText in Go, covering message-based generation, settings, and error-handling patterns. Canonical URL: https://goaisdk.com/docs/ai-sdk-core/generating-text Documentation index: https://goaisdk.com/llms.txt Large language models (LLMs) can generate text in response to a prompt, which can contain instructions and information to process. For example, you can ask a model to come up with a recipe, draft an email, or summarize a document. The Go AI SDK Core provides two functions to generate text from LLMs: - [`ai.GenerateText()`](#generatetext) - Generates text for a given prompt and model. - [`ai.StreamText()`](#streamtext) - Streams text from a given prompt and model. Advanced LLM features such as [tool calling](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md) and [structured data generation](https://goaisdk.com/docs/ai-sdk-core/generating-structured-data.md) are built on top of text generation. ## GenerateText You can generate text using the [`ai.GenerateText()`](https://goaisdk.com/docs/reference/ai/generate-text.md) function. This function is ideal for non-interactive use cases where you need to write text (e.g. drafting email or summarizing web pages) and for agents that use tools. ```go package main import ( "context" "fmt" "log" "os" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { ctx := context.Background() provider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) model, _ := provider.LanguageModel("gpt-5.2") result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Write a vegetarian lasagna recipe for 4 people.", }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) } ``` You can use more [advanced prompts](https://goaisdk.com/docs/foundations/prompts.md) to generate text with more complex instructions and content: ```go article := "..." // your article text result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, System: "You are a professional writer. " + "You write simple, clear, and concise content.", Prompt: fmt.Sprintf("Summarize the following article in 3-5 sentences: %s", article), }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) ``` ### Result Object The result object of `GenerateText` contains several fields that provide information about the generation: ```go type GenerateTextResult struct { // The generated text Text string // Ordered output content from all completed steps. Includes text, // reasoning, tool calls, tool results, files, sources, and provider // metadata-bearing parts in the same order emitted by the model/tool loop. Content []types.ContentPart // Output contains the parsed output when a WithOutput option was provided. // Type-assert to the concrete type, e.g.: recipe := result.Output.(Recipe) // Nil when no Output option was set. Output any // Tool calls made during generation ToolCalls []types.ToolCall // Tool results from executed tools ToolResults []types.ToolResult // Steps taken during generation (for multi-step tool calling) Steps []types.StepResult // Reason the model finished generating FinishReason types.FinishReason // Token usage information Usage types.Usage // Warnings from the model provider Warnings []types.Warning // Provider-specific metadata ProviderMetadata map[string]interface{} // Raw request/response (for debugging) RawRequest interface{} RawResponse interface{} } ``` ### Accessing Response Headers & Body Sometimes you need access to the full response from the model provider, e.g. to access some provider-specific headers or body content. You can access the raw request and response using the `RawRequest` and `RawResponse` fields: ```go result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Hello", }) if err != nil { log.Fatal(err) } fmt.Printf("Raw request: %+v\n", result.RawRequest) fmt.Printf("Raw response: %+v\n", result.RawResponse) ``` ### OnFinish Callback When using `GenerateText`, you can provide an `OnFinish` callback that is triggered after the last step is finished. It contains the text, usage information, finish reason, and more: ```go result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Invent a new holiday and describe its traditions.", OnFinish: func(ctx context.Context, result *ai.GenerateTextResult, userContext interface{}) { // Your own logic, e.g. for saving the chat history or recording usage fmt.Printf("Text: %s\n", result.Text) fmt.Printf("Finish reason: %s\n", result.FinishReason) fmt.Printf("Usage: %+v\n", result.Usage) fmt.Printf("Steps: %d\n", len(result.Steps)) }, }) ``` ### ToolOrder `ToolOrder` controls the order tools are sent to providers after active-tool filtering, middleware transforms, and `PrepareStep` updates. Listed names are sent first in the provided order; unlisted tools follow alphabetically. ```go result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Inspect the user account.", Tools: tools, ToolOrder: []string{"readProfile", "listOrders"}, }) ``` ### OnStepEnd `OnStepEnd` is the canonical callback name for step completion. `OnStepFinish` remains as a deprecated compatibility alias; when both are set, `OnStepEnd` wins. The same migration applies to typed event callbacks: use `OnStepEndEvent` instead of `OnStepFinishEvent`. ```go result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Use a tool, then summarize.", Tools: tools, StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, OnStepEnd: func(ctx context.Context, step types.StepResult, userContext interface{}) { fmt.Println(step.StepNumber, step.FinishReason, step.Usage) }, }) ``` ## StreamText Depending on your model and prompt, it can take a large language model (LLM) up to a minute to finish generating its response. This delay can be unacceptable for interactive use cases such as chatbots or real-time applications, where users expect immediate responses. The Go AI SDK Core provides the [`ai.StreamText()`](https://goaisdk.com/docs/reference/ai/stream-text.md) function which simplifies streaming text from LLMs: ```go result, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Invent a new holiday and describe its traditions.", }) if err != nil { log.Fatal(err) } defer result.Close() // Stream text chunks as they arrive for chunk := range result.Chunks() { fmt.Print(chunk.Text) } ``` > **Note:** `StreamText` immediately starts streaming and errors become part of the stream. Use the `OnError` callback to log errors. > **Note:** `StreamText` uses backpressure and only generates tokens as they are requested. You need to consume the channel for the stream to finish. ### Stream Result Object The stream result object provides access to the streamed data: ```go // StreamTextResult provides these methods to access streamed data. type streamTextResult interface { // Streaming methods (available immediately) Chunks() <-chan provider.StreamChunk // Channel for receiving chunks Close() error // Close the stream ReadAll() (string, error) // Read all text (blocks until complete) // Accessor methods (available after stream completes) Text() string // Complete generated text Content() []types.ContentPart // Ordered content across all steps FinishReason() types.FinishReason // Reason the model finished Usage() types.Usage // Token usage information ToolCalls() []types.ToolCall // Tool calls made during streaming ToolResults() []types.ToolResult // Tool results from executed tools ContextManagement() interface{} // Context management stats (Anthropic) Err() error // Error if stream failed } ``` ### Standalone Stream Helpers For new code, use `result.Stream()` with the package-level helpers. The result-bound helpers are retained only as deprecated compatibility wrappers. ```go textStream, errStream := ai.ToTextStream(ctx, result.Stream()) for text := range textStream { fmt.Print(text) } if err := <-errStream; err != nil { log.Fatal(err) } ``` Use `ai.ToUIMessageStream(ctx, result.Stream(), ...)` to convert provider stream chunks into UI message chunks. `result.FullStream()` remains as a deprecated alias for `result.Stream()`. ### Channel-Based Streaming The Go AI SDK uses Go channels for streaming, providing a natural and idiomatic way to handle streaming data: ```go result, _ := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Write a story", }) defer result.Close() // Channel closes automatically when done for chunk := range result.Chunks() { if chunk.Type == provider.ChunkTypeError { fmt.Printf("Error: %s\n", chunk.AbortReason) break } fmt.Print(chunk.Text) } ``` ### OnError Callback `StreamText` immediately starts streaming to enable sending data without waiting for the model. Errors become part of the stream and are not returned to prevent servers from crashing. To log errors, you can provide an `OnError` callback that is triggered when an error occurs: ```go result, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Generate text", OnError: func(ctx context.Context, err error) { log.Printf("Stream error: %v", err) // Your error logging logic here }, }) ``` ### OnChunk Callback When using `StreamText`, you can provide an `OnChunk` callback that is triggered for each chunk of the stream. It receives the following chunk types: - `text` - Text content - `tool-call` - Tool call - `tool-result` - Tool result - `finish` - Stream finish ```go result, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Generate text", OnChunk: func(chunk provider.StreamChunk) { // Implement your own logic here if chunk.Type == provider.ChunkTypeText { fmt.Print(chunk.Text) } else if chunk.Type == provider.ChunkTypeToolCall { fmt.Printf("\n[Tool call: %s]\n", chunk.ToolCall.ToolName) } }, }) ``` ### OnFinish Callback When using `StreamText`, you can provide an `OnFinish` callback that is triggered when the stream is finished. It contains the text, usage information, finish reason, and more: ```go result, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Generate text", OnFinish: func(result *ai.StreamTextResult) { // Your own logic, e.g. for saving the chat history or recording usage fmt.Printf("\n\nText: %s\n", result.Text()) fmt.Printf("Finish reason: %s\n", result.FinishReason()) fmt.Printf("Usage: %+v\n", result.Usage()) }, }) ``` ### Accumulating Streamed Text If you need the complete text after streaming, you can use `ReadAll()`: ```go result, _ := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Write a story", }) defer result.Close() // Option 1: Use ReadAll (blocks until complete) fullText, err := result.ReadAll() if err != nil { log.Fatal(err) } fmt.Println(fullText) // Option 2: Manually accumulate var builder strings.Builder for chunk := range result.Chunks() { fmt.Print(chunk.Text) // Display to user builder.WriteString(chunk.Text) // Accumulate } completeText := builder.String() ``` ### Context Cancellation Streaming respects context cancellation, allowing you to stop generation early: ```go ctx, cancel := context.WithCancel(context.Background()) go func() { // Cancel after 5 seconds time.Sleep(5 * time.Second) cancel() }() result, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Write a very long story...", }) if err != nil { log.Fatal(err) } defer result.Close() for chunk := range result.Chunks() { select { case <-ctx.Done(): fmt.Println("\n\nCancelled!") return default: fmt.Print(chunk.Text) } } ``` ## Advanced Features ### Multi-Step Generation with Tools Both `GenerateText` and `StreamText` support multi-step generation with automatic tool execution: ```go weatherTool := types.Tool{ Name: "get_weather", Description: "Get weather for a location", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{"type": "string"}, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { location := input["location"].(string) return map[string]interface{}{ "temperature": 72, "condition": "sunny", "location": location, }, nil }, } maxSteps := 5 // Allow up to 5 steps of tool calling result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "What's the weather in London?", Tools: []types.Tool{weatherTool}, MaxSteps: &maxSteps, }) fmt.Println(result.Text) // Output: The weather in London is sunny with a temperature of 72°F. ``` See [Tools and Tool Calling](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md) for more details. ### Step-by-Step Processing Access intermediate steps in multi-step generations: ```go maxSteps := 10 result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "What's the weather in Tokyo and Paris?", Tools: []types.Tool{weatherTool}, MaxSteps: &maxSteps, OnStepFinish: func(ctx context.Context, step types.StepResult, userContext interface{}) { fmt.Printf("Step %d finished\n", step.StepNumber) for _, toolCall := range step.ToolCalls { fmt.Printf(" Tool: %s\n", toolCall.ToolName) } for _, toolResult := range step.ToolResults { fmt.Printf(" Result: %v\n", toolResult.Result) } }, }) // Access all steps after completion for i, step := range result.Steps { fmt.Printf("Step %d: %d tool calls, %d results\n", i+1, len(step.ToolCalls), len(step.ToolResults)) } ``` ### Sources Some providers such as Perplexity and Google Generative AI include sources in the response. These are web pages that ground the response. You can access them using the `Sources` field of the result: ```go result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "List the top 5 San Francisco news from the past week.", }) if err != nil { log.Fatal(err) } for _, source := range result.Sources { if source.SourceType == "url" { fmt.Printf("ID: %s\n", source.ID) fmt.Printf("Title: %s\n", source.Title) fmt.Printf("URL: %s\n", source.URL) fmt.Println() } } ``` When streaming, sources are available both as `ChunkTypeSource` chunks and via `result.Sources()` after the stream completes: ```go result, _ := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Latest news", }) defer result.Close() for chunk := range result.Chunks() { if chunk.Type == provider.ChunkTypeSource && chunk.SourceContent != nil { if chunk.SourceContent.SourceType == "url" { fmt.Printf("Source: %s - %s\n", chunk.SourceContent.Title, chunk.SourceContent.URL) } } } // Or access all sources after streaming completes for _, source := range result.Sources() { fmt.Printf("Source: %s - %s\n", source.Title, source.URL) } ``` ## Message-Based Generation For chat applications, use message-based generation: ```go import "github.com/digitallysavvy/go-ai/pkg/provider/types" messages := []types.Message{ {Role: types.RoleUser, Content: []types.ContentPart{types.TextContent{Text: "Hi!"}}}, {Role: types.RoleAssistant, Content: []types.ContentPart{types.TextContent{Text: "Hello! How can I help?"}}}, {Role: types.RoleUser, Content: []types.ContentPart{types.TextContent{Text: "Tell me a joke."}}}, } result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Messages: messages, }) fmt.Println(result.Text) ``` ## Settings Control generation behavior with various settings: ```go temperature := 0.8 maxTokens := 500 topP := 0.9 topK := 40 result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Generate creative text", Temperature: &temperature, // Higher = more creative MaxTokens: &maxTokens, // Limit response length TopP: &topP, // Nucleus sampling TopK: &topK, // Top-k sampling StopSequences: []string{"\n\n"}, // Stop at double newline }) ``` See [Settings](https://goaisdk.com/docs/ai-sdk-core/settings.md) for all available options. ## Error Handling Handle errors appropriately: ```go import providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Generate text", }) if err != nil { // Check for specific error types var rateLimitErr *providererrors.RateLimitError if errors.As(err, &rateLimitErr) { fmt.Println("Rate limit exceeded, retry later") return } if providererrors.IsValidationError(err) { fmt.Println("Invalid request parameters") return } log.Fatal(err) } ``` See [Error Handling](https://goaisdk.com/docs/ai-sdk-core/error-handling.md) for comprehensive error handling strategies. ## Best Practices 1. **Use Context**: Always pass context for cancellation and timeouts 2. **Close Streams**: Use `defer result.Close()` for streaming 3. **Handle Errors**: Check errors from both function calls and stream chunks 4. **Choose Wisely**: Use `GenerateText` for short responses, `StreamText` for long ones 5. **Set Limits**: Use `MaxTokens` to control costs and response length 6. **Monitor Usage**: Track token usage for cost management 7. **Cache Results**: Consider caching for repeated queries ## Examples ### Basic Text Generation ```go result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Explain quantum entanglement in simple terms.", }) fmt.Println(result.Text) ``` ### Streaming with Progress ```go result, _ := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Write a long article about AI", }) defer result.Close() tokenCount := 0 for chunk := range result.Chunks() { fmt.Print(chunk.Text) tokenCount += len(strings.Fields(chunk.Text)) fmt.Printf("\r[Tokens: ~%d]", tokenCount) } fmt.Println("\nComplete!") ``` ### Concurrent Generation ```go prompts := []string{ "Explain photosynthesis", "Explain gravity", "Explain evolution", } var wg sync.WaitGroup results := make([]string, len(prompts)) for i, prompt := range prompts { wg.Add(1) go func(idx int, p string) { defer wg.Done() result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: p, }) results[idx] = result.Text }(i, prompt) } wg.Wait() for i, result := range results { fmt.Printf("%d: %s\n\n", i+1, result) } ``` ## Custom Content Types Some providers return ordered content that carries provider-specific metadata. The Go AI SDK preserves that metadata on output content as `ProviderMetadata`; when the content is replayed to a provider as response messages, it is converted to provider-facing `ProviderOptions`, matching the TypeScript SDK. This applies to standard content such as text, reasoning, tool-call, tool-result, and tool-error parts, and to provider-specific content that does not map to standard text, reasoning, or tool call types. The Go AI SDK represents provider-specific parts as `CustomContent` and `ReasoningFileContent`. ### CustomContent `CustomContent` wraps provider-specific response parts that have no standard mapping (e.g., XAI citations, OpenAI compaction events): ```go import "github.com/digitallysavvy/go-ai/pkg/provider/types" // CustomContent is emitted by providers for unknown/provider-specific parts type CustomContent struct { Kind string // e.g. "xai.citation", "openai.compaction" ProviderOptions map[string]interface{} // input: provider-specific options to forward ProviderMetadata json.RawMessage // output: raw JSON from provider } ``` - `Kind` identifies the content type using the format `"{provider}.{type}"` (e.g., `"xai.citation"`) - `ProviderMetadata` carries the raw JSON returned by the provider (output direction) - `ProviderOptions` carries provider-specific options keyed by provider name (input direction) ### ReasoningFileContent `ReasoningFileContent` carries reasoning output as a binary file (e.g., PDFs from Google models): ```go // ReasoningFileContent carries reasoning output as a binary file type ReasoningFileContent struct { MediaType string // e.g. "application/pdf", "image/png" Data []byte // auto base64-encodes in JSON marshaling ProviderOptions map[string]interface{} // input: provider-specific options to forward ProviderMetadata json.RawMessage // output: raw JSON from provider } ``` - `MediaType` is the IANA media type of the file - `Data` holds raw bytes; Go's `encoding/json` automatically handles base64 encoding/decoding - `ProviderOptions` and `ProviderMetadata` serve the same input/output roles as on `CustomContent` ### Stream Chunk Handling Both content types flow through stream chunks via `OnChunk`. Use `ChunkTypeCustom` and `ChunkTypeReasoningFile` to detect them: ```go import "github.com/digitallysavvy/go-ai/pkg/provider" result, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Analyze this data with reasoning.", OnChunk: func(chunk provider.StreamChunk) { switch chunk.Type { case provider.ChunkTypeText: fmt.Print(chunk.Text) case provider.ChunkTypeCustom: // Provider-specific content fmt.Printf("Custom content [%s]: %s\n", chunk.CustomContent.Kind, string(chunk.CustomContent.ProviderMetadata)) case provider.ChunkTypeReasoningFile: // Reasoning output as file fmt.Printf("Reasoning file: %s (%d bytes)\n", chunk.ReasoningFileContent.MediaType, len(chunk.ReasoningFileContent.Data)) } }, }) if err != nil { log.Fatal(err) } defer result.Close() for range result.Chunks() { } ``` ### Multi-Turn Behavior In multi-turn conversations, `CustomContent` and `ReasoningFileContent` appear in assistant messages within the conversation history: - **`ProviderOptions`** is forwarded back to the provider in subsequent requests. This allows provider-specific state (e.g., cached references) to round-trip correctly. - **`ProviderMetadata`** is output-only and is **not** re-sent to the provider. It is preserved in conversation history for logging and debugging. ## Next Steps - Learn about [Generating Structured Data](https://goaisdk.com/docs/ai-sdk-core/generating-structured-data.md) - Explore [Tools and Tool Calling](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md) - Build [Agents](https://goaisdk.com/docs/agents/overview.md) ## See Also - [Prompts](https://goaisdk.com/docs/foundations/prompts.md) - [Streaming](https://goaisdk.com/docs/foundations/streaming.md) - [Settings](https://goaisdk.com/docs/ai-sdk-core/settings.md) - [Error Handling](https://goaisdk.com/docs/ai-sdk-core/error-handling.md) - [API Reference: GenerateText](https://goaisdk.com/docs/reference/ai/generate-text.md) - [API Reference: StreamText](https://goaisdk.com/docs/reference/ai/stream-text.md) --- # Generating Structured Data > Explains generating structured JSON output with GenerateObject and StreamObject in Go, including the SchemaFor helper, output strategies, and schema naming. Canonical URL: https://goaisdk.com/docs/ai-sdk-core/generating-structured-data Documentation index: https://goaisdk.com/llms.txt While text generation can be useful, your use case will likely call for generating structured data. For example, you might want to extract information from text, classify data, or generate synthetic data. Many language models are capable of generating structured data, often defined as using "JSON modes" or "tools". However, you need to manually provide schemas and then validate the generated data as LLMs can produce incorrect or incomplete structured data. The Go AI SDK standardizes structured output via the `Output` option on [`ai.GenerateText()`](https://goaisdk.com/docs/reference/ai/generate-text.md) and [`ai.StreamText()`](https://goaisdk.com/docs/reference/ai/stream-text.md). Five output factories cover all common patterns: | Factory | Use case | |---|---| | `ai.TextOutput()` | Plain text (default) | | `ai.ObjectOutput[T]()` | Typed struct from JSON | | `ai.ArrayOutput[T]()` | Slice of typed structs from JSON | | `ai.ChoiceOutput[T]()` | Enum/classification | | `ai.JSONOutput()` | Untyped `interface{}` JSON | > **Note:** The older `ai.GenerateObject()` and `ai.StreamObject()` functions are **deprecated**. Use the `Output` option instead. ## Structured Output with GenerateText Use `GenerateText` with an `Output` factory to get a fully typed, validated result in a single call. The SDK automatically sets the correct `ResponseFormat` and parses the model's response. ### Object Output ```go package main import ( "context" "fmt" "log" "os" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) type Recipe struct { Name string `json:"name"` Ingredients []string `json:"ingredients"` Steps []string `json:"steps"` } func main() { ctx := context.Background() provider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) model, _ := provider.LanguageModel("gpt-6-astra") result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Generate a lasagna recipe.", Output: ai.ObjectOutput[Recipe](ai.ObjectOutputOptions{ Schema: ai.SchemaFor[Recipe](), Name: "recipe", Description: "A complete recipe with name, ingredients, and steps", }), }) if err != nil { log.Fatal(err) } // Type-assert the Output field to get your typed struct recipe := result.Output.(Recipe) fmt.Printf("Recipe: %s\n", recipe.Name) fmt.Printf("Steps: %v\n", recipe.Steps) } ``` ### Array Output ```go type Hero struct { Name string `json:"name"` Class string `json:"class"` Description string `json:"description"` } result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Generate 3 hero descriptions for a fantasy RPG.", Output: ai.ArrayOutput[Hero](ai.ArrayOutputOptions[Hero]{ ElementSchema: ai.SchemaFor[Hero](), Name: "heroes", }), }) if err != nil { log.Fatal(err) } heroes := result.Output.([]Hero) for _, h := range heroes { fmt.Printf("%s (%s): %s\n", h.Name, h.Class, h.Description) } ``` ### Choice Output ```go type Sentiment string const ( Positive Sentiment = "positive" Negative Sentiment = "negative" Neutral Sentiment = "neutral" ) result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: `Classify the sentiment of: "I love this product!"`, Output: ai.ChoiceOutput[Sentiment](ai.ChoiceOutputOptions[Sentiment]{ Options: []Sentiment{Positive, Negative, Neutral}, }), }) if err != nil { log.Fatal(err) } sentiment := result.Output.(Sentiment) fmt.Printf("Sentiment: %s\n", sentiment) // positive ``` ### JSON Output (Untyped) ```go result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Return some arbitrary structured data about the Go language.", Output: ai.JSONOutput(ai.JSONOutputOptions{}), }) if err != nil { log.Fatal(err) } data := result.Output.(map[string]interface{}) fmt.Printf("Data: %v\n", data) ``` ## Structured Streaming with StreamText Use `StreamText` with an `Output` factory to receive structured data as it streams. Call `result.PartialOutput()` at any time to get the most recently parsed partial value. ```go type Notification struct { Name string `json:"name"` Message string `json:"message"` } result, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Generate a system notification.", Output: ai.ObjectOutput[Notification](ai.ObjectOutputOptions{ Schema: ai.SchemaFor[Notification](), }), }) if err != nil { log.Fatal(err) } defer result.Close() for chunk := range result.Chunks() { if chunk.Type == provider.ChunkTypeText { // Read incremental parsed value during streaming partial := result.PartialOutput() if n, ok := partial.(Notification); ok { fmt.Printf("Partial name: %s\n", n.Name) } } } // Full text after stream completes fmt.Printf("Raw JSON: %s\n", result.Text()) ``` ### Element Streaming for Arrays When using `ArrayOutput`, you can receive fully validated array elements one at a time as they stream: ```go streamResult, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Generate 5 notification objects.", Output: arrayOut, // ArrayOutput[Notification] }) if err != nil { log.Fatal(err) } elements := ai.ElementStreamWithOutput(streamResult, arrayOut) for elem := range elements { fmt.Printf("Notification %d: %s\n", elem.Index, elem.Element.Name) } // Check for a stream-level error after consuming all elements if err := streamResult.Err(); err != nil { log.Printf("Error: %v", err) } ``` ## SchemaFor Helper `ai.SchemaFor[T]()` generates a JSON Schema from a Go struct's field types and `json` tags. Use it instead of writing schemas by hand: ```go type Address struct { Street string `json:"street"` City string `json:"city"` Country string `json:"country"` } schema := ai.SchemaFor[Address]() // Result: {"type":"object","properties":{"street":{"type":"string"},...},"required":["street","city","country"]} ``` Fields with `omitempty` in their json tag become optional (not included in `required`). Fields tagged `json:"-"` are excluded. --- ## Legacy API (Deprecated) > **Deprecated:** The functions below still work but are deprecated. Prefer `GenerateText` / `StreamText` with `Output` instead. ## Generate Object The `ai.GenerateObject()` function generates structured data from a prompt. The schema is also used to validate the generated data, ensuring type safety and correctness. ```go package main import ( "context" "encoding/json" "fmt" "log" "os" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/providers/openai" "github.com/digitallysavvy/go-ai/pkg/schema" ) type Recipe struct { Name string `json:"name"` Ingredients []struct { Name string `json:"name"` Amount string `json:"amount"` } `json:"ingredients"` Steps []string `json:"steps"` } func main() { ctx := context.Background() provider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) model, _ := provider.LanguageModel("gpt-6-astra") // Define JSON Schema recipeSchema := map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "recipe": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "name": map[string]interface{}{"type": "string"}, "ingredients": map[string]interface{}{ "type": "array", "items": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "name": map[string]interface{}{"type": "string"}, "amount": map[string]interface{}{"type": "string"}, }, }, }, "steps": map[string]interface{}{ "type": "array", "items": map[string]interface{}{"type": "string"}, }, }, }, }, "required": []string{"recipe"}, } result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: schema.NewSimpleJSONSchema(recipeSchema), Prompt: "Generate a lasagna recipe.", }) if err != nil { log.Fatal(err) } // result.Object is already unmarshaled; re-marshal it to decode into a typed struct objBytes, err := json.Marshal(result.Object) if err != nil { log.Fatal(err) } var recipe Recipe if err := json.Unmarshal(objBytes, &recipe); err != nil { log.Fatal(err) } fmt.Printf("Recipe: %s\n", recipe.Name) fmt.Printf("Ingredients: %+v\n", recipe.Ingredients) fmt.Printf("Steps: %+v\n", recipe.Steps) } ``` ### Accessing Response Headers & Body Sometimes you need access to the full response from the model provider, e.g. to access some provider-specific headers or body content. You can access the raw response headers and body using the `Response` field: ```go result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: schema, Prompt: "Generate a recipe.", }) if err != nil { log.Fatal(err) } fmt.Printf("Headers: %+v\n", result.Response.Headers) fmt.Printf("Body: %+v\n", result.Response.Body) ``` ## Stream Object Given the added complexity of returning structured data, model response time can be unacceptable for your interactive use case. With the [`ai.StreamObject()`](https://goaisdk.com/docs/reference/ai/stream-object.md) function, you can receive partial objects as they are parsed during generation via the `OnChunk` callback. Unlike `StreamText`, `ai.StreamObject()` blocks until generation completes and returns the final `*GenerateObjectResult` directly — it does not return a channel-based stream. ```go result, err := ai.StreamObject(ctx, ai.StreamObjectOptions{ Model: model, Schema: schema, Prompt: "Generate a lasagna recipe.", OnChunk: func(partialObject interface{}) { fmt.Printf("Partial object: %v\n", partialObject) }, }) if err != nil { log.Fatal(err) } // Get final complete object finalObject := result.Object fmt.Printf("Final object: %v\n", finalObject) ``` ### OnError Callback `StreamObject` surfaces stream-level errors (distinct from schema parse/validation errors) via the `OnError` callback, which is called before the function returns its final error: ```go result, err := ai.StreamObject(ctx, ai.StreamObjectOptions{ Model: model, Schema: schema, Prompt: "Generate data", OnError: func(ctx context.Context, err error) { log.Printf("Stream error: %v", err) // Your error logging logic here }, }) ``` ## Output Strategy You can use both functions with different output strategies: `object`, `array`, `enum`, or `no-schema`. ### Object The default output strategy is `object`, which returns the generated data as an object. You don't need to specify the output strategy if you want to use the default. ```go result, _ := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: schema, Prompt: "Generate a person profile.", // OutputMode: "object", // default, can be omitted }) ``` ### Array If you want to generate an array of objects, you can set the output strategy to `array`. When you use the `array` output strategy, the schema specifies the shape of an array element. With `StreamObject`, you can receive the partial array through `OnChunk` as it is generated: ```go // Schema for a single hero heroSchema := map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "name": map[string]interface{}{"type": "string"}, "class": map[string]interface{}{ "type": "string", "description": "Character class, e.g. warrior, mage, or thief.", }, "description": map[string]interface{}{"type": "string"}, }, "required": []string{"name", "class", "description"}, } result, _ := ai.StreamObject(ctx, ai.StreamObjectOptions{ Model: model, OutputMode: "array", Schema: schema.NewSimpleJSONSchema(heroSchema), Prompt: "Generate 3 hero descriptions for a fantasy role playing game.", OnChunk: func(partialObject interface{}) { fmt.Printf("Partial array so far: %v\n", partialObject) }, }) // The final array is available once StreamObject returns for _, hero := range result.Array { fmt.Printf("Hero: %v\n", hero) } ``` > **Note:** To receive fully validated elements one at a time as they complete, use the non-deprecated `StreamText` with `ArrayOutput[T]()` and `ai.ElementStreamWithOutput()` shown above under [Element Streaming for Arrays](#element-streaming-for-arrays). ### Enum If you want to generate a specific enum value, e.g. for classification tasks, you can set the output strategy to `enum` and provide a list of possible values in the `EnumValues` parameter. > **Note:** Enum output is only available with `GenerateObject`. ```go result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, OutputMode: "enum", EnumValues: []string{"action", "comedy", "drama", "horror", "sci-fi"}, Prompt: "Classify the genre of this movie plot: " + "\"A group of astronauts travel through a wormhole in search of a " + "new habitable planet for humanity.\"", }) if err != nil { log.Fatal(err) } // result.EnumValue contains the selected enum value fmt.Printf("Genre: %s\n", result.EnumValue) // Output: sci-fi ``` ### No Schema In some cases, you might not want to use a schema, for example when the data is a dynamic user request. You can use the `OutputMode` setting to set the output format to `no-schema` in those cases and omit the schema parameter. ```go result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, OutputMode: "no-schema", Prompt: "Generate a lasagna recipe.", }) if err != nil { log.Fatal(err) } // result.Object contains the parsed value without schema validation fmt.Printf("Object: %v\n", result.Object) ``` ## Schema Name and Description You can optionally specify a name and description for the schema. These are used by some providers for additional LLM guidance, e.g. via tool or schema name. ```go recipeSchema := map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "name": map[string]interface{}{"type": "string"}, "ingredients": map[string]interface{}{ "type": "array", "items": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "name": map[string]interface{}{"type": "string"}, "amount": map[string]interface{}{"type": "string"}, }, }, }, "steps": map[string]interface{}{ "type": "array", "items": map[string]interface{}{"type": "string"}, }, }, "required": []string{"name", "ingredients", "steps"}, } result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, SchemaName: "Recipe", SchemaDescription: "A recipe for a dish.", Schema: schema.NewSimpleJSONSchema(recipeSchema), Prompt: "Generate a lasagna recipe.", }) ``` ## Error Handling When `GenerateObject` cannot generate a valid object, it returns an error. This error occurs when the AI provider fails to generate a parsable object that conforms to the schema. It can arise due to the following reasons: - The model failed to generate a response - The model generated a response that could not be parsed - The model generated a response that could not be validated against the schema ```go import providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: schema, Prompt: "Generate data", }) if err != nil { // Check for specific error types var noObjectErr *ai.NoObjectGeneratedError if errors.As(err, &noObjectErr) { fmt.Println("No object was generated:", noObjectErr.Message) } else if providererrors.IsValidationError(err) { fmt.Println("Object validation failed") } else { fmt.Printf("Error: %v\n", err) } return } ``` ## Advanced Usage ### Using Go Structs for Type Safety For better type safety, define Go structs and generate schemas from them: ```go type Person struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email"` Address Address `json:"address"` } type Address struct { Street string `json:"street"` City string `json:"city"` Country string `json:"country"` } // Generate a JSON schema directly from the struct's json tags result, _ := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: ai.SchemaFor[Person](), Prompt: "Generate a person profile.", }) // result.Object is already unmarshaled; re-marshal it to decode into a typed struct objBytes, _ := json.Marshal(result.Object) var person Person json.Unmarshal(objBytes, &person) fmt.Printf("Person: %+v\n", person) ``` ### Information Extraction Extract structured information from unstructured text: ```go articleSchema := map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "title": map[string]interface{}{"type": "string"}, "author": map[string]interface{}{"type": "string"}, "date": map[string]interface{}{"type": "string"}, "summary": map[string]interface{}{"type": "string"}, "keywords": map[string]interface{}{ "type": "array", "items": map[string]interface{}{"type": "string"}, }, }, } article := "..." // your article text result, _ := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: schema.NewSimpleJSONSchema(articleSchema), Prompt: fmt.Sprintf("Extract key information from this article: %s", article), }) // result.Object is already unmarshaled; re-marshal it to decode into a typed value objBytes, _ := json.Marshal(result.Object) var extractedInfo map[string]interface{} json.Unmarshal(objBytes, &extractedInfo) fmt.Printf("Extracted: %+v\n", extractedInfo) ``` ### Classification Use enum mode for classification tasks: ```go result, _ := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, OutputMode: "enum", EnumValues: []string{"positive", "negative", "neutral"}, Prompt: "Classify the sentiment of this review: \"This product is amazing!\"", }) fmt.Printf("Sentiment: %s\n", result.EnumValue) // Output: positive ``` ### Synthetic Data Generation Generate synthetic test data: ```go userSchema := map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "username": map[string]interface{}{"type": "string"}, "email": map[string]interface{}{"type": "string"}, "age": map[string]interface{}{"type": "integer", "minimum": 18, "maximum": 80}, "role": map[string]interface{}{"type": "string", "enum": []string{"admin", "user", "moderator"}}, }, } result, _ := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, OutputMode: "array", Schema: schema.NewSimpleJSONSchema(userSchema), Prompt: "Generate 5 realistic test users for a web application.", }) // For array mode, elements are available in result.Array for i, user := range result.Array { fmt.Printf("User %d: %+v\n", i+1, user) } ``` ## Callbacks ### OnFinish Callback Monitor when object generation completes: ```go result, _ := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: schema, Prompt: "Generate data", OnFinish: func(ctx context.Context, result *ai.GenerateObjectResult, userContext interface{}) { fmt.Printf("Object generated\n") fmt.Printf("Usage: %+v\n", result.Usage) fmt.Printf("Finish reason: %s\n", result.FinishReason) }, }) ``` ### OnChunk Callback (Streaming) Process partial objects as they arrive during streaming (note: `StreamObject` blocks until generation completes; `OnChunk` is a callback, not a channel): ```go result, _ := ai.StreamObject(ctx, ai.StreamObjectOptions{ Model: model, Schema: schema, Prompt: "Generate data", OnChunk: func(partialObject interface{}) { fmt.Printf("Partial: %v\n", partialObject) }, }) ``` ## Validation The Go AI SDK automatically validates generated objects against your schema. If validation fails, an error is returned: ```go import providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: schema, Prompt: "Generate data", }) if err != nil { if providererrors.IsValidationError(err) { fmt.Println("Generated object does not match schema") // Handle validation failure } } ``` ## Best Practices 1. **Define Clear Schemas**: Provide detailed schemas with descriptions for better results 2. **Use Enums for Classification**: Use enum mode for fixed-choice classification tasks 3. **Stream Long Objects**: Use `StreamObject` for large or complex objects 4. **Handle Errors**: Always check for validation and generation errors 5. **Use Type-Safe Structs**: Define Go structs for compile-time type safety 6. **Add Descriptions**: Include field descriptions in your schema to guide the model 7. **Test Schemas**: Validate your schemas work correctly before production use ## Common Patterns ### Multi-Entity Extraction Extract multiple entities from text: ```go entitiesSchema := map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "people": map[string]interface{}{ "type": "array", "items": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "name": map[string]interface{}{"type": "string"}, "role": map[string]interface{}{"type": "string"}, }, }, }, "organizations": map[string]interface{}{ "type": "array", "items": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "name": map[string]interface{}{"type": "string"}, "type": map[string]interface{}{"type": "string"}, }, }, }, "locations": map[string]interface{}{ "type": "array", "items": map[string]interface{}{"type": "string"}, }, }, } text := "Apple CEO Tim Cook announced a new initiative in Cupertino..." result, _ := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: schema.NewSimpleJSONSchema(entitiesSchema), Prompt: fmt.Sprintf("Extract all people, organizations, and locations from: %s", text), }) ``` ### Form Data Extraction Extract form data from images or documents: ```go formSchema := map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "name": map[string]interface{}{"type": "string"}, "email": map[string]interface{}{"type": "string"}, "phone": map[string]interface{}{"type": "string"}, "address": map[string]interface{}{"type": "string"}, "dob": map[string]interface{}{"type": "string"}, }, } result, _ := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: schema.NewSimpleJSONSchema(formSchema), Prompt: "Extract form data from this document: ...", }) ``` ## Next Steps - Learn about [Tools and Tool Calling](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md) - Explore [Embeddings](https://goaisdk.com/docs/ai-sdk-core/embeddings.md) - See [Examples](https://github.com/digitallysavvy/go-ai/tree/main/examples) ## See Also - [Generating Text](https://goaisdk.com/docs/ai-sdk-core/generating-text.md) - [Settings](https://goaisdk.com/docs/ai-sdk-core/settings.md) - [Error Handling](https://goaisdk.com/docs/ai-sdk-core/error-handling.md) - [API Reference: GenerateObject](https://goaisdk.com/docs/reference/ai/generate-object.md) - [API Reference: StreamObject](https://goaisdk.com/docs/reference/ai/stream-object.md) --- # Tool Calling > Covers multi-step tool calling in the Go AI SDK: strict mode, response messages, tool choice, execution options, timeouts, and packaging complex tools. Canonical URL: https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling Documentation index: https://goaisdk.com/llms.txt As covered under [Foundations](https://goaisdk.com/docs/foundations/tools.md), [tools](https://goaisdk.com/docs/foundations/tools.md) are objects that can be called by the model to perform a specific task. Go AI SDK Core tools contain several core elements: - **`Name`**: The name of the tool - **`Description`**: An optional description of the tool that can influence when the tool is picked - **`Parameters`**: A JSON schema that defines the input parameters. The schema is consumed by the LLM and also used to validate the LLM tool calls - **`Execute`**: An optional function that is called with the inputs from the tool call. It is optional because you might want to forward tool calls to the client or to a queue instead of executing them in the same process - **`Strict`**: _(optional)_ Enables strict tool calling when supported by the provider The `Tools` parameter of `GenerateText` and `StreamText` is a slice of tools: ```go package main import ( "context" "fmt" "log" "math/rand" "os" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider/types" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { ctx := context.Background() provider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) model, _ := provider.LanguageModel("gpt-5.2") weatherTool := types.Tool{ Name: "weather", Description: "Get the weather in a location", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{ "type": "string", "description": "The location to get the weather for", }, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { location := input["location"].(string) return map[string]interface{}{ "location": location, "temperature": 72 + rand.Intn(21) - 10, }, nil }, } maxSteps := 5 result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{weatherTool}, MaxSteps: &maxSteps, Prompt: "What is the weather in San Francisco?", }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) } ``` > **Note:** When a model uses a tool, it is called a "tool call" and the output of the tool is called a "tool result". ## Strict Mode When enabled, language model providers that support strict tool calling will only generate tool calls that are valid according to your defined `Parameters` schema. This increases the reliability of tool calling. However, not all schemas may be supported in strict mode, and what is supported depends on the specific provider. By default, strict mode is disabled. You can enable it per-tool by setting `Strict: types.BoolPtr(true)`: ```go weatherTool := types.Tool{ Name: "weather", Description: "Get the weather in a location", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{"type": "string"}, }, "required": []string{"location"}, }, Strict: types.BoolPtr(true), // Enable strict validation for this tool Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { // ... return nil, nil }, } ``` > **Note:** Not all providers or models support strict mode. For those that do not, this option is ignored. ## Multi-Step Calls With the `MaxSteps` setting, you can enable multi-step calls in `GenerateText` and `StreamText`. When `MaxSteps` is set and the model generates a tool call, the AI SDK will trigger a new generation passing in the tool result until there are no further tool calls or the maximum steps is reached. > **Note:** The step limit is only evaluated when the last step contains tool results. By default, when you use `GenerateText` or `StreamText`, it triggers a single generation. This works well for many use cases where you can rely on the model's training data to generate a response. However, when you provide tools, the model now has the choice to either generate a normal text response, or generate a tool call. If the model generates a tool call, its generation is complete and that step is finished. You may want the model to generate text after the tool has been executed, either to summarize the tool results in the context of the user's query. In many cases, you may also want the model to use multiple tools in a single response. This is where multi-step calls come in. ### Example In the following example, there are two steps: 1. **Step 1** 1. The prompt `"What is the weather in San Francisco?"` is sent to the model 2. The model generates a tool call 3. The tool call is executed 2. **Step 2** 1. The tool result is sent to the model 2. The model generates a response considering the tool result ```go maxSteps := 5 result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{ { Name: "weather", Description: "Get the weather in a location", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{ "type": "string", "description": "The location to get the weather for", }, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { location := input["location"].(string) return map[string]interface{}{ "location": location, "temperature": 72 + rand.Intn(21) - 10, }, nil }, }, }, MaxSteps: &maxSteps, // Stop after a maximum of 5 steps if tools were called Prompt: "What is the weather in San Francisco?", }) ``` > **Note:** You can use `StreamText` in a similar way. ### Steps To access intermediate tool calls and results, you can use the `Steps` field in the result object or the `OnFinish` callback. It contains all the text, tool calls, tool results, and more from each step. #### Example: Extract tool results from all steps ```go maxSteps := 10 result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, MaxSteps: &maxSteps, Tools: tools, Prompt: "What's the weather in Tokyo and Paris?", }) if err != nil { log.Fatal(err) } // Extract all tool calls from the steps var allToolCalls []types.ToolCall for _, step := range result.Steps { allToolCalls = append(allToolCalls, step.ToolCalls...) } fmt.Printf("Total tool calls: %d\n", len(allToolCalls)) ``` ### OnStepFinish Callback When using `GenerateText` or `StreamText`, you can provide an `OnStepFinish` callback that is triggered when a step is finished, i.e. all text deltas, tool calls, and tool results for the step are available. When you have multiple steps, the callback is triggered for each step. ```go maxSteps := 5 result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: tools, MaxSteps: &maxSteps, Prompt: "What's the weather in multiple cities?", OnStepFinish: func(ctx context.Context, step types.StepResult, userContext interface{}) { // Your own logic, e.g. for saving the chat history or recording usage fmt.Printf("Step %d finished\n", step.StepNumber) fmt.Printf(" Text: %s\n", step.Text) fmt.Printf(" Tool calls: %d\n", len(step.ToolCalls)) fmt.Printf(" Tool results: %d\n", len(step.ToolResults)) fmt.Printf(" Finish reason: %s\n", step.FinishReason) fmt.Printf(" Usage: %+v\n", step.Usage) }, }) ``` ## Response Messages Adding the generated assistant and tool messages to your conversation history is a common task, especially if you are using multi-step tool calls. Both `GenerateText` and `StreamText` have a `Response.Messages` property that you can use to add the assistant and tool messages to your conversation history. It is also available in the `OnFinish` callback of `StreamText`. The `Response.Messages` field contains a slice of `Message` objects that you can add to your conversation history: ```go import "github.com/digitallysavvy/go-ai/pkg/provider/types" maxSteps := 5 messages := []types.Message{ {Role: types.RoleUser, Content: []types.ContentPart{types.TextContent{Text: "What's the weather?"}}}, } result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Messages: messages, Tools: tools, MaxSteps: &maxSteps, }) if err != nil { log.Fatal(err) } // Add the response messages from the last step to your conversation history if len(result.Steps) > 0 { lastStep := result.Steps[len(result.Steps)-1] messages = append(messages, lastStep.ResponseMessages...) } // Continue the conversation result2, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Messages: messages, Tools: tools, MaxSteps: &maxSteps, }) ``` ## Tool Choice You can use the `ToolChoice` setting to influence when a tool is selected. It supports the following settings: - `auto` (default): the model can choose whether and which tools to call - `required`: the model must call a tool. It can choose which tool to call - `none`: the model must not call tools - `tool`: the model must call the specified tool ```go // Auto: Let the model decide (default) result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{weatherTool}, ToolChoice: types.ToolChoice{Type: "auto"}, Prompt: "What's the weather?", }) // Required: Force the model to call a tool result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{weatherTool}, ToolChoice: types.ToolChoice{Type: "required"}, Prompt: "What's the weather in San Francisco?", }) // None: Prevent the model from using tools result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{weatherTool}, ToolChoice: types.ToolChoice{Type: "none"}, Prompt: "Tell me about San Francisco.", }) // Specific: Force the model to use a specific tool result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{weatherTool, calculatorTool}, ToolChoice: types.ToolChoice{ Type: "tool", ToolName: "weather", }, Prompt: "What's the weather?", }) ``` ## Tool Execution Options When tools are called, they receive the context and input parameters. You can use the context for cancellation, timeouts, and passing request-scoped values. ### Context Cancellation The abort signals from `GenerateText` and `StreamText` are forwarded to the tool execution via the context: ```go // Create a cancellable context ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() weatherTool := types.Tool{ Name: "weather", Description: "Get the weather in a location", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{"type": "string"}, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { location := input["location"].(string) // Create HTTP request with context for cancellation req, err := http.NewRequestWithContext( ctx, "GET", fmt.Sprintf("https://api.weatherapi.com/v1/current.json?q=%s", location), nil, ) if err != nil { return nil, err } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() // Parse and return weather data var data map[string]interface{} json.NewDecoder(resp.Body).Decode(&data) return data, nil }, } result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{weatherTool}, Prompt: "What's the weather in San Francisco?", }) ``` ## Tool Timeouts Set timeouts for tool execution to prevent long-running tools from blocking the step loop. The Go AI SDK provides both global and per-tool timeout configuration. ### Global Tool Timeout Set a default timeout for all tools using `ToolMs`: ```go import ( "time" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider/types" ) fiveSeconds := 5 * time.Second result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "What's the weather?", Tools: []types.Tool{weatherTool}, StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, Timeout: &ai.TimeoutConfig{ ToolMs: &fiveSeconds, // 5 second timeout for all tools }, }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) ``` ### Per-Tool Timeout Override timeouts for individual tools using the `Tools` map. Per-tool timeouts take precedence over the global `ToolMs`: ```go import "time" fiveSeconds := 5 * time.Second result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Search for weather and news", Tools: []types.Tool{weatherTool, searchTool}, StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, Timeout: &ai.TimeoutConfig{ ToolMs: &fiveSeconds, // Default: 5s for all tools Tools: map[string]time.Duration{ "get_weather": 5 * time.Second, // 5s for weather "search_web": 15 * time.Second, // 15s for web search }, }, }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) ``` ### Timeout Resolution Order The `GetToolTimeout()` helper resolves the effective timeout for a tool name using this priority: 1. **Per-tool override** (`Tools[toolName]`) — highest priority 2. **Global default** (`ToolMs`) — used if no per-tool entry exists 3. **No timeout** — if neither is set, the tool runs without a timeout ```go timeout := &ai.TimeoutConfig{ ToolMs: &fiveSeconds, Tools: map[string]time.Duration{ "slow_tool": 30 * time.Second, }, } timeout.GetToolTimeout("slow_tool") // 30s (per-tool override) timeout.GetToolTimeout("fast_tool") // 5s (global default) timeout.GetToolTimeout("another_tool") // 5s (global default) ``` ### Timeout Behavior When a tool exceeds its timeout, the tool's context is cancelled and the `Execute` function receives a `context.DeadlineExceeded` error. Tools that respect context cancellation will stop immediately: ```go Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { // Long-running operation that respects context req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil) if err != nil { return nil, err } resp, err := http.DefaultClient.Do(req) if err != nil { // Returns context.DeadlineExceeded if the tool timeout fires return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() // Process response... return result, nil }, ``` > **Tip:** Always use `http.NewRequestWithContext(ctx, ...)` and pass the context to external calls so they respect tool timeouts and cancellation. ## Complex Tool Examples ### Multiple Tools Using multiple tools together: ```go calculatorTool := types.Tool{ Name: "calculator", Description: "Perform mathematical calculations", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "operation": map[string]interface{}{ "type": "string", "enum": []string{"add", "subtract", "multiply", "divide"}, }, "a": map[string]interface{}{"type": "number"}, "b": map[string]interface{}{"type": "number"}, }, "required": []string{"operation", "a", "b"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { op := input["operation"].(string) a := input["a"].(float64) b := input["b"].(float64) var result float64 switch op { case "add": result = a + b case "subtract": result = a - b case "multiply": result = a * b case "divide": if b == 0 { return nil, fmt.Errorf("division by zero") } result = a / b } return map[string]interface{}{"result": result}, nil }, } searchTool := types.Tool{ Name: "web_search", Description: "Search the web for information", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "query": map[string]interface{}{ "type": "string", "description": "The search query", }, }, "required": []string{"query"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { query := input["query"].(string) // Call search API return map[string]interface{}{ "results": []string{ "Result 1 for: " + query, "Result 2 for: " + query, }, }, nil }, } maxSteps := 5 result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{calculatorTool, searchTool, weatherTool}, Prompt: "Search for the population of Tokyo, then calculate what " + "percentage it is of Japan's total population (125 million)", MaxSteps: &maxSteps, }) fmt.Println(result.Text) ``` ### Tool with Error Handling Robust tool implementation with comprehensive error handling: ```go weatherTool := types.Tool{ Name: "get_weather", Description: "Get current weather for a location", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{ "type": "string", "description": "City name", }, "units": map[string]interface{}{ "type": "string", "enum": []string{"celsius", "fahrenheit"}, "default": "celsius", }, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { // Validate input location, ok := input["location"].(string) if !ok || location == "" { return nil, fmt.Errorf("location must be a non-empty string") } units := "celsius" if u, ok := input["units"].(string); ok { units = u } // Check for context cancellation select { case <-ctx.Done(): return nil, ctx.Err() default: } // Call weather API apiKey := os.Getenv("WEATHER_API_KEY") url := fmt.Sprintf( "https://api.weatherapi.com/v1/current.json?key=%s&q=%s", apiKey, location, ) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, fmt.Errorf("failed to fetch weather: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("weather API returned status: %d", resp.StatusCode) } var data map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { return nil, fmt.Errorf("failed to decode response: %w", err) } // Extract and format weather data current := data["current"].(map[string]interface{}) temp := current["temp_c"].(float64) if units == "fahrenheit" { temp = temp*9/5 + 32 } return map[string]interface{}{ "location": location, "temperature": temp, "units": units, "condition": current["condition"].(map[string]interface{})["text"], }, nil }, } ``` ### Tool with Streaming Progress For long-running operations, you can provide progress updates: ```go dataProcessingTool := types.Tool{ Name: "process_data", Description: "Process a large dataset", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "dataset_id": map[string]interface{}{"type": "string"}, }, "required": []string{"dataset_id"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { datasetID := input["dataset_id"].(string) // Simulate long-running processing with progress updates total := 100 for i := 0; i < total; i++ { // Check for cancellation select { case <-ctx.Done(): return nil, ctx.Err() default: } // Simulate work time.Sleep(100 * time.Millisecond) // In a real implementation, you might send progress // via a channel or callback mechanism if i%10 == 0 { log.Printf("Processing: %d%% complete", i) } } return map[string]interface{}{ "status": "completed", "dataset_id": datasetID, "records": 1000, }, nil }, } ``` ## Tool Packaging Create reusable tool packages: ```go // weathertools/weather.go package weathertools import ( "context" "github.com/digitallysavvy/go-ai/pkg/provider/types" ) func NewWeatherTool(apiKey string) types.Tool { return types.Tool{ Name: "get_weather", Description: "Get current weather for a location", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{ "type": "string", }, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { // Implementation using apiKey return nil, nil }, } } func NewCalculatorTool() types.Tool { return types.Tool{ Name: "calculator", Description: "Perform calculations", Parameters: map[string]interface{}{ // ... }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { // Implementation return nil, nil }, } } ``` Usage: ```go skip-compile import "myapp/weathertools" weatherTool := weathertools.NewWeatherTool(apiKey) calcTool := weathertools.NewCalculatorTool() result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{weatherTool, calcTool}, StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, Prompt: "What's the weather and calculate something", }) ``` ## Error Handling Handle tool execution errors: ```go maxSteps := 5 result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: tools, MaxSteps: &maxSteps, Prompt: "Use tools", }) if err != nil { // Check for specific error types var noSuchToolErr *ai.NoSuchToolError var invalidInputErr *ai.InvalidToolInputError if errors.As(err, &noSuchToolErr) { fmt.Println("Model tried to call unknown tool") } else if errors.As(err, &invalidInputErr) { fmt.Println("Model called tool with invalid inputs") } else { fmt.Printf("Error: %v\n", err) } return } // Check for tool errors in the steps for _, step := range result.Steps { for _, toolResult := range step.ToolResults { if toolResult.Error != nil { fmt.Printf("Tool %s failed: %v\n", toolResult.ToolName, toolResult.Error) } } } ``` ## Best Practices 1. **Clear Descriptions**: Write clear tool descriptions to help the model know when to use them 2. **Schema Validation**: Define comprehensive parameter schemas with descriptions 3. **Error Handling**: Return clear error messages when tools fail 4. **Context Support**: Respect context cancellation in long-running tools 5. **Idempotency**: Make tools idempotent when possible 6. **Logging**: Log tool executions for debugging 7. **Rate Limiting**: Implement rate limiting for external API calls 8. **Type Safety**: Use Go structs for input validation 9. **Timeouts**: Set appropriate timeouts for external calls 10. **Testing**: Write unit tests for tool execution logic ## Concurrency and Parallel Tool Execution The Go AI SDK executes tools sequentially by default, but you can implement parallel execution patterns: ```go type ParallelToolExecutor struct { maxConcurrent int } func (e *ParallelToolExecutor) ExecuteTools( ctx context.Context, toolCalls []types.ToolCall, tools map[string]types.Tool, ) []types.ToolResult { results := make([]types.ToolResult, len(toolCalls)) var wg sync.WaitGroup sem := make(chan struct{}, e.maxConcurrent) for i, toolCall := range toolCalls { wg.Add(1) go func(idx int, tc types.ToolCall) { defer wg.Done() // Acquire semaphore sem <- struct{}{} defer func() { <-sem }() tool, ok := tools[tc.ToolName] if !ok { results[idx] = types.ToolResult{ ToolCallID: tc.ID, ToolName: tc.ToolName, Error: fmt.Errorf("tool not found: %s", tc.ToolName), } return } output, err := tool.Execute(ctx, tc.Arguments, types.ToolExecutionOptions{ ToolCallID: tc.ID, }) results[idx] = types.ToolResult{ ToolCallID: tc.ID, ToolName: tc.ToolName, Result: output, Error: err, } }(i, toolCall) } wg.Wait() return results } ``` ## Advanced Patterns ### Conditional Tool Selection Dynamically select tools based on context: ```go func getAvailableTools(userRole string) []types.Tool { basicTools := []types.Tool{weatherTool, calculatorTool} if userRole == "admin" { return append(basicTools, adminTools...) } return basicTools } result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: getAvailableTools(user.Role), StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, Prompt: prompt, }) ``` ### Tool Chaining Chain tool results: ```go maxSteps := 10 result, _ := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: []types.Tool{searchTool, summarizeTool, translateTool}, Prompt: "Search for AI news, summarize it, then translate to Spanish", MaxSteps: &maxSteps, OnStepFinish: func(ctx context.Context, step types.StepResult, userContext interface{}) { fmt.Printf("Step %d: Used %d tools\n", step.StepNumber, len(step.ToolCalls)) }, }) ``` ### Deferred Tool Search For large tool registries, mark tools `DeferLoading: true` and add `ai.ToolSearch()` so the model discovers only the tools it needs, instead of seeing every definition up front. By default, `ai.ToolSearch()` ranks deferred tools with built-in keyword scoring over their names and descriptions, returning up to five matches per call: ```go tools := []types.Tool{ ai.ToolSearch(), { Name: "getWeather", DeferLoading: true, Description: "Get the current weather for a city", // ... }, } ``` Use `ToolSearchConfig.MaxResults` to change how many matches a search returns (must be a positive integer; omitted or zero defaults to five), and `ToolSearchConfig.Search` to replace the built-in keyword scoring with your own ranking function: ```go tools := []types.Tool{ ai.ToolSearch(ai.ToolSearchConfig{ MaxResults: 10, Search: func(ctx context.Context, query string, candidates []ai.ToolSearchCandidate) ([]string, error) { // Return matching tool names in ranked order, e.g. from a // vector search or external index. Unknown names and // duplicates are ignored, and the result is capped at // MaxResults. return rankToolsByEmbedding(ctx, query, candidates) }, }), // ... deferred tools } ``` ## Next Steps - Learn about [Embeddings](https://goaisdk.com/docs/ai-sdk-core/embeddings.md) - Explore [Agents](https://goaisdk.com/docs/agents/overview.md) - See [Tool Examples](https://github.com/digitallysavvy/go-ai/tree/main/examples) ## See Also - [Foundations: Tools](https://goaisdk.com/docs/foundations/tools.md) - [Generating Text](https://goaisdk.com/docs/ai-sdk-core/generating-text.md) - [Settings](https://goaisdk.com/docs/ai-sdk-core/settings.md) - [Error Handling](https://goaisdk.com/docs/ai-sdk-core/error-handling.md) - [API Reference: Tool Types](https://goaisdk.com/docs/reference/types/tools.md) --- # Model Context Protocol (MCP) > Learn how to connect to Model Context Protocol (MCP) servers and use their tools with Go AI SDK. Canonical URL: https://goaisdk.com/docs/ai-sdk-core/mcp-tools Documentation index: https://goaisdk.com/llms.txt The Go AI SDK supports connecting to [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers to access their tools, resources, and prompts. This enables your AI applications to discover and use capabilities across various services through a standardized interface. ## Overview MCP (Model Context Protocol) is an open protocol that allows AI applications to: - **Discover Tools**: Automatically detect available functions from MCP servers - **Execute Tools**: Call remote functions and get structured results - **Access Resources**: Read data from various sources (files, databases, APIs) - **Use Prompts**: Retrieve reusable prompt templates ## Installation The MCP package is included in the Go AI SDK: ```bash go get github.com/digitallysavvy/go-ai ``` ```go import ( "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/mcp" ) ``` ## Initializing an MCP Client We recommend using HTTP transport for production deployments. The stdio transport should only be used for connecting to local servers as it cannot be deployed to production environments. ### HTTP Transport (Recommended) For production deployments, use the HTTP transport: ```go import ( "context" "github.com/digitallysavvy/go-ai/pkg/mcp" ) ctx := context.Background() // Create HTTP transport transport := mcp.NewHTTPTransport(mcp.HTTPTransportConfig{ URL: "https://your-server.com/mcp", TimeoutMS: 30000, // Optional: Add custom headers (set on the base transport config) Config: mcp.TransportConfig{ Headers: map[string]string{ "Authorization": "Bearer my-api-key", }, }, }) // Create client client := mcp.NewMCPClient(transport, mcp.MCPClientConfig{ ClientName: "my-go-app", ClientVersion: "1.0.0", }) // Connect err := client.Connect(ctx) if err != nil { log.Fatal(err) } defer client.Close() ``` ### HTTP with OAuth Authentication For authenticated MCP servers: ```go transport := mcp.NewHTTPTransport(mcp.HTTPTransportConfig{ URL: "https://mcp.example.com", TimeoutMS: 30000, OAuth: &mcp.OAuthConfig{ ClientID: "your-client-id", ClientSecret: "your-client-secret", TokenURL: "https://auth.example.com/token", }, }) client := mcp.NewMCPClient(transport, mcp.MCPClientConfig{ ClientName: "my-go-app", ClientVersion: "1.0.0", }) err := client.Connect(ctx) if err != nil { log.Fatal(err) } defer client.Close() ``` ### Custom OAuth Client Providers For full control over credential storage (e.g. a database-backed provider shared by multiple processes), implement `mcp.OAuthClientProvider` and drive the flow yourself with `mcp.Auth`. If the server rotates its OAuth issuer, rediscovered authorization server metadata may no longer match the metadata that issued your stored credentials. `mcp.Auth` surfaces this as a permanent failure, detectable with `mcp.IsAuthorizationServerMismatchError`: ```go result, err := mcp.Auth(ctx, provider, mcp.AuthOptions{ServerURL: serverURL}) if mcp.IsAuthorizationServerMismatchError(err) { // Treat as permanent: drop credentials and start a fresh authorization // flow rather than retrying with the stale pin. } ``` If your provider also implements `mcp.OAuthTokenInvalidator`, `mcp.Auth` identifies the exact token generation a failed refresh attempted, letting providers that share storage across clients delete only that stale generation instead of unconditionally clearing storage — so a concurrent refresh's newer tokens survive: ```go func (p *myOAuthProvider) InvalidateCredentialsForTokens(ctx context.Context, tokens mcp.OAuthTokens) error { if p.tokens != nil && p.tokens.AccessToken == tokens.AccessToken && p.tokens.RefreshToken == tokens.RefreshToken { p.tokens = nil } return nil } ``` ### HTTP Error Handling HTTP transport failures return `*mcp.MCPClientError` with structured transport fields. `Code` remains the JSON-RPC application error code; `StatusCode`, `URL`, and `ResponseBody` describe the HTTP failure. ```go err := client.Connect(ctx) if err != nil { var clientErr *mcp.MCPClientError if errors.As(err, &clientErr) && clientErr.StatusCode == http.StatusUnauthorized { log.Printf("MCP auth failed for %s: %s", clientErr.URL, clientErr.ResponseBody) return } log.Fatal(err) } ``` ### Stdio Transport (Local Servers Only) > **Warning:** The stdio transport should only be used for local development with MCP servers running as child processes. ```go // Create stdio transport for local Node.js MCP server transport := mcp.NewStdioTransport(mcp.StdioTransportConfig{ Command: "node", Args: []string{"server.mjs"}, // Optional: Set working directory WorkingDir: "/path/to/mcp/server", // Optional: Set environment variables Env: []string{ "API_KEY=your-api-key", }, }) client := mcp.NewMCPClient(transport, mcp.MCPClientConfig{ ClientName: "my-go-app", ClientVersion: "1.0.0", }) err := client.Connect(ctx) if err != nil { log.Fatal(err) } defer client.Close() ``` ### Closing the MCP Client After initialization, you should close the MCP client based on your usage pattern: - **Short-lived usage**: Close the client when the response is finished - **Long-running clients**: Keep the client open but ensure it's closed when the application terminates ```go // For short-lived usage client := setupMCPClient() defer client.Close() result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: tools, Prompt: "Your prompt here", }) // Client closes automatically via defer ``` ```go // For long-running applications client := setupMCPClient() // Register cleanup handler c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c client.Close() os.Exit(0) }() // Use client throughout application lifecycle ``` ## Using MCP Tools The Go AI SDK provides two approaches for working with MCP tools: ### Approach 1: Schema Discovery (Recommended) With schema discovery, all tools offered by the server are automatically listed and converted to Go AI SDK tools: ```go import ( "context" "os" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/mcp" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) ctx := context.Background() // Set up MCP client transport := mcp.NewHTTPTransport(mcp.HTTPTransportConfig{ URL: "https://mcp.example.com", }) client := mcp.NewMCPClient(transport, mcp.MCPClientConfig{ ClientName: "my-app", ClientVersion: "1.0.0", }) err := client.Connect(ctx) if err != nil { log.Fatal(err) } defer client.Close() // Convert MCP tools to Go AI SDK tools converter := mcp.NewMCPToolConverter(client) tools, err := converter.ConvertToGoAITools(ctx) if err != nil { log.Fatal(err) } // Use tools with AI model openaiProvider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) model, _ := openaiProvider.LanguageModel("gpt-6-astra") result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "What's the weather in Brooklyn, NY?", Tools: tools, // All MCP tools are available // Allow a follow-up step so the model can turn the tool result into a // final answer (GenerateText/StreamText default to a single step). StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) ``` This approach is simpler and automatically stays in sync with server changes. However, you won't have compile-time type safety for tool parameters. ### Approach 2: Schema Definition For better type safety and control, you can define specific tools and their schemas: ```go import ( "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider/types" ) // Define tool schema weatherTool := types.Tool{ Name: "get-weather", Description: "Get weather information for a location", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]string{ "type": "string", "description": "City name", }, "units": map[string]interface{}{ "type": "string", "enum": []string{"celsius", "fahrenheit"}, "description": "Temperature units", }, }, "required": []string{"location"}, }, Execute: func(ctx context.Context, args map[string]interface{}, _ types.ToolExecutionOptions) (interface{}, error) { // Call MCP tool manually result, err := client.CallTool(ctx, "get-weather", args) if err != nil { return nil, err } // Convert result content, err := mcp.ConvertMCPContentToAISDK(result.Content) if err != nil { return nil, err } return content, nil }, } // Use with AI model result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "What's the temperature in Paris?", Tools: []types.Tool{weatherTool}, // Allow a follow-up step so the model can turn the tool result into a // final answer (GenerateText/StreamText default to a single step). StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, }) ``` This approach provides full type safety and lets you catch parameter mismatches during development. ## Image Content Handling > **Important:** The Go AI SDK automatically handles image content from MCP tools to prevent token explosions (200K+ tokens per image). When MCP tools return images (screenshots, charts, diagrams), the SDK properly converts them to the AI SDK's native image format: ```go // MCP server returns tool result with image // { // "type": "image", // "data": "iVBORw0KGgo...", // base64 data // "mimeType": "image/png" // } // Automatically converted to ImageContent // - Base64 decoded to bytes // - Token usage: ~1K instead of 200K+ // - Cost savings: ~99% reduction // Use tools that return images normally: tools, _ := converter.ConvertToGoAITools(ctx) result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Create a sales chart and analyze it", Tools: tools, // Tool may return image StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, }) // Images in results are properly handled // No token explosion ``` ### Supported Image Formats The SDK handles all standard image formats: 1. **Base64 data**: Raw base64-encoded image 2. **Data URLs**: `data:image/png;base64,...` 3. **HTTP/HTTPS URLs**: Remote image URLs ### Performance Impact - **Without image handling**: 200K+ tokens per image - **With proper handling**: ~1K tokens per image - **Cost savings**: large reduction in image tokens sent to the model ## Using MCP Resources Resources are application-driven data sources that provide context to the model. Unlike tools (which are model-controlled), your application decides when to fetch and pass resources as context. ### Listing Resources List all available resources from the MCP server: ```go resources, err := client.ListResources(ctx) if err != nil { log.Fatal(err) } for _, resource := range resources { fmt.Printf("Resource: %s (%s)\n", resource.Name, resource.URI) fmt.Printf(" Description: %s\n", resource.Description) fmt.Printf(" MIME Type: %s\n", resource.MimeType) } ``` ### Reading Resource Contents Read the contents of a specific resource by its URI: ```go resourceData, err := client.ReadResource(ctx, "file:///example/document.txt") if err != nil { log.Fatal(err) } // Use resource content in prompt result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: fmt.Sprintf("Summarize this document:\n\n%s", resourceData.Contents[0].Text), }) ``` ### Listing Resource Templates Resource templates are dynamic URI patterns that allow flexible queries: ```go templates, err := client.ListResourceTemplates(ctx) if err != nil { log.Fatal(err) } for _, template := range templates.ResourceTemplates { fmt.Printf("Template: %s\n", template.Name) fmt.Printf(" URI Template: %s\n", template.URITemplate) fmt.Printf(" Description: %s\n", template.Description) } ``` ## Handling Elicitation Requests Some MCP servers pause a tool call to ask the client (your application) for additional user input mid-execution, using the `elicitation/create` request. Register a handler with `OnElicitationRequest` before calling `Connect`: ```go client.OnElicitationRequest(func(ctx context.Context, req mcp.ElicitationRequest) (mcp.ElicitResult, error) { fmt.Println("Server asks:", req.Message) // req.RequestedSchema describes the JSON Schema for the expected input. answer := promptUserSomehow(req) return mcp.ElicitResult{ Action: "accept", // or "decline" / "cancel" Content: map[string]interface{}{"answer": answer}, }, nil }) err := client.Connect(ctx) ``` If no handler is registered, the client rejects `elicitation/create` requests automatically. A handler error is reported through the client's `OnError` callback rather than propagating to the tool call. ## Using MCP Prompts > **Warning:** MCP Prompts is an experimental feature and may change in the future. Prompts are user-controlled templates that servers expose for clients to list and retrieve with optional arguments. ### Listing Prompts ```go prompts, err := client.ListPrompts(ctx) if err != nil { log.Fatal(err) } for _, prompt := range prompts { fmt.Printf("Prompt: %s\n", prompt.Name) fmt.Printf(" Description: %s\n", prompt.Description) if len(prompt.Arguments) > 0 { fmt.Println(" Arguments:") for _, arg := range prompt.Arguments { fmt.Printf(" - %s (required: %v): %s\n", arg.Name, arg.Required, arg.Description) } } } ``` ### Getting a Prompt Retrieve prompt messages, optionally passing arguments: ```go prompt, err := client.GetPrompt(ctx, "code_review", map[string]interface{}{ "code": "func add(a, b int) int { return a + b }", "language": "go", }) if err != nil { log.Fatal(err) } // Convert prompt messages to AI SDK messages messages := make([]types.Message, len(prompt.Messages)) for i, msg := range prompt.Messages { messages[i] = types.Message{ Role: types.MessageRole(msg.Role), Content: []types.ContentPart{ types.TextContent{Text: msg.Content.Text}, }, } } // Use prompt messages with AI model result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Messages: messages, }) ``` ## Complete Example Here's a complete example that demonstrates all MCP features: ```go package main import ( "context" "fmt" "log" "os" "os/signal" "syscall" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/mcp" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { ctx := context.Background() // 1. Set up MCP client with HTTP transport transport := mcp.NewHTTPTransport(mcp.HTTPTransportConfig{ URL: os.Getenv("MCP_SERVER_URL"), TimeoutMS: 30000, Config: mcp.TransportConfig{ Headers: map[string]string{ "Authorization": "Bearer " + os.Getenv("MCP_API_KEY"), }, }, }) client := mcp.NewMCPClient(transport, mcp.MCPClientConfig{ ClientName: "go-ai-example", ClientVersion: "1.0.0", }) err := client.Connect(ctx) if err != nil { log.Fatal(err) } // Ensure cleanup defer client.Close() // Handle interrupt signal c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c client.Close() os.Exit(0) }() // 2. List available resources resources, err := client.ListResources(ctx) if err != nil { log.Fatal(err) } fmt.Println("Available resources:") for _, r := range resources { fmt.Printf(" - %s: %s\n", r.Name, r.Description) } // 3. Convert MCP tools to AI SDK tools converter := mcp.NewMCPToolConverter(client) tools, err := converter.ConvertToGoAITools(ctx) if err != nil { log.Fatal(err) } fmt.Printf("\nConverted %d MCP tools\n\n", len(tools)) // 4. Use tools with AI model provider := openai.New(openai.Config{ APIKey: os.Getenv("OPENAI_API_KEY"), }) model, err := provider.LanguageModel("gpt-6-astra") if err != nil { log.Fatal(err) } maxSteps := 5 // Allow multiple tool calls result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Use the available tools to get the current weather " + "in Brooklyn, New York and create a chart showing " + "temperature trends", Tools: tools, MaxSteps: &maxSteps, }) if err != nil { log.Fatal(err) } // 5. Display results fmt.Println("AI Response:") fmt.Println(result.Text) // Show tool calls made if len(result.Steps) > 0 { fmt.Println("\nTool Calls:") for i, step := range result.Steps { fmt.Printf(" Step %d:\n", i+1) for _, call := range step.ToolCalls { fmt.Printf(" - %s(%v)\n", call.ToolName, call.Arguments) } } } // Display usage statistics fmt.Printf("\nUsage: %d input tokens, %d output tokens\n", result.Usage.GetInputTokens(), result.Usage.GetOutputTokens()) } ``` ## Error Handling Handle common MCP errors gracefully: ```go import ( "errors" "github.com/digitallysavvy/go-ai/pkg/mcp" ) // Connect with error handling err := client.Connect(ctx) if err != nil { var transportErr *mcp.TransportError if errors.As(err, &transportErr) { log.Printf("Failed to connect to MCP server: %v", err) // Retry or fallback logic } else { log.Fatal(err) } } // Tool execution with error handling result, err := client.CallTool(ctx, "get-weather", map[string]interface{}{ "location": "New York", }) if err != nil { var clientErr *mcp.MCPClientError var timeoutErr *mcp.TimeoutError if errors.As(err, &timeoutErr) { log.Printf("Tool execution timed out: %v", err) // Retry with longer timeout } else if errors.As(err, &clientErr) { log.Printf("Tool execution failed: %v", err) // Handle tool failure } else { log.Fatal(err) } } else { fmt.Printf("Tool result: %+v\n", result.Content) } ``` ## Best Practices ### 1. Use HTTP Transport in Production ```go // ✅ Good: HTTP transport for production httpTransport := mcp.NewHTTPTransport(mcp.HTTPTransportConfig{ URL: "https://mcp.production.com", }) // ❌ Avoid: Stdio transport in production (won't work) stdioTransport := mcp.NewStdioTransport(mcp.StdioTransportConfig{ Command: "node", // Cannot deploy to cloud }) ``` ### 2. Always Close the Client ```go // ✅ Good: Use defer to ensure cleanup client := setupMCPClient() defer client.Close() // ✅ Good: Handle signals for long-running apps c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { <-c client.Close() os.Exit(0) }() ``` ### 3. Handle Context Cancellation ```go // ✅ Good: Respect context cancellation ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: tools, Prompt: prompt, }) if err != nil { if ctx.Err() == context.DeadlineExceeded { log.Println("Request timed out") } return err } ``` ### 4. Use Schema Discovery for Simplicity ```go // ✅ Good: Simple schema discovery converter := mcp.NewMCPToolConverter(client) tools, err := converter.ConvertToGoAITools(ctx) // Use all available tools result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: tools, StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, Prompt: "Use whatever tools you need", }) ``` ### 5. Monitor Token Usage ```go // ✅ Good: Monitor usage to catch image token explosions result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Tools: tools, StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, Prompt: prompt, }) if err != nil { return err } // Check if image handling is working correctly if result.Usage.GetInputTokens() > 100000 { log.Printf("WARNING: High token usage (%d) - possible image handling issue", result.Usage.GetInputTokens()) } ``` ## Testing The MCP package includes comprehensive tests: ```bash # Run all MCP tests go test ./pkg/mcp/... -v # Run tests with coverage go test ./pkg/mcp/... -cover # Run specific test go test ./pkg/mcp -run TestContentConversion -v ``` ## Troubleshooting ### Connection Failures If you cannot connect to an MCP server: ```go // Enable debug logging transport := mcp.NewHTTPTransport(mcp.HTTPTransportConfig{ URL: "https://mcp.example.com", Config: mcp.TransportConfig{ EnableLogging: true, // Enable verbose logging }, }) // Check network connectivity // - Verify the URL is correct // - Check firewall settings // - Test with curl: curl https://mcp.example.com/mcp ``` ### Tool Execution Failures If tools fail to execute: ```go // Check tool availability tools, err := converter.ConvertToGoAITools(ctx) if err != nil { log.Fatal("Failed to convert tools:", err) } fmt.Printf("Available tools: %d\n", len(tools)) for _, tool := range tools { fmt.Printf(" - %s: %s\n", tool.Name, tool.Description) } // Test tool manually result, err := client.CallTool(ctx, "problem-tool", map[string]interface{}{}) if err != nil { fmt.Printf("Tool error: %v\n", err) } else { fmt.Printf("Tool result: %+v\n", result.Content) } ``` ### High Token Usage with Images If you're seeing unexpectedly high token usage: ```go // This should NOT happen with proper image handling // but if you see 200K+ token usage, check: // 1. Verify image conversion is working result, err := client.CallTool(ctx, "create-chart", args) if err != nil { log.Fatal(err) } // 2. Inspect the result content for _, content := range result.Content { if content.Type == "image" { fmt.Println("Image found - should be converted to bytes") // If you see base64 text here, there's a problem } } ``` ## See Also - [MCP Specification](https://spec.modelcontextprotocol.io/) - [Generating Text](https://goaisdk.com/docs/ai-sdk-core/generating-text.md) - [Tools](https://goaisdk.com/docs/foundations/tools.md) - [Error Handling](https://goaisdk.com/docs/ai-sdk-core/error-handling.md) - [Advanced: Tool Calling](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md) ## May 2026 parity updates MCP JSON parsing uses secure parsing to avoid prototype-pollution style payloads when consuming remote server data. MCP clients expose server instructions and attach TypeScript-compatible MCP provider metadata to converted tools under the `"mcp"` key: `clientName`, `toolName`, optional `title`, and optional MCP Apps `app` metadata. The default client name is `ai-sdk-mcp-client`; `ClientName` overrides it, and `Name` remains as a deprecated alias. MCP Apps hosts can advertise app-rendering support with `mcp.MCPAppClientCapabilities()`, inspect `_meta.ui` using `GetMCPAppToolMeta`, split tools with `SplitMCPAppTools`, and read `ui://` app resources with `ReadMCPAppResource`. MCP tool and prompt content also accept `resource_link` parts, matching the current MCP schema. Custom transports remain supported through the transport interfaces in `pkg/mcp`. Use explicit redirect handling for HTTP transports and prefer the default redirect error mode unless the server is trusted. ```go transport := mcp.NewHTTPTransport(mcp.HTTPTransportConfig{ URL: "https://mcp.example.com", Config: mcp.TransportConfig{ Redirect: mcp.MCPRedirectError, }, }) ``` --- # Upload File and Upload Skill > Upload provider files and multi-file skills for later reference in model calls. Canonical URL: https://goaisdk.com/docs/ai-sdk-core/upload-file-and-skill Documentation index: https://goaisdk.com/llms.txt The Go SDK provides top-level helpers for uploading files and skills through provider upload APIs: - `ai.UploadFile(...)` - `ai.UploadSkill(...)` Both helpers accept either: 1. A provider upload API directly (`provider.FilesAPI` / `provider.SkillsAPI`) 2. A provider implementing `Files()` / `Skills()` ## UploadFile ```go result, err := ai.UploadFile(ctx, ai.UploadFileOptions{ API: openaiProvider, Data: []byte("hello world"), Filename: "hello.txt", MediaType: "text/plain", ProviderOptions: map[string]interface{}{ "openai": map[string]interface{}{"purpose": "assistants"}, }, }) if err != nil { panic(err) } fileID := result.ProviderReference["openai"] ``` `UploadFile` normalizes shorthand data inputs (`[]byte`, `string`) into `types.FileData`, resolves the target files API, and auto-detects media type when omitted. String data is treated as a TypeScript-compatible base64 data string and decoded by the provider upload implementation. Use the returned provider reference in later prompts without downloading or inlining the file: ```go result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Messages: []types.Message{ { Role: types.RoleUser, Content: []types.ContentPart{ types.TextContent{Text: "Summarize this file."}, types.FileContent{ MediaType: upload.MediaType, Filename: upload.Filename, FileData: types.FileData{ Type: types.FileDataTypeReference, Reference: upload.ProviderReference, MediaType: upload.MediaType, }, }, }, }, }, }) ``` See `examples/upload-file/provider-reference` for a complete upload-then-reference flow. ## UploadSkill ```go result, err := ai.UploadSkill(ctx, ai.UploadSkillOptions{ API: openaiProvider, DisplayTitle: "My Skill", Files: []ai.UploadSkillFile{ {Path: "index.ts", Data: []byte("export default {}")}, {Path: "README.md", Data: "# Skill"}, }, }) if err != nil { panic(err) } skillID := result.ProviderReference["openai"] ``` `UploadSkill` normalizes shorthand file data and delegates to the provider skills API. --- # Realtime Sessions > Documents provider-neutral realtime session helpers in the Go AI SDK, exposing provider.Experimental_RealtimeModelV4 through ai.ConnectRealtime. Canonical URL: https://goaisdk.com/docs/ai-sdk-core/realtime Documentation index: https://goaisdk.com/llms.txt The Go SDK exposes the TypeScript AI SDK experimental realtime model surface through `provider.Experimental_RealtimeModelV4` and the provider-neutral helper `ai.ConnectRealtime`. ```go model, err := openaiProvider.RealtimeModel(openai.ModelGPT4oRealtimePreview) if err != nil { log.Fatal(err) } instructions := "Answer briefly." session, err := ai.ConnectRealtime(ctx, model, ai.RealtimeSessionOptions{ SessionConfig: &provider.RealtimeSessionConfig{ Instructions: &instructions, }, }) if err != nil { log.Fatal(err) } defer session.Close() ``` The helper creates a provider client secret, opens the provider WebSocket, sends an initial session update when `SessionConfig` is supplied, and normalizes provider server events. Unknown provider events are preserved on `RealtimeServerEvent.Raw` with their raw provider event type so callers can handle new provider events before the SDK adds first-class fields. ## Providers - OpenAI realtime: `openai.Provider.RealtimeModel`, `ExperimentalRealtimeModel`, and `GetRealtimeToken`. - Google Gemini Live: `google.Provider.RealtimeModel`, `ExperimentalRealtimeModel`, and `GetRealtimeToken`. - xAI realtime: `xai.Provider.RealtimeModel`, `ExperimentalRealtimeModel`, and `GetRealtimeToken`. Gateway realtime is not exposed because the June 6 TypeScript range did not include a Gateway realtime model, factory, tests, or changeset. ## Not Applicable Browser Pieces The TypeScript SDK also ships browser-only realtime transports and React helpers. They are intentionally not implemented in Go: - `BrowserRealtimeTransport` - `BrowserRealtimeAudio` - `useRealtime` Go callers provide a server-side WebSocket dialer through `RealtimeSessionOptions.Dialer` when they need custom transport behavior. --- # Error Handling > Covers Go AI SDK error handling: regular and streaming errors, context cancellation, specific error types, and a comprehensive error-handling example. Canonical URL: https://goaisdk.com/docs/ai-sdk-core/error-handling Documentation index: https://goaisdk.com/llms.txt The Go AI SDK provides robust error handling with specific error types for different failure scenarios. All errors follow Go's standard error handling patterns. ## Handling Regular Errors Regular errors are returned from functions and should be checked using Go's idiomatic error handling: ```go import ( "context" "log" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { ctx := context.Background() provider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) model, _ := provider.LanguageModel("gpt-6-astra") result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Write a vegetarian lasagna recipe for 4 people.", }) if err != nil { // Handle error log.Printf("Error generating text: %v", err) return } fmt.Println(result.Text) } ``` See [Error Types](#error-types) below for more information on the different types of errors that may be returned. ## Handling Streaming Errors When errors occur during streaming, you should check for errors both while reading from the stream channel and after the stream completes. ### Simple Text Streaming ```go import ( "context" "fmt" "log" "github.com/digitallysavvy/go-ai/pkg/ai" ) func main() { ctx := context.Background() stream, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Write a vegetarian lasagna recipe for 4 people.", }) if err != nil { log.Fatal(err) } // Read from stream for chunk := range stream.Chunks() { fmt.Print(chunk.Text) } // Check for errors after stream completes if err := stream.Err(); err != nil { log.Printf("Stream error: %v", err) return } } ``` ### Full Stream with Error Chunks Full streams support error chunks within the stream itself. You should handle both chunk errors and post-stream errors: ```go import ( "context" "fmt" "log" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider" ) func main() { ctx := context.Background() stream, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Write a vegetarian lasagna recipe for 4 people.", }) if err != nil { log.Fatal(err) } // Read from full stream for chunk := range stream.Chunks() { switch chunk.Type { case provider.ChunkTypeText: fmt.Print(chunk.Text) case provider.ChunkTypeToolCall: fmt.Printf("Tool call: %s\n", chunk.ToolCall.ToolName) case provider.ChunkTypeError: // Handle error chunk log.Printf("Error in stream: %v", chunk.Err) case provider.ChunkTypeFinish: fmt.Printf("\nFinished: %s\n", chunk.FinishReason) } } // Check for errors after stream completes if err := stream.Err(); err != nil { log.Printf("Stream error: %v", err) return } } ``` ## Handling Context Cancellation Go uses `context.Context` for cancellation and timeouts. When a context is canceled, operations return a context error: ```go import ( "context" "fmt" "time" ) func main() { // Create context with timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() stream, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Write a very long story...", }) if err != nil { log.Fatal(err) } for chunk := range stream.Chunks() { fmt.Print(chunk.Text) } // Check if operation was canceled or timed out if err := stream.Err(); err != nil { if ctx.Err() == context.DeadlineExceeded { fmt.Println("\nOperation timed out") } else if ctx.Err() == context.Canceled { fmt.Println("\nOperation was canceled") } else { fmt.Printf("\nStream error: %v\n", err) } } } ``` ### OnFinish Callback with Context The `OnFinish` callback is only called when the operation completes normally. It is **not** called when the context is canceled: ```go stream, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Write a story...", OnFinish: func(result *ai.StreamTextResult) { // Called only on normal completion, NOT on cancellation fmt.Printf("Completed after %d steps\n", len(result.Steps())) fmt.Printf("Total tokens used: %d\n", result.Usage().GetTotalTokens()) }, }) // Handle context cancellation separately for chunk := range stream.Chunks() { fmt.Print(chunk.Text) } if ctx.Err() == context.Canceled { fmt.Println("Stream was canceled") // Perform cleanup operations here } ``` ## Error Types The Go AI SDK provides several specific error types for different failure scenarios. Use `errors.Is()` and `errors.As()` to check for specific error types. ### Provider Error Represents an error from an AI provider (e.g., invalid API key, model not found, rate limiting): ```go import ( "errors" "fmt" providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" ) result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Generate text", }) if err != nil { var providerErr *providererrors.ProviderError if errors.As(err, &providerErr) { fmt.Printf("Provider: %s\n", providerErr.Provider) fmt.Printf("Status Code: %d\n", providerErr.StatusCode) fmt.Printf("Error Code: %s\n", providerErr.ErrorCode) fmt.Printf("Message: %s\n", providerErr.Message) return } } ``` ### Rate Limit Error Represents a rate limit error from a provider: ```go import ( "errors" "time" providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" ) result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Generate text", }) if err != nil { var rateLimitErr *providererrors.RateLimitError if errors.As(err, &rateLimitErr) { fmt.Printf("Rate limit exceeded for %s\n", rateLimitErr.Provider) if rateLimitErr.RetryAfterSeconds != nil { fmt.Printf("Retry after %d seconds\n", *rateLimitErr.RetryAfterSeconds) time.Sleep(time.Duration(*rateLimitErr.RetryAfterSeconds) * time.Second) // Retry the request... } return } } ``` ### Validation Error Represents a validation error (e.g., invalid parameters, schema validation failure): ```go import ( "errors" providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" ) result, err := ai.GenerateObject(ctx, ai.GenerateObjectOptions{ Model: model, Schema: invalidSchema, Prompt: "Generate object", }) if err != nil { var validationErr *providererrors.ValidationError if errors.As(err, &validationErr) { fmt.Printf("Validation failed on field: %s\n", validationErr.Field) fmt.Printf("Error: %s\n", validationErr.Message) return } } ``` ### Tool Execution Error Represents an error during tool execution: ```go import ( "errors" providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" ) result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Use the weather tool", Tools: []types.Tool{weatherTool}, }) if err != nil { var toolErr *providererrors.ToolExecutionError if errors.As(err, &toolErr) { fmt.Printf("Tool %s failed (call ID: %s)\n", toolErr.ToolName, toolErr.ToolCallID) fmt.Printf("Error: %s\n", toolErr.Message) return } } ``` ### Stream Error Represents an error during streaming: ```go import ( "errors" providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" ) stream, err := ai.StreamText(ctx, ai.StreamTextOptions{ Model: model, Prompt: "Generate text", }) if err != nil { log.Fatal(err) } for chunk := range stream.Chunks() { fmt.Print(chunk.Text) } if err := stream.Err(); err != nil { var streamErr *providererrors.StreamError if errors.As(err, &streamErr) { fmt.Printf("Stream error: %s\n", streamErr.Message) return } } ``` ### Standard Errors The SDK also provides standard error variables for common cases: ```go import ( "errors" providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" ) // Check for specific standard errors if errors.Is(err, providererrors.ErrInvalidInput) { fmt.Println("Invalid input parameters") } if errors.Is(err, providererrors.ErrModelNotFound) { fmt.Println("Model not found") } if errors.Is(err, providererrors.ErrProviderNotFound) { fmt.Println("Provider not found") } if errors.Is(err, providererrors.ErrToolNotFound) { fmt.Println("Tool not found") } if errors.Is(err, providererrors.ErrValidationFailed) { fmt.Println("Validation failed") } if errors.Is(err, providererrors.ErrUnsupportedFeature) { fmt.Println("Feature not supported by provider") } ``` ## Comprehensive Error Handling Example Here's a complete example showing robust error handling: ```go package main import ( "context" "errors" "fmt" "log" "os" "time" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider" providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func generateWithRetry(ctx context.Context, model provider.LanguageModel, prompt string, maxRetries int) (*ai.GenerateTextResult, error) { var lastErr error for attempt := 0; attempt <= maxRetries; attempt++ { if attempt > 0 { fmt.Printf("Retry attempt %d/%d\n", attempt, maxRetries) // Exponential backoff backoff := time.Duration(1<= 500 && providerErr.StatusCode < 600 { fmt.Printf("Provider error %d, retrying...\n", providerErr.StatusCode) continue } // 4xx errors are not retryable return nil, fmt.Errorf("non-retryable provider error: %w", err) } // Don't retry on context errors if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return nil, err } // Don't retry on validation errors if providererrors.IsValidationError(err) { return nil, fmt.Errorf("validation error: %w", err) } } return nil, fmt.Errorf("failed after %d attempts: %w", maxRetries+1, lastErr) } func main() { ctx := context.Background() provider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}) model, err := provider.LanguageModel("gpt-6-astra") if err != nil { log.Fatal(err) } result, err := generateWithRetry(ctx, model, "Explain quantum computing", 3) if err != nil { log.Fatalf("Failed to generate text: %v", err) } fmt.Println(result.Text) } ``` ## Best Practices ### 1. Always Check Errors Never ignore errors - always check and handle them appropriately: ```go // Good result, err := ai.GenerateText(ctx, options) if err != nil { return fmt.Errorf("failed to generate text: %w", err) } // Bad result, _ := ai.GenerateText(ctx, options) ``` ### 2. Use Error Wrapping Wrap errors to provide context: ```go result, err := ai.GenerateText(ctx, options) if err != nil { return fmt.Errorf("failed to process user request: %w", err) } ``` ### 3. Check Specific Error Types Use `errors.As()` and `errors.Is()` to check for specific errors: ```go var rateLimitErr *providererrors.RateLimitError if errors.As(err, &rateLimitErr) { // Handle rate limit specifically } ``` ### 4. Handle Context Cancellation Always respect context cancellation: ```go for chunk := range stream.Chunks() { select { case <-ctx.Done(): return ctx.Err() default: fmt.Print(chunk.Text) } } ``` ### 5. Use Defer for Cleanup Use defer to ensure cleanup happens even on error: ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // Always called, even on error result, err := ai.GenerateText(ctx, options) // ... ``` ### 6. Log Errors Appropriately Log errors with sufficient context for debugging: ```go if err != nil { log.Printf("Failed to generate text for user %s: %v", userID, err) return err } ``` ### 7. Don't Retry Non-Retryable Errors Some errors shouldn't be retried (validation errors, 4xx errors): ```go if providererrors.IsValidationError(err) { return err // Don't retry } var providerErr *providererrors.ProviderError if errors.As(err, &providerErr) && providerErr.StatusCode >= 400 && providerErr.StatusCode < 500 { return err // Don't retry 4xx errors } ``` ### 8. Implement Exponential Backoff When retrying, use exponential backoff to avoid overwhelming the service: ```go for attempt := 0; attempt <= maxRetries; attempt++ { if attempt > 0 { backoff := time.Duration(1< # API Reference > Complete API reference index for the Go AI SDK, linking core functions, tool and helper APIs, type definitions, provider interfaces, and middleware. Canonical URL: https://goaisdk.com/docs/reference/ Documentation index: https://goaisdk.com/llms.txt Complete reference documentation for all Go AI SDK APIs, types, and interfaces. ## Core AI Functions High-level functions for working with AI models: - [GenerateText](https://goaisdk.com/docs/reference/ai/generate-text.md) - Generate text completions - [StreamText](https://goaisdk.com/docs/reference/ai/stream-text.md) - Stream text completions in real-time - [GenerateObject](https://goaisdk.com/docs/reference/ai/generate-object.md) - Generate structured data - [StreamObject](https://goaisdk.com/docs/reference/ai/stream-object.md) - Stream structured data - [Embed](https://goaisdk.com/docs/reference/ai/embed.md) - Generate embeddings for text - [EmbedMany](https://goaisdk.com/docs/reference/ai/embed-many.md) - Generate embeddings for multiple texts - [Rerank](https://goaisdk.com/docs/reference/ai/rerank.md) - Rerank documents by relevance - [GenerateImage](https://goaisdk.com/docs/reference/ai/generate-image.md) - Generate images from text - [Transcribe](https://goaisdk.com/docs/reference/ai/transcribe.md) - Transcribe audio to text - [GenerateSpeech](https://goaisdk.com/docs/reference/ai/generate-speech.md) - Generate speech from text - [Agent](https://goaisdk.com/docs/reference/ai/agent.md) - The `agent.Agent` interface (in `pkg/agent`) - [ToolLoopAgent](https://goaisdk.com/docs/reference/ai/tool-loop-agent.md) - `agent.ToolLoopAgent`, the SDK's tool-using agent (in `pkg/agent`) ## Tool & Helper APIs Functions for working with tools and utilities: - [Tool](https://goaisdk.com/docs/reference/ai/tool.md) - Define tools for AI models - [Dynamic Tools](https://goaisdk.com/docs/reference/ai/dynamic-tool.md) - Register tools at runtime - [CosineSimilarity](https://goaisdk.com/docs/reference/ai/cosine-similarity.md) - Calculate similarity between vectors - [IsStepCount](https://goaisdk.com/docs/reference/ai/step-count-is.md) - Stop condition: stop after n steps - [HasToolCall](https://goaisdk.com/docs/reference/ai/has-tool-call.md) - Stop condition: stop when a tool is called - [StopCondition](https://goaisdk.com/docs/reference/ai/stop-condition.md) - The StopCondition type and StopWhen evaluation - [GenerateID](https://goaisdk.com/docs/reference/ai/generate-id.md) - Generate unique IDs - [Callback Events](https://goaisdk.com/docs/reference/ai/callback-events.md) - Structured lifecycle events for observability - [Stream Transport Helpers](https://goaisdk.com/docs/reference/ai/stream-transport-helpers.md) - Convert stream results to HTTP-friendly payloads - [Agent UI Stream Helpers](https://goaisdk.com/docs/reference/ai/agent-ui-stream-helpers.md) - Bridge ToolLoopAgent streams to UI transport - [Middleware Aliases](https://goaisdk.com/docs/reference/ai/middleware-aliases.md) - Middleware APIs re-exported from pkg/ai ## Type Definitions Core types used throughout the SDK: - [Messages](https://goaisdk.com/docs/reference/types/messages.md) - Message types and structures - [Tools](https://goaisdk.com/docs/reference/types/tools.md) - Tool definition types - [Usage](https://goaisdk.com/docs/reference/types/usage.md) - Token usage tracking types - [Errors](https://goaisdk.com/docs/reference/types/errors.md) - Error types and handling ## Provider Interfaces Interfaces for implementing custom providers: - [LanguageModel](https://goaisdk.com/docs/reference/providers/language-model.md) - Language model interface - [EmbeddingModel](https://goaisdk.com/docs/reference/providers/embedding-model.md) - Embedding model interface - [ImageModel](https://goaisdk.com/docs/reference/providers/image-model.md) - Image generation interface - [SpeechModel](https://goaisdk.com/docs/reference/providers/speech-model.md) - Speech synthesis interface - [TranscriptionModel](https://goaisdk.com/docs/reference/providers/transcription-model.md) - Audio transcription interface - [CustomProvider](https://goaisdk.com/docs/reference/providers/custom-provider.md) - Create custom providers ## Middleware Middleware system for modifying model behavior: - [WrapLanguageModel](https://goaisdk.com/docs/reference/middleware/wrap-language-model.md) - Wrap models with middleware - [MiddlewareInterface](https://goaisdk.com/docs/reference/middleware/middleware-interface.md) - Middleware interface specification - [BuiltInMiddleware](https://goaisdk.com/docs/reference/middleware/built-in-middleware.md) - Built-in middleware functions ## Schema & Registry Schema validation and provider registry: - [JSONSchema](https://goaisdk.com/docs/reference/schema/json-schema.md) - JSON schema definitions - [ProviderRegistry](https://goaisdk.com/docs/reference/registry/provider-registry.md) - Provider registration system ## Error Types Comprehensive error reference: - [Error Types Overview](https://goaisdk.com/docs/reference/types/errors.md) - All error types and handling ## Integrations (experimental) Adapters and experimental runtimes added in v0.5.0: - [Code Mode](https://goaisdk.com/docs/reference/ai/code-mode.md) - Run model-written JavaScript that calls host tools, in a QuickJS-on-WebAssembly sandbox - [Harness Sandbox: Vercel](https://goaisdk.com/docs/reference/ai/harness-sandbox-vercel.md) - Run harness sandboxes on Vercel Sandbox - [LlamaIndex Adapter](https://goaisdk.com/docs/reference/ai/llamaindex.md) - Convert a LlamaIndex chat-engine stream to UI message stream chunks ## See Also - [Getting Started Guide](https://goaisdk.com/docs/getting-started.md) - [AI SDK Core](https://goaisdk.com/docs/ai-sdk-core/overview.md) - [Provider Documentation](https://goaisdk.com/docs/providers.md) --- # Agent UI Stream Helpers > API reference for pkg/agent UI stream helpers that bridge ToolLoopAgent streams to UI-transport-friendly forms, including CreateAgentUIStream. Canonical URL: https://goaisdk.com/docs/reference/ai/agent-ui-stream-helpers Documentation index: https://goaisdk.com/llms.txt These helper APIs live in `pkg/agent` and bridge `ToolLoopAgent` streams to UI transport-friendly forms. ## `CreateAgentUIStream` ```go func CreateAgentUIStream(ctx context.Context, agent *agent.ToolLoopAgent, opts agent.AgentStreamOptions) (<-chan ai.UIMessageChunk, <-chan error, error) ``` Creates UI message chunks from an agent stream. ## `CreateAgentUIStreamResponse` ```go func CreateAgentUIStreamResponse(ctx context.Context, agent *agent.ToolLoopAgent, opts agent.AgentStreamOptions) (*http.Response, error) ``` Returns a Server-Sent Events (`text/event-stream`) HTTP response for agent UI chunks. ## `PipeAgentUIStreamToResponse` ```go func PipeAgentUIStreamToResponse(ctx context.Context, agent *agent.ToolLoopAgent, opts agent.AgentStreamOptions, w io.Writer) error ``` Writes agent UI chunks to a writer as Server-Sent Events. ## Serving `useChat` from UI messages A chat frontend built with `useChat` posts its whole message history as UI messages. These helpers take those messages directly. They validate them against the agent's tools, convert them to model messages (including any tool approvals the user just gave), run the agent, and stream the reply back in the UI message stream protocol. They mirror TS `createAgentUIStream`, `createAgentUIStreamResponse` and `pipeAgentUIStreamToResponse`, which take `uiMessages`. ### `CreateAgentUIStreamFromUIMessages` ```go func CreateAgentUIStreamFromUIMessages(ctx context.Context, agent *agent.ToolLoopAgent, opts agent.CreateAgentUIStreamFromUIMessagesOptions) (<-chan ai.UIMessageChunk, <-chan error, error) ``` Returns the reply as a UI message chunk stream. `opts.UIMessages` accepts `[]ai.UIMessage`, decoded JSON, or raw JSON bytes or a string. It returns an error before streaming when the messages fail validation. ### `PipeAgentUIStreamFromUIMessagesToResponse` ```go func PipeAgentUIStreamFromUIMessagesToResponse(ctx context.Context, agent *agent.ToolLoopAgent, opts agent.CreateAgentUIStreamFromUIMessagesOptions, w io.Writer) error ``` Writes the reply to `w` as Server-Sent Events. On an `http.ResponseWriter` it sets the UI message stream headers and status first. The agent stops when writing fails, for example when the client disconnects. ```go package main import ( "encoding/json" "log" "net/http" "os" "github.com/digitallysavvy/go-ai/pkg/agent" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { model, err := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")}).LanguageModel("gpt-6-astra") if err != nil { log.Fatal(err) } assistant := agent.NewToolLoopAgent(agent.AgentConfig{Model: model, System: "You are a helpful assistant."}) http.HandleFunc("POST /api/chat", func(w http.ResponseWriter, r *http.Request) { var body struct { Messages json.RawMessage `json:"messages"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { http.Error(w, "invalid body", http.StatusBadRequest) return } err := agent.PipeAgentUIStreamFromUIMessagesToResponse(r.Context(), assistant, agent.CreateAgentUIStreamFromUIMessagesOptions{UIMessages: []byte(body.Messages)}, w) if err != nil { log.Printf("chat: %v", err) } }) log.Fatal(http.ListenAndServe(":8080", nil)) } ``` Messages that fail validation return an error before anything is written. To answer those with a 400, start the stream yourself and pipe it once it succeeds: ```go chunks, _, err := agent.CreateAgentUIStreamFromUIMessages(r.Context(), assistant, agent.CreateAgentUIStreamFromUIMessagesOptions{UIMessages: []byte(body.Messages)}) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if err := ai.PipeUIMessageChunksToResponse(chunks, w, nil); err != nil { log.Printf("chat: %v", err) } ``` Once streaming has started, errors reach the client as `error` chunks. ### `CreateAgentUIStreamResponseFromUIMessages` ```go func CreateAgentUIStreamResponseFromUIMessages(ctx context.Context, agent *agent.ToolLoopAgent, opts agent.CreateAgentUIStreamFromUIMessagesOptions) (*http.Response, error) ``` Returns an `*http.Response` that streams the reply, with the UI message stream status and headers already set. ## Options {/* gen:fields agent.CreateAgentUIStreamFromUIMessagesOptions */} | Field | Type | Description | | --- | --- | --- | | `UIMessages` | `interface{}` | UIMessages are the input UI messages (for example []ai.UIMessage, decoded JSON, or raw JSON bytes/string). Validated against the agent's tools via ai.ValidateUIMessagesForAgent before conversion. | | `ExperimentalRefineToolInput` | `map[string]ai.ToolInputRefiner` | ExperimentalRefineToolInput reconstructs approved tool input from an approval's inputSchemaInput during validation. Optional. | | `ConvertDataPart` | `func(part ai.DataUIPart) types.ContentPart` | ConvertDataPart converts custom UI data parts to text or file model message parts. Data parts are ignored when nil or when the callback returns nil. Mirrors TS createAgentUIStream/createAgentUIStreamResponse/ pipeAgentUIStreamToResponse's `convertDataPart` option (TS #21816). | | `AgentOptions` | `AgentStreamOptions` | AgentOptions carries the remaining agent stream options (RuntimeContext, ToolsContext, CallOptions, AbortSignal-equivalents, etc). Its Prompt and Messages fields are ignored: the converted UI messages are used instead. | | `UIMessageStream` | `ai.UIMessageStreamResultOptions` | UIMessageStream carries ToUIMessageStream/CreateUIMessageStream options (message metadata, ID generation, callbacks, etc). Tools defaults to agent.Tools() when nil. OriginalMessages defaults to the validated UI messages (converted to UIMessageChunk) when nil, kept for backwards compatibility with callers that already computed their own chunks (TS `originalMessages` escape hatch). | {/* /gen:fields */} ## Tool approval signing When `agent.AgentConfig.ExperimentalToolApprovalSecret` is set, these helpers sign each approval request the agent issues and verify each approval response in the posted UI messages before a tool runs. A response with a missing or wrong signature fails with `*ai.InvalidToolApprovalSignatureError`. See [Tool approval helpers](https://goaisdk.com/docs/reference/ai/tool-approvals.md). --- # Batch API > Reference for the experimental batch functions in the Go AI SDK: start, status, results, cancel and list, with their option and result types. Canonical URL: https://goaisdk.com/docs/reference/ai/batch Documentation index: https://goaisdk.com/llms.txt Batch functions submit many requests to a provider for asynchronous processing, then poll for status and read the results. The functions are experimental and carry the `Experimental` prefix. A `Provider` field accepts a `provider.BatchV4` value, a provider that exposes `ExperimentalBatch()`, or nil. When it is nil, the batch runs through the AI Gateway. ## Functions | Function | Returns | | --- | --- | | `ai.ExperimentalStartBatch(ctx, StartBatchOptions)` | `*StartBatchResult`. Submits the batch. | | `ai.ExperimentalGetBatchStatus(ctx, GetBatchStatusOptions)` | `*Batch`. The batch with its latest status. | | `ai.ExperimentalGetBatchResults(ctx, GetBatchResultsOptions)` | `*BatchResultsStream`. Streams the terminal results. | | `ai.ExperimentalCancelBatch(ctx, CancelBatchOptions)` | `*CancelBatchResult`. Requests cancellation. | | `ai.ExperimentalListBatches(ctx, ListBatchesOptions)` | `*ListBatchesResult`. One page of batches. | ## BatchReference and Batch `ai.BatchReference` is the value to persist. It holds `Version`, `ID` and `Provider`, which is enough to check status, read results or cancel from another process. `ai.Batch` embeds `BatchReference` and `provider.BatchV4Status`. ## StartBatchOptions {/* gen:fields ai.StartBatchOptions */} | Field | Type | Description | | --- | --- | --- | | `Provider` | `interface{}` | Provider is a provider.BatchV4 instance, a provider.BatchProvider (a Provider exposing ExperimentalBatch()), or nil. When nil, batches are processed through the AI Gateway (mirrors TypeScript: `globalThis.AI_SDK_DEFAULT_PROVIDER ?? gateway`). | | `Requests` | `[]BatchRequest` | | | `ProviderOptions` | `map[string]interface{}` | | | `WebhookURL` | `string` | WebhookURL, when set, is the URL the provider notifies when the batch reaches a terminal state. Providers that do not support completion webhooks return an unsupported-functionality warning instead of erroring. | | `MaxRetries` | `*int` | | | `Headers` | `map[string]string` | | | `Timeout` | `*time.Duration` | | {/* /gen:fields */} ### StartBatchResult {/* gen:fields ai.StartBatchResult */} | Field | Type | Description | | --- | --- | --- | | (embedded) | `Batch` | Embedded. | | `Warnings` | `[]provider.BatchV4Warning` | | {/* /gen:fields */} ### BatchImageRequest An image request inside a batch. {/* gen:fields ai.BatchImageRequest */} | Field | Type | Description | | --- | --- | --- | | `ID` | `string` | | | `Model` | `string` | | | `Prompt` | `string` | | | `N` | `*int` | | | `Size` | `string` | | | `AspectRatio` | `string` | | | `Seed` | `*int` | | | `Files` | `[]provider.ImageFile` | | | `Mask` | `*provider.ImageFile` | | | `ProviderOptions` | `map[string]interface{}` | | {/* /gen:fields */} ## GetBatchStatusOptions {/* gen:fields ai.GetBatchStatusOptions */} | Field | Type | Description | | --- | --- | --- | | `Provider` | `interface{}` | | | `Batch` | `BatchReference` | | | `ProviderOptions` | `map[string]interface{}` | | | `MaxRetries` | `*int` | | | `Headers` | `map[string]string` | | | `Timeout` | `*time.Duration` | | {/* /gen:fields */} ## GetBatchResultsOptions {/* gen:fields ai.GetBatchResultsOptions */} | Field | Type | Description | | --- | --- | --- | | `Provider` | `interface{}` | | | `Batch` | `BatchReference` | | | `ProviderOptions` | `map[string]interface{}` | | | `MaxRetries` | `*int` | | | `Headers` | `map[string]string` | | | `Timeout` | `*time.Duration` | | {/* /gen:fields */} ### BatchResultsStream `Next()` returns the next `*ai.BatchItemResult` and `io.EOF` when the stream is complete. `Err()` returns the first error. `Close()` releases the stream. The shape matches `provider.TextStream`. {/* gen:fields ai.BatchItemResult */} | Field | Type | Description | | --- | --- | --- | | `Type` | `provider.BatchRequestType` | | | `ID` | `string` | | | `Status` | `provider.BatchItemStatus` | | | `Text` | `*types.GenerateResult` | Text is populated when Type is text and Status is succeeded. | | `Image` | `*types.ImageResult` | Image is populated when Type is image and Status is succeeded. | | `Error` | `*provider.BatchError` | | | `ProviderMetadata` | `map[string]interface{}` | | {/* /gen:fields */} ## CancelBatchOptions {/* gen:fields ai.CancelBatchOptions */} | Field | Type | Description | | --- | --- | --- | | `Provider` | `interface{}` | | | `Batch` | `BatchReference` | | | `ProviderOptions` | `map[string]interface{}` | | | `Headers` | `map[string]string` | | | `Timeout` | `*time.Duration` | | {/* /gen:fields */} {/* gen:fields ai.CancelBatchResult */} | Field | Type | Description | | --- | --- | --- | | `ProviderMetadata` | `map[string]interface{}` | | {/* /gen:fields */} ## ListBatchesOptions {/* gen:fields ai.ListBatchesOptions */} | Field | Type | Description | | --- | --- | --- | | `Provider` | `interface{}` | | | `ProviderOptions` | `map[string]interface{}` | | | `Limit` | `*int` | Limit is optional (nil means unset), mirroring TypeScript's `limit?: number`. An explicit 0 is forwarded to the provider rather than treated as "not set". | | `Cursor` | `string` | | | `MaxRetries` | `*int` | | | `Headers` | `map[string]string` | | | `Timeout` | `*time.Duration` | | {/* /gen:fields */} ### ListBatchesResult {/* gen:fields ai.ListBatchesResult */} | Field | Type | Description | | --- | --- | --- | | `Batches` | `[]Batch` | | | `NextCursor` | `string` | | | `ProviderMetadata` | `map[string]interface{}` | | {/* /gen:fields */} --- # CosineSimilarity > API reference for CosineSimilarity, a Go AI SDK function that measures similarity between two embedding vectors, returning a value from -1 to 1. Canonical URL: https://goaisdk.com/docs/reference/ai/cosine-similarity Documentation index: https://goaisdk.com/llms.txt Calculates the cosine similarity between two embedding vectors. Returns a value between -1 (opposite) and 1 (identical). ## Signature ```go func CosineSimilarity(a, b []float64) (float64, error) ``` ## Parameters | Parameter | Type | Description | |-----------|------|-------------| | a | []float64 | First embedding vector | | b | []float64 | Second embedding vector | ## Return Value Returns a float64 between -1 and 1: - 1.0: Vectors are identical - 0.0: Vectors are orthogonal (no similarity) - -1.0: Vectors are opposite ### Zero and empty vectors A zero vector (all-zero components) or an empty (`len == 0`) vector on either side returns `(0, nil)` — not an error — matching the TypeScript AI SDK. Only a length mismatch between `a` and `b` returns an error, a typed `*errors.InvalidArgumentError` with message `"Vectors must have the same length (vector1Length=…, vector2Length=…)"`. ## Examples ### Basic Similarity Calculation ```go package main import ( "context" "fmt" "log" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { provider := openai.New(openai.Config{ APIKey: "your-api-key", }) model, _ := provider.EmbeddingModel("text-embedding-3-small") // Embed two texts result1, _ := ai.Embed(context.Background(), ai.EmbedOptions{ Model: model, Input: "machine learning", }) result2, _ := ai.Embed(context.Background(), ai.EmbedOptions{ Model: model, Input: "artificial intelligence", }) // Calculate similarity similarity, err := ai.CosineSimilarity(result1.Embedding, result2.Embedding) if err != nil { log.Fatal(err) } fmt.Printf("Similarity: %.4f\n", similarity) } ``` ### Semantic Search ```go // Query embedding queryResult, _ := ai.Embed(ctx, ai.EmbedOptions{ Model: model, Input: "neural networks", }) // Document embeddings documents := []string{ "Deep learning uses neural networks", "The weather is sunny today", "Artificial neural networks are computational models", } // Find most similar var bestMatch string var bestScore float64 for _, doc := range documents { docResult, _ := ai.Embed(ctx, ai.EmbedOptions{ Model: model, Input: doc, }) similarity, _ := ai.CosineSimilarity(queryResult.Embedding, docResult.Embedding) if similarity > bestScore { bestScore = similarity bestMatch = doc } } fmt.Printf("Best match (%.4f): %s\n", bestScore, bestMatch) ``` ### Similarity Threshold ```go similarity, err := ai.CosineSimilarity(embedding1, embedding2) if err != nil { log.Fatal(err) } switch { case similarity > 0.9: fmt.Println("Highly similar") case similarity > 0.7: fmt.Println("Moderately similar") case similarity > 0.5: fmt.Println("Somewhat similar") default: fmt.Println("Not similar") } ``` ### Pairwise Similarity Matrix ```go embeddings := [][]float64{ embedding1, embedding2, embedding3, } fmt.Println("Similarity Matrix:") for i := 0; i < len(embeddings); i++ { for j := 0; j < len(embeddings); j++ { sim, _ := ai.CosineSimilarity(embeddings[i], embeddings[j]) fmt.Printf("%.3f ", sim) } fmt.Println() } ``` ## Error Handling `CosineSimilarity` only returns an error for a length mismatch; a zero or empty vector is not an error condition. ```go similarity, err := ai.CosineSimilarity(a, b) if err != nil { var invalidArg *providererrors.InvalidArgumentError if errors.As(err, &invalidArg) { log.Printf("Dimension mismatch: %v", invalidArg) } else { log.Println("Unknown error:", err) } return } // similarity is 0 (not an error) if either vector is all-zero or empty. ``` ## See Also - [Embed](https://goaisdk.com/docs/reference/ai/embed.md) - Generate embeddings - [EmbedMany](https://goaisdk.com/docs/reference/ai/embed-many.md) - Batch embeddings - EuclideanDistance - Alternative distance metric - DotProduct - Dot product similarity - [RAG Guide](https://goaisdk.com/docs/ai-sdk-core/embeddings.md) --- # Dynamic Tools > API reference for dynamic tools in the Go AI SDK: registering tools at runtime with types.Tool.Type set to types.ToolTypeDynamic instead of a static, typed definition. Canonical URL: https://goaisdk.com/docs/reference/ai/dynamic-tool Documentation index: https://goaisdk.com/llms.txt The Go AI SDK does not have a `DynamicTool` helper function. Instead, a tool is marked as **dynamic** by setting its `Type` field to `types.ToolTypeDynamic`. Dynamic tools are registered and typed at runtime (for example, tools loaded from an MCP server or built from user-supplied configuration) rather than being known at compile time. The SDK tracks dynamic tool calls and results separately from statically defined ones. ## Tool.Type ```go const ( ToolTypeFunction = "function" // default: locally executed function tool ToolTypeDynamic = "dynamic" // dynamic tool whose schema can vary at runtime ToolTypeProviderDefined = "provider" // provider-defined native tool ToolTypeProviderExecuted = "provider-executed" ) ``` Setting `Type: types.ToolTypeDynamic` on a `types.Tool` tells the SDK this tool's shape isn't known statically. It otherwise has the same fields as any other tool: `Name`, `Description`, `Parameters`, and `Execute`. ## Tracking dynamic tool calls and results Results that come from dynamic tools are split out from static ones: - `ai.GenerateTextResult.DynamicToolCalls []types.ToolCall` - `ai.GenerateTextResult.DynamicToolResults []types.ToolResult` - `ai.StreamTextResult.DynamicToolCalls() []types.ToolCall` - `ai.StreamTextResult.DynamicToolResults() []types.ToolResult` - `types.ToolCall.Dynamic bool` and `types.ToolResult.Dynamic bool` ## Examples ### Registering a dynamic tool ```go package main import ( "context" "fmt" "log" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/provider/types" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { p := openai.New(openai.Config{APIKey: "your-api-key"}) model, _ := p.LanguageModel("gpt-6-astra") searchTool := types.Tool{ Name: "search_db", Type: types.ToolTypeDynamic, Description: "Search the database", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "query": map[string]interface{}{"type": "string"}, }, "required": []string{"query"}, }, Execute: func(ctx context.Context, input map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { query := input["query"].(string) return []string{"result for " + query}, nil }, } result, err := ai.GenerateText(context.Background(), ai.GenerateTextOptions{ Model: model, Prompt: "Search the database for invoices", Tools: []types.Tool{searchTool}, StopWhen: []ai.StopCondition{ai.IsStepCount(5)}, }) if err != nil { log.Fatal(err) } fmt.Printf("Dynamic tool calls: %d\n", len(result.DynamicToolCalls)) } ``` ### Checking whether a result came from a dynamic tool ```go for _, tr := range result.ToolResults { if tr.Dynamic { fmt.Printf("dynamic tool %s returned %v\n", tr.ToolName, tr.Result) } } ``` ## See Also - [Tool](https://goaisdk.com/docs/reference/ai/tool.md) - Static tool definition - [Tool Calling Guide](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md) --- # Embed > API reference for Embed, the Go AI SDK function that generates a single vector embedding for one text input using an embedding model. Canonical URL: https://goaisdk.com/docs/reference/ai/embed Documentation index: https://goaisdk.com/llms.txt Generates a vector embedding for a single text input using an embedding model. ## Signature ```go func Embed(ctx context.Context, opts EmbedOptions) (*EmbedResult, error) ``` ## Parameters ### EmbedOptions | Field | Type | Required | Description | |-------|------|----------|-------------| | Model | provider.EmbeddingModel | Yes | Embedding model to use | | Input | string | Yes | Text to embed | ## Return Value ### EmbedResult | Field | Type | Description | |-------|------|-------------| | Embedding | []float64 | Vector embedding | | Usage | types.EmbeddingUsage | Token usage information | ## Examples ### Basic Embedding ```go package main import ( "context" "fmt" "log" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { provider := openai.New(openai.Config{ APIKey: "your-api-key", }) model, err := provider.EmbeddingModel("text-embedding-3-small") if err != nil { log.Fatal(err) } result, err := ai.Embed(context.Background(), ai.EmbedOptions{ Model: model, Input: "The quick brown fox jumps over the lazy dog", }) if err != nil { log.Fatal(err) } fmt.Printf("Embedding dimensions: %d\n", len(result.Embedding)) fmt.Printf("First 5 values: %v\n", result.Embedding[:5]) fmt.Printf("Tokens used: %d\n", result.Usage.TotalTokens) } ``` ### Semantic Search ```go // Embed query queryResult, err := ai.Embed(ctx, ai.EmbedOptions{ Model: model, Input: "machine learning algorithms", }) if err != nil { log.Fatal(err) } // Embed documents docs := []string{ "Neural networks are a type of machine learning model", "The weather today is sunny and warm", "Deep learning uses multiple layers of neural networks", } var docEmbeddings [][]float64 for _, doc := range docs { result, err := ai.Embed(ctx, ai.EmbedOptions{ Model: model, Input: doc, }) if err != nil { log.Fatal(err) } docEmbeddings = append(docEmbeddings, result.Embedding) } // Find most similar document idx, similarity, err := ai.FindMostSimilar(queryResult.Embedding, docEmbeddings) if err != nil { log.Fatal(err) } fmt.Printf("Most similar document: %s\n", docs[idx]) fmt.Printf("Similarity score: %.4f\n", similarity) ``` ### Calculate Similarity ```go result1, err := ai.Embed(ctx, ai.EmbedOptions{ Model: model, Input: "artificial intelligence", }) if err != nil { log.Fatal(err) } result2, err := ai.Embed(ctx, ai.EmbedOptions{ Model: model, Input: "machine learning", }) if err != nil { log.Fatal(err) } similarity, err := ai.CosineSimilarity(result1.Embedding, result2.Embedding) if err != nil { log.Fatal(err) } fmt.Printf("Cosine similarity: %.4f\n", similarity) ``` ### With Different Providers ```go // OpenAI openaiProvider := openai.New(openai.Config{APIKey: "key"}) openaiModel, _ := openaiProvider.EmbeddingModel("text-embedding-3-small") result1, err := ai.Embed(ctx, ai.EmbedOptions{ Model: openaiModel, Input: "test input", }) // Cohere cohereProvider := cohere.New(cohere.Config{APIKey: "key"}) cohereModel, _ := cohereProvider.EmbeddingModel("embed-english-v3.0") result2, err := ai.Embed(ctx, ai.EmbedOptions{ Model: cohereModel, Input: "test input", }) fmt.Printf("OpenAI dimensions: %d\n", len(result1.Embedding)) fmt.Printf("Cohere dimensions: %d\n", len(result2.Embedding)) ``` ## Error Handling ```go result, err := ai.Embed(ctx, opts) if err != nil { switch { case strings.Contains(err.Error(), "model is required"): log.Println("Missing required model parameter") case strings.Contains(err.Error(), "input is required"): log.Println("Missing required input text") case strings.Contains(err.Error(), "embedding failed"): log.Println("Embedding generation error:", err) case errors.Is(err, context.DeadlineExceeded): log.Println("Request timed out") default: log.Println("Unknown error:", err) } return } ``` ## See Also - [EmbedMany](https://goaisdk.com/docs/reference/ai/embed-many.md) - Batch embedding generation - [CosineSimilarity](https://goaisdk.com/docs/reference/ai/cosine-similarity.md) - Calculate similarity between embeddings - [Embedding Models Guide](https://goaisdk.com/docs/ai-sdk-core/embeddings.md) - [RAG Guide](https://goaisdk.com/docs/ai-sdk-core/embeddings.md) --- # Files and skills > Reference for the Go AI SDK functions that upload, inspect, download and delete provider files and upload skills. Canonical URL: https://goaisdk.com/docs/reference/ai/files-and-skills Documentation index: https://goaisdk.com/llms.txt These functions call a provider's Files API or Skills API. The `API` field of each options struct takes a `provider.FilesAPI` (or `provider.SkillsAPI`) or a provider that exposes a `Files()` (or `Skills()`) method. ## Functions | Function | Description | | --- | --- | | `ai.UploadFile(ctx, UploadFileOptions) (*types.UploadFileResult, error)` | Uploads a file and returns a provider reference. | | `ai.GetFileMetadata(ctx, GetFileMetadataOptions) (*provider.FileMetadataResult, error)` | Returns metadata for an uploaded file. | | `ai.DownloadFile(ctx, DownloadFileOptions) (*provider.DownloadFileResult, error)` | Downloads an uploaded file. | | `ai.DeleteFile(ctx, DeleteFileOptions) (*provider.DeleteFileResult, error)` | Deletes an uploaded file. | | `ai.UploadSkill(ctx, UploadSkillOptions) (*types.UploadSkillResult, error)` | Uploads a skill. | ## UploadFileOptions {/* gen:fields ai.UploadFileOptions */} | Field | Type | Description | | --- | --- | --- | | `API` | `interface{}` | | | `Data` | `interface{}` | | | `MediaType` | `string` | | | `Filename` | `string` | | | `ProviderOptions` | `map[string]interface{}` | | | `Headers` | `map[string]string` | Headers are additional HTTP headers to send with the upload request. Only applicable for HTTP-based providers. | {/* /gen:fields */} ## GetFileMetadataOptions {/* gen:fields ai.GetFileMetadataOptions */} | Field | Type | Description | | --- | --- | --- | | `API` | `interface{}` | API is a provider.FilesAPI instance or a provider.FilesProvider (a Provider exposing a Files() method). | | `File` | `types.ProviderReference` | File is the provider reference of the file, as returned by UploadFile. | | `Headers` | `map[string]string` | | | `ProviderOptions` | `map[string]interface{}` | | {/* /gen:fields */} ## DownloadFileOptions {/* gen:fields ai.DownloadFileOptions */} | Field | Type | Description | | --- | --- | --- | | `API` | `interface{}` | API is a provider.FilesAPI instance or a provider.FilesProvider (a Provider exposing a Files() method). | | `File` | `types.ProviderReference` | File is the provider reference of the file, as returned by UploadFile. | | `Headers` | `map[string]string` | | | `ProviderOptions` | `map[string]interface{}` | | {/* /gen:fields */} ## DeleteFileOptions {/* gen:fields ai.DeleteFileOptions */} | Field | Type | Description | | --- | --- | --- | | `API` | `interface{}` | API is a provider.FilesAPI instance or a provider.FilesProvider (a Provider exposing a Files() method). | | `File` | `types.ProviderReference` | File is the provider reference of the file, as returned by UploadFile. | | `Headers` | `map[string]string` | | | `ProviderOptions` | `map[string]interface{}` | | {/* /gen:fields */} ## UploadSkillOptions {/* gen:fields ai.UploadSkillOptions */} | Field | Type | Description | | --- | --- | --- | | `API` | `interface{}` | | | `Files` | `[]UploadSkillFile` | | | `DisplayTitle` | `string` | | | `ProviderOptions` | `map[string]interface{}` | | {/* /gen:fields */} ## Errors | Variable | Returned when | | --- | --- | | `ai.ErrFilesAPINotSupported` | The provider does not expose a `Files()` method. | | `ai.ErrFileMetadataNotSupported` | The provider's Files API cannot return metadata. | | `ai.ErrFileDownloadNotSupported` | The provider's Files API cannot download files. | | `ai.ErrFileDeleteNotSupported` | The provider's Files API cannot delete files. | | `ai.ErrSkillsAPINotSupported` | The provider does not expose a `Skills()` method. | --- # GenerateID / CreateIDGenerator > API reference for GenerateID and CreateIDGenerator, Go AI SDK helpers that create unique identifier strings for tool calls and requests. Canonical URL: https://goaisdk.com/docs/reference/ai/generate-id Documentation index: https://goaisdk.com/llms.txt Generates a unique identifier string. Useful for creating tool call IDs or request IDs. ## Signature ```go func GenerateID() string ``` Not cryptographically secure (uses `math/rand`, matching the TypeScript AI SDK's `Math.random()`-based implementation). Equivalent to calling `CreateIDGenerator()()` with default options — a 16-character random string from the alphabet `0-9A-Za-z`. ## Return Value Returns a unique string identifier (16-character random alphanumeric string by default). ## CreateIDGenerator Builds a custom ID generator function, mirroring the TypeScript AI SDK's `createIdGenerator` / `generateId` from `@ai-sdk/provider-utils`. ```go func CreateIDGenerator(opts ...CreateIDGeneratorOptions) (IDGenerator, error) type CreateIDGeneratorOptions struct { Prefix string // prepended (with Separator) to every generated ID Separator string // between Prefix and the random part; default "-" Size int // length of the random part; default 16 Alphabet string // character set for the random part; default "0-9A-Za-z" } ``` `CreateIDGenerator` returns a `*errors.InvalidArgumentError` if `Prefix` is set and `Separator` appears inside `Alphabet` (this would make prefix detection ambiguous — matches the TS SDK's synchronous throw at construction time). ```go gen, err := ai.CreateIDGenerator(ai.CreateIDGeneratorOptions{ Prefix: "msg", Size: 24, }) if err != nil { log.Fatal(err) } id := gen() // e.g. "msg-aB3dE7fG9hJ2kL4mN6pQ8rS" ``` ## Examples ### Generate Request ID ```go package main import ( "fmt" "github.com/digitallysavvy/go-ai/pkg/ai" ) func main() { requestID := ai.GenerateID() fmt.Printf("Request ID: %s\n", requestID) } ``` ### Custom Tool Call ID ```go toolCall := types.ToolCall{ ID: ai.GenerateID(), ToolName: "my_tool", Arguments: map[string]interface{}{ "param": "value", }, } ``` ### Session Tracking ```go type Session struct { ID string CreatedAt time.Time } session := Session{ ID: ai.GenerateID(), CreatedAt: time.Now(), } fmt.Printf("Session: %s\n", session.ID) ``` ## See Also - [Tool](https://goaisdk.com/docs/reference/ai/tool.md) - Tool definition - [GenerateText](https://goaisdk.com/docs/reference/ai/generate-text.md) - Text generation --- # GenerateImage > API reference for GenerateImage, the Go AI SDK function that generates images from text prompts using a configured image model and provider. Canonical URL: https://goaisdk.com/docs/reference/ai/generate-image Documentation index: https://goaisdk.com/llms.txt Generates images from prompts using an image model. ## Signature ```go func GenerateImage(ctx context.Context, opts GenerateImageOptions) (*GenerateImageResult, error) ``` ## GenerateImageOptions | Field | Type | Required | Description | |---|---|---|---| | `Model` | `provider.ImageModel` | Yes | Image model implementation | | `Prompt` | `string` | Yes | Prompt text | | `N` | `*int` | No | Number of images requested | | `Size` | `string` | No | Provider image size string | | `AspectRatio` | `string` | No | Aspect ratio string | | `Seed` | `*int` | No | Deterministic seed | | `Quality` | `string` | No | Provider quality setting | | `Style` | `string` | No | Provider style setting | | `Files` | `[]provider.ImageFile` | No | Input files for edit/variation flows | | `Mask` | `*provider.ImageFile` | No | Optional mask file | | `Headers` | `map[string]string` | No | Extra request headers | | `MaxImagesPerCall` | `int` | No | Override the model's per-call image limit for automatic batching | | `MaxRetries` | `*int` | No | Maximum retries per image model call; defaults to 2, set to 0 to disable | | `ProviderOptions` | `map[string]interface{}` | No | Provider-specific options | ## GenerateImageResult | Field | Type | Description | |---|---|---| | `Image` | `types.GeneratedFile` | First generated image | | `Images` | `[]types.GeneratedFile` | All generated images | | `Warnings` | `[]types.Warning` | Provider warnings | | `Responses` | `[]*types.ResponseMetadata` | Provider response metadata entries | | `ProviderMetadata` | `map[string]interface{}` | Provider-specific metadata | | `Usage` | `types.ImageUsage` | Image generation usage metrics | `GeneratedFile.Data` contains binary image bytes. When a provider returns base64 image strings, `GenerateImage` decodes them into `GeneratedFile.Data` so JSON encoding of the result preserves the provider base64 value instead of double-encoding it. ## Example ```go result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{ Model: imageModel, Prompt: "A serene landscape at sunset", }) if err != nil { // handle } _ = os.WriteFile("output.png", result.Image.Data, 0644) ``` --- # GenerateSpeech > API reference for GenerateSpeech, the Go AI SDK function that generates speech audio from text using a configured speech model and provider. Canonical URL: https://goaisdk.com/docs/reference/ai/generate-speech Documentation index: https://goaisdk.com/llms.txt Generates speech audio from text. ## Signature ```go func GenerateSpeech(ctx context.Context, opts GenerateSpeechOptions) (*GenerateSpeechResult, error) ``` ## GenerateSpeechOptions | Field | Type | Required | Description | |---|---|---|---| | `Model` | `provider.SpeechModel` | Yes | Speech model | | `Text` | `string` | Yes | Input text | | `Voice` | `string` | No | Voice identifier | | `Speed` | `*float64` | No | Speech speed | | `OutputFormat` | `string` | No | Desired audio format, such as `mp3` or `wav` | | `Instructions` | `string` | No | Provider-dependent delivery instructions | | `Language` | `string` | No | Speech language code or provider-supported automatic language mode | | `ProviderOptions` | `map[string]interface{}` | No | Provider-specific options keyed by provider name | | `Headers` | `map[string]string` | No | Extra request headers | | `MaxRetries` | `*int` | No | Maximum retries per speech call. Defaults to 2; set to 0 to disable | | `Telemetry` | `*ai.TelemetrySettings` | No | Observability configuration for this call. `ExperimentalTelemetry` is a deprecated alias; `Telemetry` wins when both are set | ### Telemetry When a telemetry integration is registered (global via `telemetry.RegisterTelemetryIntegration`, or per-call via `Telemetry.Integrations`), `GenerateSpeech` emits an `ai.generateSpeech` root span covering the whole call (including retries), with `gen_ai.output.type: "speech"`, the input text (`ai.request.text`, input-gated), the generated audio's size/media type/format (`ai.response.audio.*`, output-gated), and the provider's reported usage flattened into numeric `ai.usage.*` / `gen_ai.usage.*` attributes (e.g. `ai.usage.characters` for a provider that reports `{"characters": N}`; see the [Telemetry guide](https://goaisdk.com/docs/ai-sdk-core/telemetry.md#speech-and-transcription) for the key-normalization rules). A failed call — including a provider response with no audio — ends the span with `codes.Error` status. ## GenerateSpeechResult | Field | Type | Description | |---|---|---| | `Audio` | `ai.GeneratedAudioFile` | Generated audio file payload | | `Warnings` | `[]types.Warning` | Provider warnings (if exposed) | | `Responses` | `[]ai.SpeechModelResponseMetadata` | Response metadata entries (`timestamp`, `modelId`, optional `headers`, optional `body`) | | `ProviderMetadata` | `map[string]interface{}` | Provider-specific metadata | ## GeneratedAudioFile ```go type GeneratedAudioFile struct { Data []byte `json:"data,omitempty"` URL string `json:"url,omitempty"` MediaType string `json:"mediaType"` Format string `json:"format"` } func (f GeneratedAudioFile) Base64() string func (f GeneratedAudioFile) Uint8Array() []byte ``` ## Deprecated Compatibility Aliases The TypeScript SDK still exports deprecated `experimental_generateSpeech` and `Experimental_SpeechResult` names. Go exposes equivalent migration aliases: ```go func ExperimentalGenerateSpeech(ctx context.Context, opts GenerateSpeechOptions) (*GenerateSpeechResult, error) type Experimental_SpeechResult = GenerateSpeechResult ``` ## Example ```go result, err := ai.GenerateSpeech(ctx, ai.GenerateSpeechOptions{ Model: speechModel, Text: "Hello from Go AI SDK", Voice: "alloy", }) if err != nil { // handle } _ = os.WriteFile("output.mp3", result.Audio.Data, 0644) ``` --- # Harness Sandbox: Vercel > API reference for pkg/harness/sandbox/vercel, a Go port of the Vercel Sandbox harness provider Canonical URL: https://goaisdk.com/docs/reference/ai/harness-sandbox-vercel Documentation index: https://goaisdk.com/llms.txt `pkg/harness/sandbox/vercel` runs [`pkg/harness`](#harness-docs) sandboxes on [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox), with network policies and template snapshots. It is a Go port of the TypeScript harness's Vercel Sandbox provider. ## Package ```go import "github.com/digitallysavvy/go-ai/pkg/harness/sandbox/vercel" ``` ## Auth options ```go type Credentials struct { Token string TeamID string ProjectID string } ``` `ResolveCredentials` mirrors the TypeScript provider's credential resolution: explicit `Token`/`TeamID`/`ProjectID` win when all three fields are set (setting only some of them is an error); otherwise `VERCEL_OIDC_TOKEN` is read from the environment and decoded as a JWT for its `owner_id`/`project_id` claims. ```go func ResolveCredentials(explicit Credentials) (Credentials, error) func HasConfiguredCredentials(explicit Credentials) bool ``` `ErrNoCredentials` is returned when neither explicit credentials nor `VERCEL_OIDC_TOKEN` are available. See [Known differences: Vercel Sandbox has no OIDC token refresh loop](https://goaisdk.com/docs/migration-guides/known-differences.md#vercel-sandbox-no-oidc-token-refresh-loop) — unlike the TypeScript provider, this port does not run a background `@vercel/oidc` refresh loop; `VERCEL_OIDC_TOKEN` is re-read from the environment on each call. ## Creating a session ```go func CreateNetworkSandboxSession(ctx context.Context, opts CreateSessionOptions) (harness.NetworkSandboxSession, error) type CreateSessionOptions struct { Credentials Credentials BaseURL string // overrides the Vercel API base URL (tests only) SandboxID string Name string Runtime string Image string Source *SnapshotSource TimeoutMs int64 Ports []int Persistent *bool NetworkPolicy *NetworkPolicy SnapshotExpiration *int64 Resources *ResourcesParams Env map[string]string Tags map[string]string Region string FailoverRegions []string KeepLastSnapshots *KeepLastSnapshotsParams Template *Template // one-time preparation recipe, cached by Template.Identity } ``` ```go session, err := vercel.CreateNetworkSandboxSession(ctx, vercel.CreateSessionOptions{ Runtime: "node22", TimeoutMs: 10 * 60 * 1000, Env: map[string]string{"NODE_ENV": "production"}, }) ``` When `Template` is set, `CreateNetworkSandboxSession` reuses (or creates and caches) a snapshot for `Template.Identity`, running `Template.Prepare` only the first time that identity is seen, then forks a live sandbox from the snapshot — this is the template-snapshot behavior mentioned in the v0.5.0 release notes. ## Resuming a session ```go func ResumeNetworkSandboxSession(ctx context.Context, opts ResumeSessionOptions) (harness.NetworkSandboxSession, error) type ResumeSessionOptions struct { Credentials Credentials BaseURL string SandboxID string } ``` ```go session, err := vercel.ResumeNetworkSandboxSession(ctx, vercel.ResumeSessionOptions{ SandboxID: existingSandboxID, }) ``` ## Wrapping an existing sandbox If you already created a `*vercel.Sandbox` through the lower-level `CreateSandbox`/`GetSandbox` client calls, wrap it instead of going through `CreateNetworkSandboxSession`: ```go func NetworkSessionFromNativeSandbox(sandbox *Sandbox) harness.NetworkSandboxSession func SessionFromNativeSandbox(sandbox *Sandbox) providerutils.SandboxSession ``` `SessionFromNativeSandbox` returns the restricted (file I/O + exec only) session interface; `NetworkSessionFromNativeSandbox` returns the full network-capable session, including `SetNetworkPolicy`, `SetRequestTransformations`, and port endpoint resolution. ## Harness docs {#harness-docs} There is no separate top-level reference page for `pkg/harness` itself yet. See the Harness section of [Migrating from the TypeScript AI SDK](https://goaisdk.com/docs/migration-guides/from-typescript-ai-sdk.md#harness) for the mapping from `@ai-sdk/harness` concepts (`HarnessV1Session`, `Agent`/`AgentSession`, adapter packages, sandbox providers) to their Go equivalents, and the [Migrating from v0.4.x to v0.5.0](https://goaisdk.com/docs/migration-guides/from-v0.4-to-v0.5.md) guide's "Provider updates to review" section for what's new this cycle. ## See Also - [Known differences from the TypeScript AI SDK](https://goaisdk.com/docs/migration-guides/known-differences.md) - [Migrating from the TypeScript AI SDK](https://goaisdk.com/docs/migration-guides/from-typescript-ai-sdk.md) --- # HasToolCall > Built-in StopCondition that stops the tool-calling loop when a specific tool is called Canonical URL: https://goaisdk.com/docs/reference/ai/has-tool-call Documentation index: https://goaisdk.com/llms.txt Returns a `StopCondition` that stops the tool-calling loop when the model calls a tool with one of the given names. Use this for **semantic completion signals** — for example, when the model is expected to call a `"finish"` or `"submit"` tool to indicate that it has completed its task. ## Signature ```go func HasToolCall(toolNames ...string) StopCondition ``` ## Parameters | Parameter | Type | Description | |-----------|------|-------------| | toolNames | ...string | Names of the tools whose invocation stops the loop | ## Return Value A `StopCondition` function. When the condition fires it returns the reason string `"tool 'toolName' was called"`; otherwise it returns `""`. ```go type StopCondition func(state StopConditionState) string ``` ## Behavior `HasToolCall` inspects the **most recent step's tool calls**. If any of the given `toolNames` appears in them the condition fires. It returns `""` when there are no completed steps yet (safe to evaluate on step zero). ## Examples ### Semantic completion signal Define a `finish` tool that the model calls when it is satisfied with its research. Use `HasToolCall("finish")` to stop the loop the moment it fires. Pair it with `IsStepCount` as a hard-limit fallback in case the model never calls the tool. ```go finishTool := types.Tool{ Name: "finish", Description: "Signal that the task is complete. Provide a summary of your findings.", Parameters: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "summary": map[string]interface{}{ "type": "string", "description": "Final summary of the research", }, }, "required": []string{"summary"}, }, Execute: func(ctx context.Context, args map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) { return map[string]interface{}{"status": "complete", "summary": args["summary"]}, nil }, } result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: `Research Go concurrency patterns. When you are satisfied with your findings, call the finish tool.`, Tools: []types.Tool{searchTool, finishTool}, StopWhen: []ai.StopCondition{ ai.HasToolCall("finish"), // stop on semantic completion ai.IsStepCount(10), // safety ceiling }, }) if err != nil { log.Fatal(err) } fmt.Printf("StopReason: %s\n", result.StopReason) // → "StopReason: tool 'finish' was called" (if model finished early) // → "StopReason: maximum number of steps (10) reached" (if it hit the ceiling) ``` ### With ToolLoopAgent ```go myAgent := agent.NewToolLoopAgent(agent.AgentConfig{ Model: model, Tools: []types.Tool{searchTool, submitTool}, StopWhen: []ai.StopCondition{ ai.HasToolCall("submit"), ai.IsStepCount(20), }, }) result, err := myAgent.Execute(ctx, "Complete the report and submit it") fmt.Printf("StopReason: %s\n", result.StopReason) ``` ### Condition ordering and side effects `EvaluateStopConditions` runs **all** conditions before returning the first match. This means every condition in `StopWhen` always executes, regardless of order. Side-effectful conditions (e.g. logging, metric recording) can therefore be placed anywhere and are guaranteed to run. ```go // Both conditions always run; the first non-empty reason is returned. StopWhen: []ai.StopCondition{ ai.HasToolCall("finish"), // fires → "tool 'finish' was called" ai.IsStepCount(20), // also evaluated, but result ignored if finish fired first }, ``` ## See Also - [IsStepCount](https://goaisdk.com/docs/reference/ai/step-count-is.md) — stop after n steps (safety ceiling pattern) - [Loop Control](https://goaisdk.com/docs/agents/loop-control.md) — full guide with examples - [GenerateText](https://goaisdk.com/docs/reference/ai/generate-text.md) — `GenerateTextOptions.StopWhen` field - [ToolLoopAgent](https://goaisdk.com/docs/reference/ai/tool-loop-agent.md) — `AgentConfig.StopWhen` field - [Stop-When Example](https://github.com/digitallysavvy/go-ai/blob/main/examples/agents/callbacks/stop-when/main.go) — runnable example --- # LlamaIndex Adapter > API reference for pkg/llamaindex, a Go port of @ai-sdk/llamaindex that adapts LlamaIndex chat-engine responses into AI SDK UI message stream chunks. Canonical URL: https://goaisdk.com/docs/reference/ai/llamaindex Documentation index: https://goaisdk.com/llms.txt `pkg/llamaindex` adapts a LlamaIndex chat-engine response stream into AI SDK UI message stream chunks. It is a Go port of `@ai-sdk/llamaindex`. The TypeScript package has no dependency on the real `llamaindex` npm package: its `EngineResponse` is a locally-defined `{ delta: string }` shape, and the adapter only ever reads `.delta` off of it. Go has no LlamaIndex SDK to bind against either — this package defines the same minimal input contract. You bridge whatever LlamaIndex client you use (HTTP, gRPC, a wrapped Python subprocess, etc.) into a `<-chan EngineResponse` of delta chunks; the package does not talk to LlamaIndex itself. ## Package ```go import "github.com/digitallysavvy/go-ai/pkg/llamaindex" ``` ## EngineResponse ```go type EngineResponse struct { Delta string } ``` The minimal Go analog of a LlamaIndex chat-engine streamed response chunk. ## StreamCallbacks ```go type StreamCallbacks struct { // OnStart is called once, before any chunk is processed. OnStart func() error // OnToken is called for every delta chunk, including empty ones // produced by leading-whitespace trimming. OnToken func(token string) error // OnText is called for every delta chunk, identically to OnToken. OnText func(text string) error // OnFinal is called once, with the full concatenation of every // (possibly trimmed) delta chunk, after the input stream ends and // before the "text-end" chunk is emitted. OnFinal func(completion string) error } ``` ## ToUIMessageStream ```go func ToUIMessageStream( ctx context.Context, stream <-chan EngineResponse, callbacks *StreamCallbacks, ) (<-chan ai.UIMessageChunk, <-chan error) ``` Converts a LlamaIndex chat-engine response stream into AI SDK UI message stream chunks: a single text part (id `"1"`) spanning `text-start` → `text-delta` (one or more) → `text-end`. Leading whitespace is trimmed from the stream exactly once: every delta is trimmed until the first delta that is non-empty *after* trimming is seen; after that, deltas pass through unmodified, even if empty. `callbacks` may be `nil`. The returned error channel receives at most one error: a callback error (from `OnStart`/`OnToken`/`OnText`/`OnFinal`) or `ctx.Err()` if `ctx` is cancelled mid-stream. Both channels are closed when the stream is fully drained. ### Example ```go package main import ( "context" "log" "github.com/digitallysavvy/go-ai/pkg/llamaindex" ) func consumeEngineStream(ctx context.Context, engineDeltas <-chan llamaindex.EngineResponse) { chunks, errs := llamaindex.ToUIMessageStream(ctx, engineDeltas, &llamaindex.StreamCallbacks{ OnFinal: func(completion string) error { log.Printf("final completion: %s", completion) return nil }, }) for chunk := range chunks { // chunk is an ai.UIMessageChunk (map[string]interface{}) with // "type": "text-start" | "text-delta" | "text-end". _ = chunk } if err := <-errs; err != nil { log.Printf("stream error: %v", err) } } ``` To serve these chunks over HTTP in the AI SDK's UI message stream protocol (so a TS `useChat` frontend can consume them directly), merge the returned channel into a stream built with `ai.CreateUIMessageStreamWithOptions` via `UIMessageStreamWriter.Merge`, the same integration point used for any non-`StreamText` chunk producer. See [UI message stream helpers](https://goaisdk.com/docs/reference/ai/stream-transport-helpers.md). ## See Also - [UI message stream helpers](https://goaisdk.com/docs/reference/ai/stream-transport-helpers.md) - [Migrating from v0.4.x to v0.5.0](https://goaisdk.com/docs/migration-guides/from-v0.4-to-v0.5.md) --- # Middleware Aliases > Reference for the middleware type aliases, wrapper functions, and built-in middleware constructors that pkg/ai re-exports for discoverability. Canonical URL: https://goaisdk.com/docs/reference/ai/middleware-aliases Documentation index: https://goaisdk.com/llms.txt The `pkg/ai` package re-exports middleware APIs for parity-oriented discoverability. ## Type aliases - `ai.LanguageModelMiddleware` - `ai.EmbeddingModelMiddleware` - `ai.ImageModelMiddleware` - `ai.ExtractReasoningOptions` - `ai.ExtractJSONOptions` - `ai.AddToolInputExamplesOptions` ## Wrapper functions ```go func WrapLanguageModel(model provider.LanguageModel, middleware []*ai.LanguageModelMiddleware, modelID, providerID *string) provider.LanguageModel func WrapEmbeddingModel(model provider.EmbeddingModel, middleware []*ai.EmbeddingModelMiddleware, modelID, providerID *string) provider.EmbeddingModel func WrapImageModel(model provider.ImageModel, middleware []*ai.ImageModelMiddleware, modelID, providerID *string) provider.ImageModel func WrapProvider(p provider.Provider, languageModelMiddleware []*ai.LanguageModelMiddleware, embeddingModelMiddleware []*ai.EmbeddingModelMiddleware) provider.Provider ``` `WrapProvider` accepts image model middleware through the variadic `WithImageModelMiddleware(...)` option (see `pkg/registry` for the same option on `NewRegistry`): ```go wrapped := ai.WrapProvider(baseProvider, languageModelMiddleware, embeddingModelMiddleware, ai.WithImageModelMiddleware([]*ai.ImageModelMiddleware{myImageMiddleware}), ) ``` ## Built-in middleware constructors ```go func SimulateStreamingMiddleware() *ai.LanguageModelMiddleware func DefaultSettingsMiddleware(settings *provider.GenerateOptions) *ai.LanguageModelMiddleware func ExtractReasoningMiddleware(options *ai.ExtractReasoningOptions) *ai.LanguageModelMiddleware func ExtractJSONMiddleware(options *ai.ExtractJSONOptions) *ai.LanguageModelMiddleware func AddToolInputExamplesMiddleware(options *ai.AddToolInputExamplesOptions) *ai.LanguageModelMiddleware ``` ### AddToolInputExamplesOptions.Remove `Remove` is `*bool`, not `bool`. `nil` means "remove the examples" (the TS default); pass an explicit pointer to keep them: ```go // Removes InputExamples from the tool schema (nil == remove, the default). ai.AddToolInputExamplesMiddleware(&ai.AddToolInputExamplesOptions{}) // Explicitly keeps InputExamples. keep := false ai.AddToolInputExamplesMiddleware(&ai.AddToolInputExamplesOptions{Remove: &keep}) ``` ### LanguageModelMiddleware.OverrideSupportedURLs `OverrideSupportedURLs func(model provider.LanguageModel) map[string][]string` lets middleware override which URL patterns (by media type) a wrapped model reports as natively supported. A wrapped model without this field set forwards the underlying model's `SupportedURLs()` unchanged. --- # IsStepCount > API reference for IsStepCount (formerly StepCountIs), a built-in StopCondition that stops the Go AI SDK's tool-calling loop once a given number of steps completes. Canonical URL: https://goaisdk.com/docs/reference/ai/step-count-is Documentation index: https://goaisdk.com/llms.txt Returns a `StopCondition` that stops the tool-calling loop once exactly `n` steps have completed (TS `isStepCount`). `StepCountIs` is the deprecated name for the same function and still works. Pass it to `StopWhen` in `GenerateTextOptions` or `AgentConfig`. ## Signature ```go func IsStepCount(n int) StopCondition // Deprecated: use IsStepCount. func StepCountIs(n int) StopCondition ``` ## Parameters | Parameter | Type | Description | |-----------|------|-------------| | n | int | Stop when `len(state.Steps) == n` | ## Return Value A `StopCondition` function. When the condition fires it returns the reason string `"maximum number of steps (n) reached"`; otherwise it returns `""`. ```go type StopCondition func(state StopConditionState) string ``` ## Behavior `IsStepCount(n)` checks `len(steps) == n`, the same exact match as TypeScript's `isStepCount(n)` (`steps.length === n`). Stop conditions are evaluated after every step and the step count grows by one each time, so the threshold is never skipped. ## Examples ### Basic ceiling ```go result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Analyze the dataset and create a summary", Tools: tools, StopWhen: []ai.StopCondition{ ai.IsStepCount(5), }, }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) fmt.Printf("Stopped because: %s\n", result.StopReason) // → "Stopped because: maximum number of steps (5) reached" ``` ### Combined with HasToolCall (safety ceiling pattern) Place `HasToolCall` first so the semantic completion signal fires before the hard ceiling. Because `EvaluateStopConditions` runs **all** conditions before returning the first match, both conditions always execute regardless of order. ```go StopWhen: []ai.StopCondition{ ai.HasToolCall("finish"), // semantic completion ai.IsStepCount(10), // hard limit fallback }, ``` ### With ToolLoopAgent ```go myAgent := agent.NewToolLoopAgent(agent.AgentConfig{ Model: model, Tools: tools, StopWhen: []ai.StopCondition{ ai.IsStepCount(8), }, }) result, err := myAgent.Execute(ctx, "Complete the task") fmt.Printf("Steps: %d, StopReason: %s\n", len(result.Steps), result.StopReason) ``` ## Defaults | Scenario | Behavior | |----------|----------| | Neither `StopWhen` nor `MaxSteps` set | `GenerateText` / `StreamText` default to `IsStepCount(1)`; `agent.NewToolLoopAgent` defaults to `IsStepCount(20)` | | `MaxSteps: &n` set, `StopWhen` not set | Converts to `StopWhen{IsStepCount(n)}` | | Both set | `StopWhen` takes precedence | ## See Also - [HasToolCall](https://goaisdk.com/docs/reference/ai/has-tool-call.md) — stop when a specific tool is called - [Loop Control](https://goaisdk.com/docs/agents/loop-control.md) — full guide with examples - [GenerateText](https://goaisdk.com/docs/reference/ai/generate-text.md) — `GenerateTextOptions.StopWhen` field - [ToolLoopAgent](https://goaisdk.com/docs/reference/ai/tool-loop-agent.md) — `AgentConfig.StopWhen` field --- # StopCondition > Type reference for StopCondition and StopConditionState in the Go AI SDK, covering the function type, evaluation order, and usage with StopWhen. Canonical URL: https://goaisdk.com/docs/reference/ai/stop-condition Documentation index: https://goaisdk.com/llms.txt A function type that determines whether the tool-calling loop should stop after a step. Pass one or more `StopCondition` values to the `StopWhen` field in `GenerateTextOptions` or `AgentConfig`. ## Type Definition ```go type StopCondition func(state StopConditionState) string ``` A `StopCondition` is called after each step. Return a non-empty reason string to stop the loop; return `""` to continue. ## StopConditionState `StopConditionState` is passed to every `StopCondition` after each step completes. ```go type StopConditionState struct { // Steps completed so far. The step that just finished is the last element. Steps []types.StepResult // Full message history including the latest tool-result messages. Messages []types.Message // Accumulated token usage across all completed steps. Usage types.Usage } ``` | Field | Type | Description | |-------|------|-------------| | Steps | `[]types.StepResult` | Completed steps; last element is the most recent step | | Messages | `[]types.Message` | Full conversation history at this point in the loop | | Usage | `types.Usage` | Token usage summed across all steps so far | ## EvaluateStopConditions ```go func EvaluateStopConditions(conditions []StopCondition, state StopConditionState) string ``` The loop engine calls `EvaluateStopConditions` internally after every step. It runs **all** conditions before inspecting any result, then returns the first non-empty reason string (or `""` if none fired). Key invariant: **every condition always executes**, regardless of order. Side-effectful conditions (logging, metrics) are therefore safe to place anywhere in the slice and are guaranteed to run even when an earlier condition fires first. ## Examples ### Custom closure Any function with the right signature is a valid `StopCondition`: ```go stopOnError := func(state ai.StopConditionState) string { if len(state.Steps) == 0 { return "" } last := state.Steps[len(state.Steps)-1] for _, result := range last.ToolResults { if result.Error != nil { return "tool returned an error" } } return "" } result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Search for recent Go releases", Tools: []types.Tool{searchTool}, StopWhen: []ai.StopCondition{stopOnError, ai.IsStepCount(10)}, }) ``` ### Token budget Stop before the model runs out of context by inspecting `state.Usage`: ```go tokenBudget := func(maxTokens int) ai.StopCondition { return func(state ai.StopConditionState) string { if int(state.Usage.GetTotalTokens()) >= maxTokens { return fmt.Sprintf("token budget of %d reached", maxTokens) } return "" } } result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Summarize this document", Tools: tools, StopWhen: []ai.StopCondition{ tokenBudget(50_000), ai.HasToolCall("finish"), ai.IsStepCount(20), }, }) if err != nil { log.Fatal(err) } fmt.Printf("StopReason: %s\n", result.StopReason) ``` ### Side-effectful condition (metrics) Because `EvaluateStopConditions` always runs all conditions, you can record metrics in a `StopCondition` without worrying about evaluation order: ```go recordStepMetric := func(state ai.StopConditionState) string { metrics.RecordStep(len(state.Steps), int(state.Usage.GetTotalTokens())) return "" // never stops the loop on its own } result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{ Model: model, Prompt: "Summarize this document", StopWhen: []ai.StopCondition{ recordStepMetric, // always runs; records every step ai.HasToolCall("done"), // may stop early ai.IsStepCount(15), // hard ceiling }, }) ``` ## Condition ordering guarantee `EvaluateStopConditions` collects all reason strings in a single pass, then returns the first non-empty one. This means: 1. **Every condition runs on every step** — no short-circuit evaluation. 2. The **first** condition with a non-empty reason wins (slice order matters for which reason is surfaced, not for which conditions execute). 3. The winning reason is returned as `GenerateTextResult.StopReason`. ```go // Both conditions always run; the first non-empty reason is returned. StopWhen: []ai.StopCondition{ ai.HasToolCall("finish"), // fires → "tool 'finish' was called" ai.IsStepCount(20), // also evaluated, but result ignored if finish fired first }, ``` ## See Also - [IsStepCount](https://goaisdk.com/docs/reference/ai/step-count-is.md) — built-in condition: stop after n steps - [HasToolCall](https://goaisdk.com/docs/reference/ai/has-tool-call.md) — built-in condition: stop when a tool is called - [Loop Control](https://goaisdk.com/docs/agents/loop-control.md) — full guide with examples - [GenerateText](https://goaisdk.com/docs/reference/ai/generate-text.md) — `GenerateTextOptions.StopWhen` field - [ToolLoopAgent](https://goaisdk.com/docs/reference/ai/tool-loop-agent.md) — `AgentConfig.StopWhen` field --- # Streaming transcription and translation > Reference for the experimental Go AI SDK functions that stream audio to a model for live transcription and speech translation, with stream part types. Canonical URL: https://goaisdk.com/docs/reference/ai/streaming-audio Documentation index: https://goaisdk.com/llms.txt Two experimental functions send raw audio to a model as it arrives and stream results back. ```go func ExperimentalStreamTranscribe(ctx context.Context, opts StreamTranscribeOptions) (*StreamTranscriptionResult, error) func ExperimentalStreamTranslate(ctx context.Context, opts StreamTranslateOptions) (*StreamTranslationResult, error) ``` `FullStream()` on each result is single-consumer. Read it before any other result accessor. An accessor such as `ProviderMetadata()` consumes the stream, and `FullStream()` is unavailable afterwards. ## Audio sources | Function | Description | | --- | --- | | `ai.NewChannelAudioStream(chunks <-chan []byte, onCancel func(reason error)) provider.AudioStream` | Wraps a channel of raw audio chunks, for live input. The producer closes the channel at the end of the audio. `onCancel` runs at most once if the consumer cancels. | | `ai.NewReaderAudioStream(r io.Reader, chunkSize int) provider.AudioStream` | Reads audio from `r` in `chunkSize`-byte chunks. `chunkSize` defaults to 4096. Cancel closes `r` when it is an `io.Closer`. | ## StreamTranscribeOptions {/* gen:fields ai.StreamTranscribeOptions */} | Field | Type | Description | | --- | --- | --- | | `Model` | `provider.TranscriptionModel` | Model must implement provider.TranscriptionStreamer. | | `Audio` | `provider.AudioStream` | Audio is the source of raw audio chunks to transcribe. | | `InputAudioFormat` | `provider.AudioFormat` | InputAudioFormat is the input audio format for the raw audio chunks. | | `ProviderOptions` | `map[string]interface{}` | ProviderOptions contains provider-specific request options. | | `Headers` | `map[string]string` | Additional HTTP/WebSocket headers to send when supported by the provider. | | `IncludeRawChunks` | `bool` | IncludeRawChunks requests provider raw chunks in the stream. | | `Telemetry` | `*TelemetrySettings` | Telemetry configures observability for this operation. When both Telemetry and ExperimentalTelemetry are set, Telemetry wins. | | `ExperimentalTelemetry` | `*TelemetrySettings` | ExperimentalTelemetry configures observability for this operation. Deprecated: use Telemetry. | {/* /gen:fields */} ## StreamTranslateOptions {/* gen:fields ai.StreamTranslateOptions */} | Field | Type | Description | | --- | --- | --- | | `Model` | `provider.SpeechTranslationModel` | Model to use for speech-to-speech translation. | | `Audio` | `provider.AudioStream` | Audio is the source of raw audio chunks to translate. | | `InputAudioFormat` | `provider.AudioFormat` | InputAudioFormat is the input audio format for the raw audio chunks. | | `TargetLanguage` | `string` | TargetLanguage is the language to translate the audio into, as a BCP-47-style language tag (e.g. "en", "es", "fr-CA"). | | `SourceLanguage` | `string` | SourceLanguage is the language of the source audio. Auto-detected when empty. | | `OutputAudioFormat` | `*provider.AudioFormat` | OutputAudioFormat is the desired audio format for translated audio chunks. When nil, the provider default output format is used. | | `ProviderOptions` | `map[string]interface{}` | ProviderOptions contains provider-specific request options. | | `Headers` | `map[string]string` | Additional HTTP/WebSocket headers to send when supported by the provider. | | `IncludeRawChunks` | `bool` | IncludeRawChunks requests provider raw chunks in the stream. | {/* /gen:fields */} ## Stream types `ai.TranscriptionStream` and `ai.TranslationStream` have the same shape as `provider.TextStream`: `Next()`, `Err()` and `Close()`. ### TranscriptionStreamPart `Type` is one of `transcript-delta`, `transcript-partial`, `transcript-final`, `raw` or `error`. {/* gen:fields ai.TranscriptionStreamPart */} | Field | Type | Description | | --- | --- | --- | | `Type` | `string` | Type is one of "transcript-delta", "transcript-partial", "transcript-final", "raw", or "error". | | `ID` | `string` | | | `Delta` | `string` | | | `Text` | `string` | | | `StartSecond` | `*float64` | | | `EndSecond` | `*float64` | | | `DurationInSeconds` | `*float64` | | | `ChannelIndex` | `*int` | | | `ProviderMetadata` | `map[string]interface{}` | | | `RawValue` | `interface{}` | RawValue carries the provider's raw chunk when Type is "raw". | | `Err` | `interface{}` | Err carries the streamed error payload when Type is "error". | {/* /gen:fields */} ### TranslationStreamPart `Type` is one of `audio`, `output-text-delta`, `output-text-final`, `source-transcript-delta`, `source-transcript-partial`, `source-transcript-final`, `raw` or `error`. {/* gen:fields ai.TranslationStreamPart */} | Field | Type | Description | | --- | --- | --- | | `Type` | `string` | Type is one of "audio", "output-text-delta", "output-text-final", "source-transcript-delta", "source-transcript-partial", "source-transcript-final", "raw", or "error". | | `ID` | `string` | | | `AudioData` | `[]byte` | | | `Delta` | `string` | | | `Text` | `string` | | | `StartSecond` | `*float64` | | | `EndSecond` | `*float64` | | | `ChannelIndex` | `*int` | | | `ProviderMetadata` | `map[string]interface{}` | | | `RawValue` | `interface{}` | RawValue carries the provider's raw chunk when Type is "raw". | | `Err` | `interface{}` | Err carries the streamed error payload when Type is "error". | {/* /gen:fields */} ### SpeechTranslationModelResponseMetadata {/* gen:fields ai.SpeechTranslationModelResponseMetadata */} | Field | Type | Description | | --- | --- | --- | | `Timestamp` | `time.Time` | | | `ModelID` | `string` | | | `Headers` | `map[string]string` | | {/* /gen:fields */} ## Errors `ai.IsNoTranslationGeneratedError(err)` reports whether `err` is a `*ai.NoTranslationGeneratedError`. --- # Tool call parsing and repair > Reference for the Go AI SDK functions that parse, repair and refine model tool calls, and the tool definition fingerprints that detect drift. Canonical URL: https://goaisdk.com/docs/reference/ai/tool-call-repair Documentation index: https://goaisdk.com/llms.txt The SDK parses each tool call the model returns against the tool's input schema. A call that names an unknown tool or has invalid input is marked invalid unless a repair function fixes it. `GenerateText`, `StreamText` and `ToolLoopAgent` run these steps for you. The functions on this page are the building blocks. ## RepairToolCall Set `RepairToolCall` on `ai.GenerateTextOptions` or `ai.StreamTextOptions` to repair calls that fail to parse. The function type is `ai.ToolCallRepairFunction`. ```go type ToolCallRepairFunction func(ctx context.Context, options ToolCallRepairOptions) (*types.ToolCall, error) ``` Return `(nil, nil)` when the call cannot be repaired. The returned call may carry the repaired input as `RawArguments` (JSON text) or `Arguments`. {/* gen:fields ai.ToolCallRepairOptions */} | Field | Type | Description | | --- | --- | --- | | `ToolCall` | `types.ToolCall` | ToolCall is the tool call that failed to parse. | | `Tools` | `[]types.Tool` | Tools are the tools available in the step. | | `InputSchema` | `func(toolName string) (map[string]interface{}, error)` | InputSchema returns the JSON schema of a tool's input. | | `Instructions` | `string` | Instructions is the text instructions (system prompt) of the step. | | `InstructionMessages` | `[]types.Message` | InstructionMessages are the system-message instructions of the step, when instructions were given as system messages. | | `System` | `string` | System is the step's system prompt. Deprecated: use Instructions. | | `Messages` | `[]types.Message` | Messages are the messages of the current generation step. | | `Error` | `error` | Error is the \*NoSuchToolError or \*InvalidToolInputError that occurred. | {/* /gen:fields */} `ai.RepairTextFunc` is a function type that repairs raw JSON text before parsing: `func(ctx context.Context, text string, parseErr error) (*string, error)`. Return nil to leave the text unchanged. ## ParseToolCall ```go func ParseToolCall(ctx context.Context, opts ParseToolCallOptions) (types.ToolCall, error) ``` Resolves a provider tool call against the available tools, parses and validates its input, and runs `RepairToolCall` for unknown-tool and invalid-input errors. A call that cannot be parsed or repaired comes back with `Invalid`, `Dynamic` and `Error` set. The only error it returns is a context error. {/* gen:fields ai.ParseToolCallOptions */} | Field | Type | Description | | --- | --- | --- | | `ToolCall` | `types.ToolCall` | | | `Tools` | `[]types.Tool` | | | `RepairToolCall` | `ToolCallRepairFunction` | | | `Instructions` | `string` | | | `InstructionMessages` | `[]types.Message` | | | `Messages` | `[]types.Message` | | {/* /gen:fields */} ## Basic repair helpers These helpers predate `ToolCallRepairFunction`. They take an `ai.ToolCallRepairFunc`, which sees the failing call and the error but not the tools or messages. | Name | Description | | --- | --- | | `ai.ToolCallRepairFunc` | `func(ctx context.Context, toolCall types.ToolCall, err error) (*types.ToolCall, error)`. | | `ai.DefaultToolCallRepair` | A `ToolCallRepairFunc` that fixes common argument problems. | | `ai.RepairOptions` | Options for `TryRepairToolCalls`: `MaxAttempts` and `RepairFunc`. | | `ai.DefaultRepairOptions()` | Returns the default `RepairOptions`. | | `ai.TryRepairToolCalls(ctx, toolCalls, opts)` | Repairs every call in a slice. | | `ai.IsToolCallRepairError(err)` | Reports whether `err` is an `*ai.ToolCallRepairError`. | ## Refining tool input A refiner adjusts parsed tool input before approval, callbacks, telemetry and execution. ```go type ToolInputRefiner func(ctx context.Context, opts ToolInputRefinementOptions) (map[string]interface{}, error) func RefineToolCalls(ctx context.Context, calls []types.ToolCall, tools []types.Tool, refiners map[string]ToolInputRefiner, runtimeContext interface{}, toolsContext map[string]interface{}) ([]types.ToolCall, error) ``` `refiners` is keyed by tool name. When a refiner changes the input, the original input is kept as `inputSchemaInput` on the approval request. {/* gen:fields ai.ToolInputRefinementOptions */} | Field | Type | Description | | --- | --- | --- | | `ToolCall` | `types.ToolCall` | | | `Tool` | `*types.Tool` | | | `RuntimeContext` | `interface{}` | | | `ToolsContext` | `map[string]interface{}` | | {/* /gen:fields */} ## Tool definition fingerprints A server-controlled tool definition, such as one fetched from an MCP server, can change after you reviewed it. Fingerprint the tools when you trust them and compare later. ```go func FingerprintTools(tools []types.Tool) (map[string]string, error) func DetectToolDrift(current, baseline map[string]string) ToolDrift ``` `FingerprintTools` hashes the description, the resolved input schema and the title of each tool with SHA-256 over canonical JSON. A `DescriptionFunc` is pinned by presence only, not by output. The digests match the TypeScript SDK's `fingerprintTools`. `DetectToolDrift` returns the tool names that were added, removed or changed. The slices are sorted. {/* gen:fields ai.ToolDrift */} | Field | Type | Description | | --- | --- | --- | | `Added` | `[]string` | Added lists tools present only in the current fingerprints. | | `Removed` | `[]string` | Removed lists tools present only in the baseline fingerprints. | | `Changed` | `[]string` | Changed lists tools whose pinned definition differs. | {/* /gen:fields */} --- # Tool search and tool callers > Reference for ai.ToolSearch, deferred tool loading and the experimental tool caller routing helpers in the Go AI SDK. Canonical URL: https://goaisdk.com/docs/reference/ai/tool-search-and-callers Documentation index: https://goaisdk.com/llms.txt Two features change which tools the model sees on each step. Tool search hides tools marked `DeferLoading` until the model finds them. Tool callers route a tool so that only another tool, such as a code execution tool, can call it. Both are experimental. ## ToolSearch ```go func ToolSearch(config ...ToolSearchConfig) types.Tool ``` Returns the native tool-search tool. Pair it with tools that set `DeferLoading: true`. The generation loop binds a search registry, so a match becomes available on the next model step. `ToolSearch` panics with `*providererrors.InvalidArgumentError` when `MaxResults` is negative. {/* gen:fields ai.ToolSearchConfig */} | Field | Type | Description | | --- | --- | --- | | `Name` | `string` | Name overrides the tool's registered name (default: ToolSearchDefaultName). Set it to register more than one tool-search instance in the same request (e.g. one per tool-caller boundary), or to avoid a collision with an existing tool name. Whatever name is used here must match the corresponding entry in ExperimentalToolCallers and the tools list passed to GenerateText/StreamText. | | `MaxResults` | `int` | MaxResults caps the number of matching tools returned per search. Zero (the default) uses ToolSearchDefaultMaxResults (5). A negative value panics with an InvalidArgumentError, mirroring the TypeScript SDK's toolSearch(\{ maxResults \}) synchronous validation throw. | | `Search` | `ToolSearchRankFunc` | Search optionally selects and ranks eligible deferred tools, instead of the built-in keyword scoring over tool names and descriptions. Mirrors the TypeScript SDK's toolSearch(\{ search \}) callback. | {/* /gen:fields */} | Constant | Value | | --- | --- | | `ai.ToolSearchDefaultName` | `"toolSearch"` | | `ai.ToolSearchDefaultMaxResults` | `5` | `ai.ToolSearchRankFunc` is the type of `Search`. It is an alias for `types.ToolSearchRankFunc`. ### ToolSearchState For custom loops, `ai.NewToolSearchState(tools, toolCallers)` creates the discovery state for one generation. Do not share it across generations. Call `Apply(activeTools, toolsContext, sandbox)` once per step. It hides deferred tools that were not discovered, and rebinds any search tool to the deferred tools reachable through its callers. When no tool uses `DeferLoading` and no search tool is present, `Apply` returns its input unchanged. ## Tool callers `ai.ExperimentalToolCallers` is a `map[string][]string`. Each key is a tool name, and each value lists the tool names allowed to call it. Include `ai.DirectToolCall` (`"AI_SDK_DIRECT_TOOL_CALL"`) to let the model call the tool directly as well. A tool that is absent from the map is not routed. Set it as `ExperimentalToolCallers` on `ai.GenerateTextOptions`, `ai.StreamTextOptions` or `agent.AgentConfig`. Mark a caller tool with `types.Tool.ExperimentalToolCaller`. | Function | Description | | --- | --- | | `ai.ResolveToolCallerConfiguration(tools, toolCallers) (ResolvedToolCallers, error)` | Validates a configuration against the available tools. | | `ai.PrepareToolsForToolCallers(tools, toolCallers) (executionTools, modelTools, toolCallerMessages)` | Splits tools into the set used for execution and the set shown to the model. Collects the messages that announce local caller catalogs. | | `ai.AppendToolCallerMessages(messages, toolCallerMessages) []types.Message` | Appends the announcement messages and skips duplicates. Returns a copy. | `ai.ResolvedToolCallers` is the validated form, also a `map[string][]string`. --- # Video operations > Reference for the experimental Go AI SDK functions that start a video generation and check its status later, with option and result types. Canonical URL: https://goaisdk.com/docs/reference/ai/video-operations Documentation index: https://goaisdk.com/llms.txt `ai.ExperimentalStartVideo` starts a video generation and returns without waiting. `ai.ExperimentalGetVideoStatus` checks the operation later, from any process. Use them for long-running generations. The model must implement `provider.VideoModelStarter`. ```go func ExperimentalStartVideo(ctx context.Context, opts StartVideoOptions) (*StartVideoResult, error) func ExperimentalGetVideoStatus(ctx context.Context, model provider.VideoModelV3, opts GetVideoStatusOptions) (*GetVideoStatusResult, error) ``` ## StartVideoOptions {/* gen:fields ai.StartVideoOptions */} | Field | Type | Description | | --- | --- | --- | | `Model` | `provider.VideoModelV3` | Model to use. Must implement provider.VideoModelStarter. | | `Prompt` | `VideoPrompt` | Prompt can be text-only or image+text for image-to-video. | | `N` | `int` | Number of videos to generate (default: 1). Must not exceed the model's MaxVideosPerCall — fan out with multiple StartVideo calls. | | `MaxVideosPerCall` | `*int` | Maximum videos per API call (provider-specific). If not set, uses the model's MaxVideosPerCall() value for the limit check. | | `AspectRatio` | `string` | Aspect ratio in format "width:height", or "adaptive". | | `Resolution` | `string` | Resolution in format "widthxheight". | | `Duration` | `*float64` | Duration in seconds. | | `FPS` | `*int` | Frames per second. | | `Seed` | `*int` | Seed for reproducible generation. | | `FrameImages` | `[]VideoFrameImageInput` | FrameImages are role-tagged image inputs for image-to-video and first-last-frame generation. | | `InputReferences` | `[]VideoReferenceInput` | InputReferences are reference image or video inputs for reference-to-video generation. | | `GenerateAudio` | `*bool` | GenerateAudio requests that the model generate audio alongside the video, when supported. | | `ProviderOptions` | `map[string]interface{}` | Provider-specific options. | | `MaxRetries` | `*int` | Maximum retries for the start call (default: 2). Set to 0 to disable. | | `Headers` | `map[string]string` | Additional HTTP headers. | | `WebhookURL` | `string` | WebhookURL, when set, asks the provider to notify this URL when the generation reaches a terminal state. | {/* /gen:fields */} ## StartVideoResult `Operation` is an opaque JSON value. Persist it and pass it to `ExperimentalGetVideoStatus`. {/* gen:fields ai.StartVideoResult */} | Field | Type | Description | | --- | --- | --- | | `Operation` | `json.RawMessage` | Operation is a JSON-serializable opaque reference to the started generation. Persist it and pass it to ExperimentalGetVideoStatus to retrieve the status and result later, from any process. | | `Warnings` | `[]types.Warning` | Warnings for the call, e.g. unsupported settings. | | `ProviderMetadata` | `map[string]interface{}` | ProviderMetadata is passed through from the provider. Carries the provider's own job identifiers (e.g. the AI Gateway's providerMetadata.gateway.asyncJob.jobId). | | `Response` | `VideoModelResponseMetadata` | Response is response metadata from the provider. | {/* /gen:fields */} ## GetVideoStatusOptions {/* gen:fields ai.GetVideoStatusOptions */} | Field | Type | Description | | --- | --- | --- | | `Operation` | `json.RawMessage` | Operation is the opaque reference returned by ExperimentalStartVideo. | | `Headers` | `map[string]string` | Additional HTTP headers. | | `MaxRetries` | `*int` | Maximum retries for the status call (default: 2). Set to 0 to disable. | {/* /gen:fields */} ## GetVideoStatusResult `Status` is one of the `provider.VideoOperationStatus*` constants. `Videos` is set when the status is completed. `Error` is set when the status is error. {/* gen:fields ai.GetVideoStatusResult */} | Field | Type | Description | | --- | --- | --- | | `Status` | `string` | | | `Videos` | `[]provider.VideoModelV3VideoData` | Videos is set when Status is provider.VideoOperationStatusCompleted. | | `Error` | `string` | Error is set when Status is provider.VideoOperationStatusError. | | `Warnings` | `[]types.Warning` | | | `ProviderMetadata` | `map[string]interface{}` | | | `Response` | `VideoModelResponseMetadata` | | {/* /gen:fields */} ## VideoFrameImageInput A role-tagged image for image-to-video and first-and-last-frame generation. {/* gen:fields ai.VideoFrameImageInput */} | Field | Type | Description | | --- | --- | --- | | `Image` | `VideoPromptImage` | Image is the file used for this frame. | | `FrameType` | `string` | FrameType is provider.VideoFrameTypeFirstFrame or provider.VideoFrameTypeLastFrame. | {/* /gen:fields */} ## Errors `ai.IsNoVideoGeneratedError(err)` reports whether `err` is a `*ai.NoVideoGeneratedError`. --- # MCP protocol types > Reference for the Go AI SDK MCP protocol types: JSON-RPC messages, capabilities, initialize and discover, tool, resource and prompt payloads, and logging. Canonical URL: https://goaisdk.com/docs/reference/mcp/protocol-types Documentation index: https://goaisdk.com/llms.txt Types in `pkg/mcp` that model the protocol. Most code uses the [client](https://goaisdk.com/docs/reference/mcp/client.md) and never builds these directly. They matter when you write a transport or a server-side bridge. ## JSON-RPC messages `mcp.MCPMessage` is a generic JSON-RPC 2.0 message. {/* gen:fields mcp.MCPMessage */} | Field | Type | Description | | --- | --- | --- | | `JSONRpc` | `string` | | | `ID` | `interface{}` | | | `Method` | `string` | | | `Params` | `json.RawMessage` | | | `Result` | `json.RawMessage` | | | `Error` | `*MCPError` | | {/* /gen:fields */} | Function | Description | | --- | --- | | `mcp.CreateRequest(id, method, params) (*MCPMessage, error)` | Builds a request. | | `mcp.CreateNotification(method, params) (*MCPMessage, error)` | Builds a notification (no ID). | | `mcp.CreateResponse(id, result) (*MCPMessage, error)` | Builds a success response. | | `mcp.CreateErrorResponse(id, code, message, data) *MCPMessage` | Builds an error response. | | `mcp.ValidateJSONRPCMessage(raw []byte) (*MCPMessage, error)` | Checks that `raw` is a well-formed request, notification or response. | | `mcp.IsRequest(msg)`, `mcp.IsNotification(msg)`, `mcp.IsResponse(msg)`, `mcp.IsError(msg)` | Classify a message. | | `mcp.GetError(msg) error` | Returns the error from an error response. | | `mcp.ParseParams(msg, target)`, `mcp.ParseResult(msg, target)` | Decode `params` or `result` into `target`. | | `mcp.NewIDGenerator() *IDGenerator` | Creates a generator. `Next()` returns a new request ID. | ## Initialize and discover | Type | Description | | --- | --- | | `mcp.InitializeParams` | Parameters of the `initialize` request. | | `mcp.InitializeResult` | Result of `initialize`. | | `mcp.ClientInfo`, `mcp.ServerInfo` | Name and version of each side. | | `mcp.ClientCapabilities` | What the client supports. | | `mcp.ServerCapabilities` | What the server supports. | | `mcp.DiscoverResult` | Result of `server/discover`. | {/* gen:fields mcp.ClientCapabilities */} | Field | Type | Description | | --- | --- | --- | | `Experimental` | `map[string]interface{}` | | | `Extensions` | `map[string]interface{}` | | | `Roots` | `*RootsCapability` | | | `Sampling` | `*SamplingCapability` | | | `Elicitation` | `map[string]interface{}` | | {/* /gen:fields */} {/* gen:fields mcp.ServerCapabilities */} | Field | Type | Description | | --- | --- | --- | | `Experimental` | `map[string]interface{}` | | | `Logging` | `*LoggingCapability` | | | `Completions` | `*CompletionsCapability` | Completions advertises support for the completion/complete method (hash 68a739a). See (\*MCPClient).Complete. | | `Prompts` | `*PromptsCapability` | | | `Resources` | `*ResourcesCapability` | | | `Tools` | `*ToolsCapability` | | {/* /gen:fields */} The capability types are `mcp.RootsCapability`, `mcp.SamplingCapability`, `mcp.ToolsCapability`, `mcp.ResourcesCapability`, `mcp.PromptsCapability`, `mcp.LoggingCapability` and `mcp.CompletionsCapability`. ## Tools {/* gen:fields mcp.MCPTool */} | Field | Type | Description | | --- | --- | --- | | `Name` | `string` | | | `Title` | `string` | | | `Description` | `string` | | | `InputSchema` | `map[string]interface{}` | | | `OutputSchema` | `map[string]interface{}` | | | `Annotations` | `map[string]interface{}` | | | `Meta` | `map[string]interface{}` | | {/* /gen:fields */} | Type | Description | | --- | --- | | `mcp.ListToolsParams`, `mcp.ListToolsResult` | Parameters and result of `tools/list`, including pagination. | | `mcp.CallToolParams`, `mcp.CallToolResult` | Parameters and result of `tools/call`. | | `mcp.ToolResultContent` | One content item in a tool result. | {/* gen:fields mcp.CallToolResult */} | Field | Type | Description | | --- | --- | --- | | `Content` | `[]ToolResultContent` | | | `StructuredContent` | `interface{}` | | | `ToolResult` | `interface{}` | | | `IsError` | `bool` | | | `Metadata` | `map[string]interface{}` | | {/* /gen:fields */} ## Resources, prompts and completions See [MCP client](https://goaisdk.com/docs/reference/mcp/client.md#resources) for `MCPResource`, `MCPResourceTemplate`, `ReadResourceParams`, `ReadResourceResult`, `ResourceContent`, `MCPPrompt`, `PromptMessage`, `CompleteRequestParams` and related types. ## Logging A client can set the server's log level and receive log messages. {/* gen:consts mcp.LoggingLevel */} | Constant | Value | Description | | --- | --- | --- | | `LoggingLevelDebug` | `"debug"` | | | `LoggingLevelInfo` | `"info"` | | | `LoggingLevelNotice` | `"notice"` | | | `LoggingLevelWarning` | `"warning"` | | | `LoggingLevelError` | `"error"` | | | `LoggingLevelCritical` | `"critical"` | | | `LoggingLevelAlert` | `"alert"` | | | `LoggingLevelEmergency` | `"emergency"` | | {/* /gen:consts */} {/* gen:fields mcp.SetLevelParams */} | Field | Type | Description | | --- | --- | --- | | `Level` | `LoggingLevel` | | {/* /gen:fields */} {/* gen:fields mcp.LoggingMessageNotification */} | Field | Type | Description | | --- | --- | --- | | `Level` | `LoggingLevel` | | | `Logger` | `string` | | | `Data` | `interface{}` | | {/* /gen:fields */} --- # WrapLanguageModel > API reference for WrapLanguageModel, the Go AI SDK function that wraps a language model with middleware to add default settings, logging, or other behavior. Canonical URL: https://goaisdk.com/docs/reference/middleware/wrap-language-model Documentation index: https://goaisdk.com/llms.txt Wraps a language model with middleware to add additional behavior like default settings, logging, or extraction. ## Signature ```go func WrapLanguageModel( model provider.LanguageModel, middleware []*middleware.LanguageModelMiddleware, modelID *string, providerID *string, ) provider.LanguageModel ``` When multiple middleware are provided, the first middleware transforms the input first, and the last middleware is wrapped directly around the model. ## Parameters | Parameter | Type | Description | |-----------|------|-------------| | model | provider.LanguageModel | Base language model to wrap | | middleware | []*middleware.LanguageModelMiddleware | Middleware to apply, in order | | modelID | *string | Optional override for the reported model ID (nil keeps the wrapped model's ID) | | providerID | *string | Optional override for the reported provider name (nil keeps the wrapped model's provider) | ## Examples ### Basic Wrapping ```go package main import ( "context" "fmt" "log" "github.com/digitallysavvy/go-ai/pkg/ai" "github.com/digitallysavvy/go-ai/pkg/middleware" "github.com/digitallysavvy/go-ai/pkg/provider" "github.com/digitallysavvy/go-ai/pkg/providers/openai" ) func main() { p := openai.New(openai.Config{ APIKey: "your-api-key", }) baseModel, _ := p.LanguageModel("gpt-6-astra") temperature := 0.7 wrapped := middleware.WrapLanguageModel( baseModel, []*middleware.LanguageModelMiddleware{ middleware.DefaultSettingsMiddleware(&provider.GenerateOptions{ Temperature: &temperature, }), }, nil, nil, ) result, err := ai.GenerateText(context.Background(), ai.GenerateTextOptions{ Model: wrapped, Prompt: "Hello!", // Temperature will default to 0.7 }) if err != nil { log.Fatal(err) } fmt.Println(result.Text) } ``` ### Multiple Middleware ```go temperature := 0.7 maxTokens := 500 wrapped := middleware.WrapLanguageModel( baseModel, []*middleware.LanguageModelMiddleware{ middleware.DefaultSettingsMiddleware(&provider.GenerateOptions{ Temperature: &temperature, MaxTokens: &maxTokens, }), middleware.ExtractReasoningMiddleware(&middleware.ExtractReasoningOptions{ TagName: "think", }), }, nil, nil, ) ``` ### Overriding the Reported Model ID ```go modelID := "custom-model-alias" wrapped := middleware.WrapLanguageModel( baseModel, nil, &modelID, nil, ) ``` ### Custom Middleware ```go customMiddleware := &middleware.LanguageModelMiddleware{ WrapGenerate: func( ctx context.Context, doGenerate func() (*types.GenerateResult, error), doStream func() (provider.TextStream, error), params *provider.GenerateOptions, model provider.LanguageModel, ) (*types.GenerateResult, error) { start := time.Now() result, err := doGenerate() log.Printf("Generation took %v", time.Since(start)) return result, err }, } wrapped := middleware.WrapLanguageModel( baseModel, []*middleware.LanguageModelMiddleware{customMiddleware}, nil, nil, ) ``` ## See Also - [Middleware Interface](https://goaisdk.com/docs/reference/middleware/middleware-interface.md) - Middleware struct definitions - [Built-in Middleware](https://goaisdk.com/docs/reference/middleware/built-in-middleware.md) - Available middleware catalog --- # Tool Types > API reference for Go AI SDK tool-related types, covering Tool, ToolCall, ToolResult, ToolChoice, and the ToolExecutor function signature. Canonical URL: https://goaisdk.com/docs/reference/types/tools Documentation index: https://goaisdk.com/llms.txt Types related to tool definitions, tool calls, and tool execution. ## Tool See [Tool API Reference](https://goaisdk.com/docs/reference/ai/tool.md) for complete documentation. ## ToolCall ```go type ToolCall struct { ID string `json:"id"` ToolName string `json:"toolName"` Arguments map[string]interface{} `json:"arguments"` } ``` Represents a tool call made by the model. ### Fields | Field | Type | Description | |-------|------|-------------| | ID | string | Unique identifier for this tool call | | ToolName | string | Name of the tool to call | | Arguments | map[string]interface{} | Arguments to pass to the tool | ## ToolResult ```go type ToolResult struct { ToolCallID string `json:"toolCallId"` ToolName string `json:"toolName"` Result interface{} `json:"result"` Error error `json:"error,omitempty"` ProviderExecuted bool `json:"providerExecuted,omitempty"` } ``` Result of executing a tool. ### Fields | Field | Type | Description | |-------|------|-------------| | ToolCallID | string | ID of the tool call this result corresponds to | | ToolName | string | Name of the tool that was executed | | Result | interface{} | Result of the tool execution | | Error | error | Error if execution failed | | ProviderExecuted | bool | Whether provider executed this tool | ## ToolChoice ```go type ToolChoice struct { Type ToolChoiceType `json:"type"` ToolName string `json:"toolName,omitempty"` } ``` Specifies how the model should choose tools. ### Fields | Field | Type | Description | |-------|------|-------------| | Type | ToolChoiceType | Type of tool choice | | ToolName | string | Specific tool name (for ToolChoiceTool type) | ## ToolChoiceType ```go type ToolChoiceType string const ( ToolChoiceAuto ToolChoiceType = "auto" ToolChoiceNone ToolChoiceType = "none" ToolChoiceRequired ToolChoiceType = "required" ToolChoiceTool ToolChoiceType = "tool" ) ``` Types of tool choice strategies: - `ToolChoiceAuto`: Model decides whether to use tools - `ToolChoiceNone`: Model cannot use tools - `ToolChoiceRequired`: Model must use at least one tool - `ToolChoiceTool`: Model must use a specific tool ## ToolExecutor ```go type ToolExecutor func( ctx context.Context, input map[string]interface{}, options ToolExecutionOptions, ) (interface{}, error) ``` Function signature for tool execution. ## ToolExecutionOptions ```go type ToolExecutionOptions struct { ToolCallID string UserContext interface{} Usage *Usage Metadata map[string]interface{} } ``` Options passed to tool execution. ### Fields | Field | Type | Description | |-------|------|-------------| | ToolCallID | string | Unique ID of this tool call | | UserContext | interface{} | User-defined context | | Usage | *Usage | Current token usage | | Metadata | map[string]interface{} | Additional metadata | ## Examples ### Create Tool Call ```go package main import ( "fmt" "github.com/digitallysavvy/go-ai/pkg/provider/types" ) func main() { toolCall := types.ToolCall{ ID: "call_abc123", ToolName: "get_weather", Arguments: map[string]interface{}{ "location": "Tokyo", "units": "celsius", }, } fmt.Println(toolCall.ToolName) } ``` ### Auto Tool Choice ```go toolChoice := types.AutoToolChoice() // Equivalent to: // types.ToolChoice{Type: types.ToolChoiceAuto} ``` ### Required Tool Choice ```go toolChoice := types.RequiredToolChoice() // Model must call at least one tool ``` ### Specific Tool Choice ```go toolChoice := types.SpecificToolChoice("get_weather") // Model must call the "get_weather" tool ``` ### Handle Tool Result ```go toolResult := types.ToolResult{ ToolCallID: "call_abc123", ToolName: "get_weather", Result: map[string]interface{}{ "temperature": 22, "condition": "sunny", }, } if toolResult.Error != nil { log.Printf("Tool failed: %v", toolResult.Error) } ``` ## See Also - [Tool](https://goaisdk.com/docs/reference/ai/tool.md) - Tool definition - [Message Types](https://goaisdk.com/docs/reference/types/messages.md) - Message types - [Tool Calling Guide](https://goaisdk.com/docs/ai-sdk-core/tools-and-tool-calling.md) --- ## Pages not included here Fetch these individually as markdown. - [Go AI SDK](https://goaisdk.com/docs/introduction.md): The Go AI SDK is a powerful toolkit for building AI applications and agents with Go, featuring support for 49 providers. - [Providers and Models](https://goaisdk.com/docs/foundations/providers-and-models.md): Explains the Go AI SDK's unified provider.Provider interface, listing supported providers, model capabilities, self-hosted models, and the provider registry. - [Prompts](https://goaisdk.com/docs/foundations/prompts.md): Covers text, system, and message prompt formats in the Go AI SDK, including provider-specific options and multi-turn conversation context. - [Tools](https://goaisdk.com/docs/foundations/tools.md): Introduces tools in the Go AI SDK: defining schemas, multi-step tool calling, tool choice, execution callbacks, and handling tool errors in Go. - [Streaming](https://goaisdk.com/docs/foundations/streaming.md): Compares blocking and streaming interfaces for AI apps, then shows basic Go streaming, callbacks, accumulating text, and cancelling streams with StreamText. - [Provider options](https://goaisdk.com/docs/foundations/provider-options.md): Documents ProviderOptions in the Go AI SDK: namespaced provider-specific settings for OpenAI, Anthropic, Google/Vertex, and xAI, with type-safe combining. - [Navigating the Library](https://goaisdk.com/docs/getting-started/navigating-the-library.md): Learn how to navigate the Go AI SDK and choose the right tools for your needs. - [Building Agents](https://goaisdk.com/docs/agents/building-agents.md): Complete guide to creating agents with ToolLoopAgent in Go: configuration options, system instructions, callbacks, and human-in-the-loop tool approval. - [Workflow Patterns](https://goaisdk.com/docs/agents/workflows.md): Learn workflow patterns for building reliable agents with the Go AI SDK. - [Loop Control](https://goaisdk.com/docs/agents/loop-control.md): Control agent execution with stop conditions, PrepareCall, and manual loop patterns - [Configuring Call Options](https://goaisdk.com/docs/agents/configuring-call-options.md): Shows how to pass runtime inputs to agents in Go using factory functions, so agent settings can be dynamically configured per request in HTTP handlers. - [Agent Callbacks](https://goaisdk.com/docs/agents/callbacks.md): Reference for Go AI SDK agent lifecycle callbacks, covering both legacy and LangChain-style callback types, common use cases, data types, and run tracking. - [WorkflowAgent](https://goaisdk.com/docs/agents/workflow-agent.md): Documents pkg/workflow's WorkflowAgent for durable, serializable Go workflows, covering the constructor, Generate, Stream, and chat transport. - [Agent Callbacks](https://goaisdk.com/docs/agents/agent-callbacks.md): Legacy reference for Go AI SDK agent callback support, covering the OnStepFinish callback, other callbacks, a complete example, and TypeScript comparison. - [Agent Skills](https://goaisdk.com/docs/agents/agent-skills.md): Explains agent skills in the Go AI SDK: reusable behaviors registered with agents, covering skill structure, the skill registry, and skills vs. tools. - [Agent Subagents](https://goaisdk.com/docs/agents/agent-subagents.md): Covers hierarchical agent systems in the Go AI SDK where a main agent delegates to subagents, including the subagent registry and delegation tracking. - [Core API Overview](https://goaisdk.com/docs/ai-sdk-core/overview.md): Overview of the Go AI SDK Core API: core generation functions, context-based operations, error handling, middleware, telemetry, and Go-specific features. - [Prompt Engineering](https://goaisdk.com/docs/ai-sdk-core/prompt-engineering.md): Teaches prompt engineering for LLMs in Go, from a slogan-generator quick start to advanced techniques, Go-specific schema patterns, and debugging prompts. - [Settings](https://goaisdk.com/docs/ai-sdk-core/settings.md): Reference for configuring the Go AI SDK: common settings, provider configuration headers, provider-specific options, and checking generation warnings. - [Reasoning](https://goaisdk.com/docs/ai-sdk-core/reasoning.md): Learn how to control reasoning across providers with the top-level Reasoning parameter. - [Embeddings](https://goaisdk.com/docs/ai-sdk-core/embeddings.md): Explains embedding single and multiple values with the Go AI SDK's Embed and EmbedMany functions, plus similarity, token usage, and embedding middleware. - [Reranking](https://goaisdk.com/docs/ai-sdk-core/reranking.md): Covers reranking documents by relevance with the Go AI SDK's Rerank function, including structured documents, settings, and reranking provider options. - [Image Generation](https://goaisdk.com/docs/ai-sdk-core/image-generation.md): Shows how to generate images in Go with ai.GenerateImage, covering settings, error handling, supported image models, and advanced generation features. - [Transcription](https://goaisdk.com/docs/ai-sdk-core/transcription.md): Shows how to transcribe audio in Go with ai.Transcribe, covering supported audio input formats, accessing results, settings, and transcription models. - [Speech Generation](https://goaisdk.com/docs/ai-sdk-core/speech.md): Shows how to generate speech from text in Go with ai.GenerateSpeech, covering audio data access, settings, supported formats, and performance tips. - [Video Generation](https://goaisdk.com/docs/ai-sdk-core/video-generation.md): Covers text-to-video and image-to-video generation across multiple providers in the Go AI SDK, including async polling with DoStart and DoStatus. - [Language Model Middleware](https://goaisdk.com/docs/ai-sdk-core/middleware.md): Learn how to use middleware to enhance the behavior of language models - [Provider & Model Management](https://goaisdk.com/docs/ai-sdk-core/provider-management.md): Explains centrally managing multiple providers and models in Go using the provider registry, custom providers with middleware, and registry utilities. - [Testing](https://goaisdk.com/docs/ai-sdk-core/testing.md): Describes strategies for testing Go code that calls non-deterministic, slow, and costly language models, with examples and integration-testing tips. - [Telemetry](https://goaisdk.com/docs/ai-sdk-core/telemetry.md): Shows how to enable OpenTelemetry tracing in the Go AI SDK Core, covering telemetry settings, setup in Go, collected data, and semantic conventions. - [Development Tools & Debugging](https://goaisdk.com/docs/ai-sdk-core/devtools.md): Covers debugging, profiling, and testing Go AI SDK applications, including debug mode, request/response inspection, token usage, and performance profiling. - [Event callbacks](https://goaisdk.com/docs/ai-sdk-core/event-callbacks.md): Subscribe to lifecycle events in GenerateText, StreamText, Embed, EmbedMany, and Rerank calls. - [MCP Tool Serialization](https://goaisdk.com/docs/ai-sdk-core/mcp-serialization.md): Documents GetSerializableTools() in the Go AI SDK for retrieving MCP tool definitions in a storable, transmittable format, with pagination support. - [Agent](https://goaisdk.com/docs/reference/ai/agent.md): API reference for agent.Agent, the Go AI SDK interface implemented by autonomous agents that use tools and make decisions to complete tasks. - [Callback Events](https://goaisdk.com/docs/reference/ai/callback-events.md): Structured lifecycle events for observability in GenerateText, StreamText, and ToolLoopAgent - [Code Mode](https://goaisdk.com/docs/reference/ai/code-mode.md): API reference for pkg/codemode, a Go port of @ai-sdk/code-mode letting a model write JavaScript that calls host tools instead of per-call tool use. - [EmbedMany](https://goaisdk.com/docs/reference/ai/embed-many.md): API reference for EmbedMany, a Go AI SDK function that generates vector embeddings for multiple text inputs in a single batched operation. - [GenerateObject](https://goaisdk.com/docs/reference/ai/generate-object.md): API reference for GenerateObject, the Go AI SDK function that generates structured JSON objects conforming to a schema using language models. - [GenerateText](https://goaisdk.com/docs/reference/ai/generate-text.md): API reference for GenerateText, the Go AI SDK function that generates text with support for tool calling, streaming, and multi-step conversations. - [Rerank](https://goaisdk.com/docs/reference/ai/rerank.md): API reference for Rerank, the Go AI SDK function that reorders documents by relevance to a query using a specialized reranking model. - [StreamObject](https://goaisdk.com/docs/reference/ai/stream-object.md): API reference for StreamObject, the Go AI SDK function that streams structured object generation, parsing and validating partial JSON as it arrives. - [StreamText](https://goaisdk.com/docs/reference/ai/stream-text.md): API reference for StreamText, the Go AI SDK function that streams text generation, returning tokens incrementally as the model produces them. - [Stream Transport Helpers](https://goaisdk.com/docs/reference/ai/stream-transport-helpers.md): Reference for Go AI SDK stream transport helpers that convert stream results into HTTP-friendly payloads, including CreateUIMessageStreamResponse. - [Tool approval helpers](https://goaisdk.com/docs/reference/ai/tool-approvals.md): Reference for the Go AI SDK tool approval functions: signing, verification, collecting, validating and resuming approvals, with their option and result types and errors. - [ToolLoopAgent](https://goaisdk.com/docs/reference/ai/tool-loop-agent.md): API reference for agent.ToolLoopAgent, the Go AI SDK type that creates an agent continuously using tools in a loop until the task completes. - [Tool](https://goaisdk.com/docs/reference/ai/tool.md): API reference for the Tool type in the Go AI SDK, defining tools that language models can call to perform actions or retrieve information. - [Transcribe](https://goaisdk.com/docs/reference/ai/transcribe.md): API reference for Transcribe, the Go AI SDK function that converts audio to text using a transcription model, including URL and base64 input. - [UI message chunks](https://goaisdk.com/docs/reference/ai/ui-message-chunks.md): Reference for every chunk type the Go AI SDK UI message stream emits, with fields, matching the AI SDK UIMessageChunk union. - [UI messages](https://goaisdk.com/docs/reference/ai/ui-messages.md): Reference for the Go AI SDK UI message types, part types, type guards, validation, conversion to model messages and stream reading. - [Utilities](https://goaisdk.com/docs/reference/ai/utilities.md): Reference for Go AI SDK utility types and functions: message pruning, timeouts, stream adapters, partial output parsing, evaluation, realtime sessions, sandboxes and warnings. - [Harness adapters](https://goaisdk.com/docs/reference/harness/adapters.md): Reference for the Harness adapter interface, sessions and prompt controls in the Go AI SDK, and for the Claude Code, Codex and other built-in adapters. - [Harness agent](https://goaisdk.com/docs/reference/harness/agent.md): Reference for harness.Agent, AgentSettings and AgentSession in the Go AI SDK: running coding-agent runtimes such as Claude Code and Codex as an agent, with sessions, approvals and lifecycle state. - [Harness sandboxes](https://goaisdk.com/docs/reference/harness/sandbox.md): Reference for harness sandbox configuration, the SandboxProvider interface, bootstrap recipes and templates, network policy, request transformations and the local and Vercel providers. - [Harness stream parts](https://goaisdk.com/docs/reference/harness/stream-parts.md): Reference for the stream part types a harness adapter emits during a prompt turn, how they map to AI SDK stream chunks, and the JSON helpers. - [MCP client](https://goaisdk.com/docs/reference/mcp/client.md): Reference for the Go AI SDK MCP client in pkg/mcp: client creation, transports, tool conversion, resources, prompts, completions, elicitation, MCP Apps and errors. - [MCP OAuth](https://goaisdk.com/docs/reference/mcp/oauth.md): Reference for the Go AI SDK MCP OAuth helpers: the Auth flow, client provider interfaces, discovery, registration, token exchange, PKCE and OAuth errors. - [Built-in Middleware](https://goaisdk.com/docs/reference/middleware/built-in-middleware.md): Catalog of built-in Go AI SDK middleware, including DefaultSettingsMiddleware, DefaultInstructionsMiddleware, ExtractJSONMiddleware, ExtractReasoningMiddleware, SimulateStreamingMiddleware, and AddToolInputExamplesMiddleware. - [Middleware Interface](https://goaisdk.com/docs/reference/middleware/middleware-interface.md): API reference for LanguageModelMiddleware, EmbeddingModelMiddleware, and ImageModelMiddleware, the Go AI SDK structs for implementing custom model-behavior middleware. - [Provider Registry](https://goaisdk.com/docs/reference/registry/provider-registry.md): API reference for the Go AI SDK's provider registry, which lets you register and access multiple AI providers globally through string IDs. - [JSON Schema](https://goaisdk.com/docs/reference/schema/json-schema.md): API reference for JSON Schema types in the Go AI SDK, covering the Schema and Validator interfaces, creating schemas, constraints, and circular refs. - [Error Types](https://goaisdk.com/docs/reference/types/errors.md): API reference for Go AI SDK error types, covering common errors returned by SDK functions and recommended error-handling patterns for applications. - [Message Types](https://goaisdk.com/docs/reference/types/messages.md): API reference for Go AI SDK message types, covering MessageRole, Message, and ContentPart implementations like TextContent, ImageContent, and FileContent. - [Usage Types](https://goaisdk.com/docs/reference/types/usage.md): API reference for Go AI SDK usage types that track token counts and costs across API calls, including input and output token detail breakdowns. - [Workflow](https://goaisdk.com/docs/reference/workflow.md): Reference for pkg/workflow in the Go AI SDK: WorkflowAgent, its options and results, the chat transports, serializable tools, step iteration and the durable harness runner. - [Download API Reference](https://goaisdk.com/docs/reference/download-api.md): API reference for the Go AI SDK's Download API, which fetches files securely with built-in size limits that prevent memory-exhaustion attacks.