twilight

module
v0.6.1-0...-3e7b614 Latest Latest
Warning

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

Go to latest
Published: Aug 29, 2026 License: Apache-2.0

README

Twilight AI

A lightweight, idiomatic AI SDK for Go — inspired by Vercel AI SDK.

Go Reference License

Features

  • Simple APIGenerateText, StreamText, Embed, EmbedMany, GenerateImage, EditImage, GenerateVideo, GenerateSpeech, and StreamSpeech cover most use cases
  • Provider-agnostic — swap between OpenAI, Anthropic, Google, GitHub Copilot, Edge TTS, or any OpenAI-compatible endpoint
  • Model discoveryListModels fetches available models, Test checks provider connectivity and model support
  • Tool calling — define tools with Go structs, SDK infers JSON Schema and handles multi-step execution
  • MCP support — connect to MCP servers and expose remote MCP tools as Twilight AI sdk.Tool values
  • Streaming — first-class channel-based streaming with fine-grained StreamPart types
  • Multi-step execution — automatic tool-call loop with configurable MaxSteps
  • Rich message types — text, images, files, reasoning content, tool calls/results
  • Embeddings — generate embeddings with Embed / EmbedMany, supports OpenAI and Google providers
  • Image generation — generate and edit images with GenerateImage / EditImage, supports OpenAI (dall-e, gpt-image) and Alibaba Cloud DashScope (Qwen-Image, Wan) models
  • Video generation — create, poll, and download video jobs with OpenRouter and Ark/ModelArk providers
  • Speech synthesis — generate speech with GenerateSpeech / StreamSpeech, supports Edge TTS with an open provider model
  • Approval flow — optional human-in-the-loop approval for sensitive tool calls

Installation

go get github.com/felinics/twilight

Requires Go 1.25+.

Quick Start

Generate Text (Chat Completions API)
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/felinics/twilight/provider/openai/completions"
    "github.com/felinics/twilight/sdk"
)

func main() {
    provider := completions.New(
        completions.WithAPIKey("sk-..."),
    )
    model := provider.ChatModel("gpt-4o-mini")

    text, err := sdk.GenerateText(context.Background(),
        sdk.WithModel(model),
        sdk.WithMessages([]sdk.Message{
            sdk.UserMessage("Explain Go channels in 3 sentences."),
        }),
    )
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(text)
}
Generate Text (Responses API)
import "github.com/felinics/twilight/provider/openai/responses"

provider := responses.New(
    responses.WithAPIKey("sk-..."),
)
model := provider.ChatModel("gpt-4o-mini")

text, err := sdk.GenerateText(context.Background(),
    sdk.WithModel(model),
    sdk.WithMessages([]sdk.Message{
        sdk.UserMessage("Explain Go channels in 3 sentences."),
    }),
)

The Responses API is OpenAI's newer API with first-class support for reasoning models (o3, o4-mini), URL citation annotations, and a flat input format. See Providers for details.

Anthropic
import "github.com/felinics/twilight/provider/anthropic/messages"

provider := messages.New(
    messages.WithAPIKey("sk-ant-..."),
)
model := provider.ChatModel("claude-sonnet-4-20250514")

maxTokens := 1024
text, err := sdk.GenerateText(context.Background(),
    sdk.WithModel(model),
    sdk.WithMaxTokens(maxTokens),
    sdk.WithMessages([]sdk.Message{
        sdk.UserMessage("Explain Go channels in 3 sentences."),
    }),
)

For extended thinking (reasoning), configure the provider with WithThinking:

provider := messages.New(
    messages.WithAPIKey("sk-ant-..."),
    messages.WithThinking(messages.ThinkingConfig{
        Type:         "enabled",
        BudgetTokens: 4000,
    }),
)
Google Gemini
import "github.com/felinics/twilight/provider/google/generativeai"

provider := generativeai.New(
    generativeai.WithAPIKey("AIza..."),
)
model := provider.ChatModel("gemini-2.5-flash")

text, err := sdk.GenerateText(context.Background(),
    sdk.WithModel(model),
    sdk.WithMessages([]sdk.Message{
        sdk.UserMessage("Explain Go channels in 3 sentences."),
    }),
)
GitHub Copilot Agent
import "github.com/felinics/twilight/provider/github/copilot"

