Skip to main content

Model Providers

Chronos uses a pluggable provider interface for LLM backends. All providers implement the same interface, so you can swap OpenAI for Anthropic, Ollama, or any OpenAI-compatible endpoint without changing your agent code.

Provider Interface

type Provider interface {
Chat(ctx context.Context, req *ChatRequest) (*ChatResponse, error)
StreamChat(ctx context.Context, req *ChatRequest) (<-chan *ChatResponse, error)
Name() string
Model() string
}
MethodDescription
ChatSends a request and returns a complete response
StreamChatReturns a channel of partial responses; channel closes when complete
NameHuman-readable provider identifier
ModelDefault model ID for this provider

Provider Table

ProviderConstructorNotes
OpenAImodel.NewOpenAI(apiKey)GPT-5.5, GPT-5, GPT-4o, o-series (o3, o4-mini)
Anthropicmodel.NewAnthropic(apiKey)Claude Opus 4.8, Sonnet 5, Haiku 4.5, Fable 5
Geminimodel.NewGemini(apiKey)Gemini 3.5 Flash, 3.1 Pro, 3 Flash
Mistralmodel.NewMistral(apiKey)Mistral Large / Medium / Small
Ollamamodel.NewOllama(host, model)Local models (e.g., http://localhost:11434, llama3.2)
Azuremodel.NewAzureOpenAI(endpoint, key, deployment)Azure OpenAI
Bedrockmodel.NewBedrock(region, accessKey, secretKey, modelID)AWS Bedrock (Claude, Llama, Titan, …)
Vertex AImodel.NewOpenAICompatibleWithConfig("vertex", cfg)Google Cloud Vertex AI via OpenAI-compatible endpoint
Compatiblemodel.NewOpenAICompatible(name, url, key, model)Any OpenAI-compatible API

Google Cloud Vertex AI

Vertex AI exposes an OpenAI-compatible endpoint, so Chronos drives it through NewOpenAICompatibleWithConfig. Auth uses a short-lived GCP access token as the Bearer credential:

project := os.Getenv("GOOGLE_CLOUD_PROJECT")
location := "us-central1"
baseURL := fmt.Sprintf(
"https://%s-aiplatform.googleapis.com/v1beta1/projects/%s/locations/%s/endpoints/openapi",
location, project, location,
)

provider := model.NewOpenAICompatibleWithConfig("vertex", model.ProviderConfig{
APIKey: os.Getenv("GOOGLE_ACCESS_TOKEN"), // gcloud auth print-access-token
BaseURL: baseURL,
Model: "google/gemini-2.5-pro",
})

AWS Bedrock

provider := model.NewBedrock(
os.Getenv("AWS_REGION"),
os.Getenv("AWS_ACCESS_KEY_ID"),
os.Getenv("AWS_SECRET_ACCESS_KEY"),
"anthropic.claude-3-5-sonnet-20241022-v2:0",
)

Convenience Constructors

For providers that expose an OpenAI-compatible API, Chronos provides convenience constructors:

ConstructorBase URLUse Case
model.NewTogether(apiKey, modelID)api.together.xyzTogether AI
model.NewGroq(apiKey, modelID)api.groq.comGroq
model.NewDeepSeek(apiKey, modelID)api.deepseek.comDeepSeek
model.NewOpenRouter(apiKey, modelID)openrouter.aiOpenRouter (multi-model)
model.NewFireworks(apiKey, modelID)api.fireworks.aiFireworks AI
model.NewPerplexity(apiKey, modelID)api.perplexity.aiPerplexity
model.NewAnyscale(apiKey, modelID)api.endpoints.anyscale.comAnyscale Endpoints

Example:

provider := model.NewGroq(os.Getenv("GROQ_API_KEY"), "llama-3.1-70b-versatile")

ProviderConfig

For full configuration, use ProviderConfig with the WithConfig constructor:

cfg := model.ProviderConfig{
APIKey: os.Getenv("OPENAI_API_KEY"),
BaseURL: "https://api.openai.com/v1",
Model: "gpt-5.5",
MaxRetries: 3,
TimeoutSec: 60,
OrgID: "org-xxx",
ContextWindow: 128000,
}

provider := model.NewOpenAIWithConfig(cfg)
FieldTypeDescription
APIKeystringAuthentication key
BaseURLstringAPI base URL (optional for most providers)
ModelstringModel identifier
MaxRetriesintRetry count on transient failures
TimeoutSecintRequest timeout in seconds
OrgIDstringOrganization ID (OpenAI)
ContextWindowintOverride default context window size

ChatRequest

Input to a chat completion:

FieldTypeDescription
ModelstringOverride provider default
Messages[]MessageConversation messages
MaxTokensintMaximum tokens to generate
Temperaturefloat64Sampling temperature (0-2)
TopPfloat64Nucleus sampling
StreamboolEnable streaming
Tools[]ToolDefinitionFunction definitions for tool calling
Stop[]stringStop sequences
ResponseFormatstring"json_object" for JSON mode
Reasoning*ReasoningConfigProvider-native reasoning/thinking settings

ChatResponse

Output of a chat completion:

FieldTypeDescription
IDstringResponse ID
ContentstringGenerated text
RolestringUsually "assistant"
UsageUsageToken counts
ToolCalls[]ToolCallRequested tool invocations
StopReasonStopReasonWhy generation stopped
ReasoningstringProvider-approved reasoning output, kept separate from final answer text
DeltaboolTrue when streaming partial response

Native reasoning and thinking

Use ReasoningConfig to request native reasoning without embedding provider-specific request fields in application code:

req := &model.ChatRequest{
Messages: messages,
Reasoning: &model.ReasoningConfig{
Enabled: true,
Effort: "high",
BudgetTokens: 4096,
Summary: true,
},
}
FieldDescription
EnabledRequest native reasoning when the provider supports it. When false, all other native reasoning fields are ignored.
EffortNormalized effort (low, medium, high); mapped to OpenAI-compatible reasoning_effort
BudgetTokensThinking budget; mapped to Anthropic thinking and Gemini thinkingConfig
SummaryRequest provider-approved reasoning/thought output where supported

YAML agents expose the same controls plus Chronos prompt strategies:

reasoning:
strategy: reflection # none, cot, reflection
native: true
effort: high
budget_tokens: 4096
summary: true

Provider and model support varies. Content always remains the final answer; available reasoning is returned separately in ChatResponse.Reasoning. Applications should avoid displaying reasoning unless the user explicitly requested it.

:::caution Native reasoning with tools Anthropic and Gemini attach signed thought blocks to tool calls. Chronos currently fails closed when native reasoning and tools are combined for those providers, rather than dropping signatures and producing an invalid follow-up request. OpenAI-compatible reasoning with tools remains available. Use prompt strategy reasoning or disable tools until signed thought-block preservation is supported. :::

Usage

type Usage struct {
PromptTokens int
CompletionTokens int
}

StopReason Constants

ConstantValueMeaning
StopReasonEnd"end"Natural completion
StopReasonMaxTokens"max_tokens"Hit token limit
StopReasonToolCall"tool_call"Model requested tool execution
StopReasonFilter"content_filter"Content filter triggered

Streaming

Use StreamChat for token-by-token streaming. The returned channel receives partial ChatResponse values with Delta: true; the final response may include usage and StopReason.

ch, err := provider.StreamChat(ctx, &model.ChatRequest{
Messages: messages,
Stream: true,
})
if err != nil {
log.Fatal(err)
}

for resp := range ch {
if resp.Content != "" {
fmt.Print(resp.Content)
}
}
fmt.Println()

Embeddings Providers

For RAG and vector search, use an EmbeddingsProvider:

type EmbeddingsProvider interface {
Embed(ctx context.Context, req *EmbeddingRequest) (*EmbeddingResponse, error)
}
ConstructorDescription
model.NewOpenAIEmbeddings(apiKey)OpenAI text-embedding-3-small
model.NewOpenAIEmbeddingsWithConfig(cfg)With full config
model.NewOllamaEmbeddings(baseURL, modelID)Local embeddings via Ollama
model.NewCachedEmbeddings(inner)In-memory cache wrapper

Example:

embedder := model.NewOpenAIEmbeddings(os.Getenv("OPENAI_API_KEY"))
cached := model.NewCachedEmbeddings(embedder)

FallbackProvider

FallbackProvider tries multiple providers in order. If the primary fails, it automatically falls back to the next. Useful for primary-cloud to cheaper-model or cloud to local-Ollama failover.

primary := model.NewOpenAI(openAIKey)
fallback := model.NewOllama("http://localhost:11434", "llama3.2")

provider, err := model.NewFallbackProvider(primary, fallback)
if err != nil {
log.Fatal(err)
}

// Optional: log when fallback occurs
provider.OnFallback = func(index int, name string, err error) {
log.Printf("Provider %s failed, trying next: %v", name, err)
}

resp, err := provider.Chat(ctx, req)

Complete Example

package main

import (
"context"
"fmt"
"log"
"os"

"github.com/spawn08/chronos/engine/model"
"github.com/spawn08/chronos/sdk/agent"
)

func main() {
ctx := context.Background()

provider := model.NewOpenAI(os.Getenv("OPENAI_API_KEY"))

a, err := agent.New("demo", "Demo Agent").
WithModel(provider).
WithSystemPrompt("You are a concise assistant.").
Build()
if err != nil {
log.Fatal(err)
}

resp, err := a.Chat(ctx, "Say hello in one sentence.")
if err != nil {
log.Fatal(err)
}

fmt.Println(resp.Content)
fmt.Printf("Tokens: %d prompt, %d completion\n",
resp.Usage.PromptTokens, resp.Usage.CompletionTokens)
}