openrouter-agent-sdk-go is a stateful Go SDK for OpenRouter with sibling ergonomics to the Claude and Codex SDKs, but with an OpenRouter-native public identity.
This is a full cutover:
- package name:
openroutersdk - root option type:
OpenRouterAgentOptions - canonical transport hook:
WithTransport(...) - no exported Claude or CLI compatibility aliases
go get github.com/ethpandaops/openrouter-agent-sdk-goThe repo ships a sibling-style Makefile:
make testruns race-enabled package tests with coverage output.make test-integrationruns./integration/...with-tags=integration.make auditruns the aggregate quality gate.
Integration setup:
OPENROUTER_API_KEYis required.OPENROUTER_MODELis optional and defaults toopenrouter/free.- Use the free router for routine smoke runs; override
OPENROUTER_MODELwith a pinned tool-capable model when you need stricter capability coverage. - integration tests are written to skip cleanly when the API key is absent.
package main
import (
"context"
"fmt"
"time"
openroutersdk "github.com/ethpandaops/openrouter-agent-sdk-go"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
for msg, err := range openroutersdk.Query(
ctx,
openroutersdk.Text("Write a two-line haiku about Go concurrency."),
openroutersdk.WithAPIKey("..."),
openroutersdk.WithModel("openrouter/free"),
) {
if err != nil {
panic(err)
}
if result, ok := msg.(*openroutersdk.ResultMessage); ok && result.Result != nil {
fmt.Println(*result.Result)
}
}
}Query(ctx, content, ...opts)andQueryStream(...)returniter.Seq2[Message, error].NewClient()exposesStart,StartWithContent,StartWithStream,Query,ReceiveMessages,ReceiveResponse,Interrupt,SetPermissionMode,SetModel,ListModels,ListModelsResponse,GetMCPStatus,RewindFiles, andClose.- Unsupported peer-parity controls such as
ReconnectMCPServer,ToggleMCPServer,StopTask, andSendToolResultare present onClientand return typedUnsupportedControlErrors. UserMessageContentis the canonical input shape. UseText(...)for text-only calls andBlocks(...)withImageInput(...),FileInput(...),AudioInput(...), orVideoInput(...)for multimodal chat-completions requests.WithSDKTools(...)registers high-level in-process tools undermcp__sdk__<name>.WithOnUserInput(...)handles SDK-owned user-input prompts built on top of tool calling.ListModels(...)andListModelsResponse(...)use OpenRouter model discovery.StatSession(...),ListSessions(...), andGetSessionMessages(...)operate on the SDK's local persisted session store.
- Authenticated discovery defaults to
/api/v1/models/user. - Fallback catalog discovery uses
/api/v1/models. ModelInfoexposes helper methods such asCostTier(),SupportsToolCalling(),SupportsStructuredOutput(),SupportsReasoning(),SupportsImageInput(),SupportsImageOutput(),SupportsWebSearch(),SupportsPromptCaching(),MaxContextLength(), and parsed pricing helpers.
- Generated images are surfaced as
*ImageBlockvalues insideAssistantMessage.Content. ImageBlock.Decode()returns raw bytes plus media type for data-URL-backed images.ImageBlock.Save(path)writes generated images to disk.- Live image-generation coverage is available behind the integration build tag when
OPENROUTER_IMAGE_MODELis set.
OpenRouter multimodal input in this SDK is block-based and currently targets chat-completions mode.
content := openroutersdk.Blocks(
openroutersdk.TextInput("Compare these two screenshots and the attached spec file."),
openroutersdk.ImageInput("https://example.com/before.png"),
openroutersdk.ImageInput("data:image/png;base64,..."),
openroutersdk.FileInput("spec.pdf", "data:application/pdf;base64,..."),
)
for msg, err := range openroutersdk.Query(ctx, content,
openroutersdk.WithAPIKey("..."),
openroutersdk.WithModel("openai/gpt-4o-mini"),
) {
_ = msg
_ = err
}ImageInput(...)accepts a normal URL or a base64 data URL.FileInput(...)accepts a filename plusfile_dataURL/data URL.AudioInput(...)accepts base64 audio data plus a format.VideoInput(...)accepts a normal URL or a data URL.- The SDK rejects multimodal input in Responses mode with an explicit error instead of faking support.
Session APIs are local SDK APIs, not remote OpenRouter account sessions.
- They read from the SDK session store configured with
WithSessionStorePath(...)orOPENROUTER_AGENT_SESSION_STORE_PATH. - They do not derive from chat
session_id. - They do not derive from Responses
previous_response_id.
OpenRouter does not have meaningful backend equivalents for some sibling control-plane methods. The SDK exposes those methods where peer parity matters, but they fail explicitly with UnsupportedControlError instead of faking semantics.
Runnable examples live under examples.