provider := copilot.New(
    // Use the inbound X-GitHub-Token value from your Copilot agent request.
    copilot.WithGitHubToken("ghu_..."),
)
model := provider.ChatModel(copilot.AutoModel)

text, err := sdk.GenerateText(context.Background(),
    sdk.WithModel(model),
    sdk.WithMessages([]sdk.Message{
        sdk.UserMessage("Explain Go channels in 3 sentences."),
    }),
)

This provider targets GitHub Copilot agent / extension runtimes that can call api.githubcopilot.com/chat/completions. GitHub currently does not expose a public Copilot models discovery endpoint, so copilot.AutoModel tells the provider to let GitHub choose the backing model instead of inventing an undocumented model ID.

Stream Text
sr, err := sdk.StreamText(ctx,
    sdk.WithModel(model),
    sdk.WithMessages([]sdk.Message{
        sdk.UserMessage("Write a haiku about concurrency."),
    }),
)
if err != nil {
    log.Fatal(err)
}

for part := range sr.Stream {
    switch p := part.(type) {
    case *sdk.TextDeltaPart:
        fmt.Print(p.Text)
    case *sdk.ErrorPart:
        log.Fatal(p.Error)
    }
}
Tool Calling

Define a struct for your tool's parameters — the SDK infers the JSON Schema automatically:

type WeatherParams struct {
    City string `json:"city" jsonschema:"City name"`
}

weatherTool := sdk.NewTool("get_weather", "Get current weather for a city",
    func(ctx *sdk.ToolExecContext, input WeatherParams) (any, error) {
        return map[string]any{"city": input.City, "temp": "22°C"}, nil
    },
)

result, err := sdk.GenerateTextResult(ctx,
    sdk.WithModel(model),
    sdk.WithMessages([]sdk.Message{
        sdk.UserMessage("What's the weather in Tokyo?"),
    }),
    sdk.WithTools([]sdk.Tool{weatherTool}),
    sdk.WithMaxSteps(5),
)
MCP Tool Calling

You can also load tools from an MCP server and use them like normal Twilight AI tools:

import (
    "context"
    "log"
    "os/exec"

    "github.com/felinics/twilight/provider/openai/completions"
    "github.com/felinics/twilight/sdk"
    "github.com/modelcontextprotocol/go-sdk/mcp"
)

// HTTP / streamable MCP
mcpClient, err := sdk.CreateMCPClient(context.Background(), &sdk.MCPClientConfig{
    Type: sdk.MCPTransportHTTP, // default; may be omitted
    URL:  "https://example.com/mcp",
    Headers: map[string]string{
        "Authorization": "Bearer <token>",
    },
})
if err != nil {
    log.Fatal(err)
}
defer mcpClient.Close()

tools, err := mcpClient.Tools(context.Background())
if err != nil {
    log.Fatal(err)
}

provider := completions.New(completions.WithAPIKey("sk-..."))
model := provider.ChatModel("gpt-4o-mini")

result, err := sdk.GenerateTextResult(context.Background(),
    sdk.WithModel(model),
    sdk.WithMessages([]sdk.Message{
        sdk.UserMessage("Use the available MCP tools to answer this request."),
    }),
    sdk.WithTools(tools),
    sdk.WithMaxSteps(5),
)
if err != nil {
    log.Fatal(err)
}

log.Println(result.Text)

For stdio, create the MCP transport yourself with the official MCP Go SDK and pass it in:

transport := &mcp.CommandTransport{
    Command: exec.Command("my-mcp-server"),
}

mcpClient, err := sdk.CreateMCPClient(context.Background(), &sdk.MCPClientConfig{
    Transport: transport,
})

Twilight AI converts mcp.Tool definitions into sdk.Tool automatically:

  • InputSchema is converted into *jsonschema.Schema
  • tool execution calls session.CallTool(...) under the hood
  • MCP text content is returned as the tool output passed back into the model
Image Generation

Generate images from text prompts using OpenAI's image models:

import "github.com/felinics/twilight/provider/openai/images"

provider := images.New(images.WithAPIKey("sk-..."))
model := provider.GenerationModel("gpt-image-1")

result, err := sdk.GenerateImage(ctx,
    sdk.WithImageGenerationModel(model),
    sdk.WithImagePrompt("A sunset over mountains, oil painting style"),
    sdk.WithImageSize("1024x1024"),
)
// result.Data[0].B64JSON contains the base64-encoded image

Edit existing images with inpainting or extensions:

model := provider.EditModel("gpt-image-1")

result, err := sdk.EditImage(ctx,
    sdk.WithImageEditModel(model),
    sdk.WithEditPrompt("Add a rainbow in the sky"),
    sdk.WithEditImages(sdk.ImageInput{
        Data:     pngBytes,
        Filename: "photo.png",
    }),
)

Alibaba Cloud Model Studio (DashScope) image models work through the same API:

import "github.com/felinics/twilight/provider/alibabacloud/images"

provider := images.New(images.WithAPIKey("sk-..."))
model := provider.GenerationModel("qwen-image-max")

result, err := sdk.GenerateImage(ctx,
    sdk.WithImageGenerationModel(model),
    sdk.WithImagePrompt("A sunset over mountains, oil painting style"),
    sdk.WithImageSize("1024x1024"),
)
// result.Data[0].URL contains the generated image URL

The DashScope provider routes Qwen-Image and Wan models to the right endpoint automatically and transparently polls async generation tasks. See Images for details.

Embeddings

Generate vector embeddings for text using OpenAI or Google:

import "github.com/felinics/twilight/provider/openai/embedding"

provider := embedding.New(embedding.WithAPIKey("sk-..."))
model := provider.EmbeddingModel("text-embedding-3-small")

// Single value
vec, err := sdk.Embed(ctx, "Hello world", sdk.WithEmbeddingModel(model))
// vec is []float64

// Multiple values
result, err := sdk.EmbedMany(ctx, []string{"Hello", "World"},
    sdk.WithEmbeddingModel(model),
    sdk.WithDimensions(256),
)
// result.Embeddings is [][]float64
// result.Usage.Tokens reports token consumption

Google Gemini embeddings:

import "github.com/felinics/twilight/provider/google/embedding"

provider := embedding.New(
    embedding.WithAPIKey("AIza..."),
    embedding.WithTaskType("RETRIEVAL_DOCUMENT"),
)
model := provider.EmbeddingModel("gemini-embedding-001")

vec, err := sdk.Embed(ctx, "Hello world", sdk.WithEmbeddingModel(model))
Speech Synthesis

Generate speech audio from text using Edge TTS (free, no API key required):

import "github.com/felinics/twilight/provider/edge/speech"

provider := speech.New()
model := provider.SpeechModel("edge-read-aloud")

// Generate complete audio
result, err := sdk.GenerateSpeech(ctx,
    sdk.WithSpeechModel(model),
    sdk.WithText("Hello, world!"),
    sdk.WithSpeechConfig(map[string]any{
        "voice": "en-US-EmmaMultilingualNeural",
        "speed": 1.0,
    }),
)
// result.Audio is []byte, result.ContentType is "audio/mpeg"

Stream audio chunks for low-latency playback:

sr, err := sdk.StreamSpeech(ctx,
    sdk.WithSpeechModel(model),
    sdk.WithText("你好,这是流式语音合成。"),
    sdk.WithSpeechConfig(map[string]any{
        "voice": "zh-CN-XiaoxiaoNeural",
    }),
)
for chunk := range sr.Stream {
    // write chunk to audio player or file
}
Provider Health Check & Model Discovery

Test connectivity and discover available models before making generation requests:

provider := completions.New(completions.WithAPIKey("sk-..."))

// Check provider connectivity
result := provider.Test(context.Background())
switch result.Status {
case sdk.ProviderStatusOK:
    fmt.Println("Provider is healthy")
case sdk.ProviderStatusUnhealthy:
    fmt.Println("Connected but unhealthy:", result.Message)
case sdk.ProviderStatusUnreachable:
    fmt.Println("Cannot connect:", result.Message)
}

// List all available models
models, err := provider.ListModels(context.Background())
for _, m := range models {
    fmt.Println(m.ID)
}

// Check if a specific model is supported
model := provider.ChatModel("gpt-4o")
testResult, err := model.Test(context.Background())
if testResult.Supported {
    fmt.Println("Model is supported")
}

Documentation

Document Description
Getting Started Installation, setup, and first request
Providers Provider interface, OpenAI, Anthropic, and Google Gemini
Images Generate and edit images with OpenAI and Alibaba Cloud DashScope image models
Embeddings Generate vector embeddings with OpenAI and Google
Speech Speech synthesis with Edge TTS and custom providers
Tool Calling Defining local tools, MCP tools, multi-step execution, approval flow
Streaming Channel-based streaming and StreamPart types
API Reference Complete type and function reference

Supported Providers

Provider Constructor API Status
OpenAI Chat Completions completions.New() /chat/completions ✅ Stable
OpenAI Responses responses.New() /responses ✅ Stable
OpenAI Codex codex.New() /codex/responses ✅ Stable
OpenAI-compatible (DeepSeek, Groq, etc.) completions.New() + WithBaseURL /chat/completions ✅ Stable
OpenRouter Responses responses.New() + WithBaseURL /responses ✅ Stable
Anthropic messages.New() /messages ✅ Stable
Google Gemini generativeai.New() Generative AI API ✅ Stable
OpenAI Images images.New() /images/generations, /images/edits ✅ Stable
Alibaba Cloud DashScope Images images.New() DashScope text2image / multimodal-generation ✅ Stable
OpenAI Embeddings embedding.New() /embeddings ✅ Stable
Google Embeddings embedding.New() embedContent / batchEmbedContents ✅ Stable
Edge TTS speech.New() Bing WebSocket ✅ Stable
OpenAI / compatible TTS speech.New() /audio/speech ✅ Stable
Deepgram TTS speech.New() /v1/speak ✅ Stable
ElevenLabs TTS speech.New() /v1/text-to-speech/{voice_id} ✅ Stable
MiniMax TTS speech.New() /v1/t2a_v2 ✅ Stable
MiMo TTS speech.New() /chat/completions + audio output ✅ Stable
Alibaba Cloud CosyVoice speech.New() DashScope WebSocket ✅ Stable
Volcengine SAMI TTS speech.New() /api/v1/invoke ✅ Stable

License

Apache License 2.0

Directories

Path Synopsis
internal
messagecompat
Package messagecompat normalizes provider-agnostic SDK messages for the instruction-role capabilities of a concrete provider protocol.
Package messagecompat normalizes provider-agnostic SDK messages for the instruction-role capabilities of a concrete provider protocol.
provider
alibabacloud/images
Package images provides an Alibaba Cloud Model Studio DashScope image provider.
Package images provides an Alibaba Cloud Model Studio DashScope image provider.
alibabacloud/speech
Package speech provides an Alibaba Cloud DashScope CosyVoice TTS provider.
Package speech provides an Alibaba Cloud DashScope CosyVoice TTS provider.
ark/videos
Package videos provides Ark / ModelArk video generation support for BytePlus ModelArk and Volcengine Ark data-plane APIs.
Package videos provides Ark / ModelArk video generation support for BytePlus ModelArk and Volcengine Ark data-plane APIs.
deepgram/speech
Package speech provides a Deepgram TTS provider.
Package speech provides a Deepgram TTS provider.
elevenlabs/speech
Package speech provides an ElevenLabs TTS provider.
Package speech provides an ElevenLabs TTS provider.
microsoft/speech
Package speech provides a Microsoft Azure Cognitive Services Text-to-Speech provider.
Package speech provides a Microsoft Azure Cognitive Services Text-to-Speech provider.
minimax/speech
Package speech provides a MiniMax TTS provider.
Package speech provides a MiniMax TTS provider.
openai/speech
Package speech provides an OpenAI-compatible TTS provider that targets the /audio/speech endpoint.
Package speech provides an OpenAI-compatible TTS provider that targets the /audio/speech endpoint.
openrouter/speech
Package speech provides an OpenRouter TTS provider.
Package speech provides an OpenRouter TTS provider.
openrouter/videos
Package videos provides OpenRouter video generation support.
Package videos provides OpenRouter video generation support.
volcengine/speech
Package speech provides a Volcengine SAMI TTS provider.
Package speech provides a Volcengine SAMI TTS provider.

Jump to

Keyboard shortcuts

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