Model or dataset
charmbracelet/fantasy avatar
charmbracelet/fantasy

charmbracelet/fantasy: one Go API for multiple LLM providers

Build AI agents with Go. Multiple providers, multiple models, one API. đź§™

1,014 stars142 forksGoApache-2.0

At a glance

What is it?
Fantasy is a Go library for building AI agents that swap between Anthropic, OpenAI, Gemini, Bedrock and OpenAI-compatible endpoints without rewriting your tool code. It is a work in progress, and it says so.
Who is it for?
Adopt Fantasy if you are writing a Go agent and you want the provider choice to stay a runtime decision rather than a rewrite, which is the problem it was built to solve for Crush. Do not adopt it if you need image models, audio models or PDF uploads, because the README lists all three as unsupported.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The provider-swapping problem Fantasy was written to solve

Every Go team that ships an agent eventually hits the same wall. The Anthropic SDK, the OpenAI SDK and Google's genai package each have their own types for a message, a tool call and a streaming chunk. If you want to offer your users a model picker, you either write an adapter layer yourself or you pick one vendor and stay there.

Fantasy takes the second job off your hands. Its stated goal is a wide variety of providers and models behind a single API. The audience is narrow and specific: Go developers building agents, not teams who want a hosted agent platform and not Python users. The README is blunt about why the library exists. It was built to power Crush, Charm's coding agent, so the feature set reflects what a coding agent needs and nothing else.

That origin is the most useful thing to know about the project. A library extracted from a single consumer tends to have sharp edges exactly where that consumer never went.

How the provider, model and tool layers fit together

The architecture is three layers stacked in one package. A provider is constructed first, with credentials passed as options. The provider then hands back a language model by name. The model is what an agent is built around, together with a system prompt and a list of tools.

The repository layout matches that story: provider.go and provider_registry.go sit at the top level, providers/ holds the per-vendor packages, and agent.go, tool.go, content.go and model.go carry the shared types. A schema/ directory and a jsonrepair/ directory exist alongside them, which tells you structured output is handled rather than left to the caller.

The provider list is the interesting part. Microsoft Azure, Amazon Bedrock and OpenRouter have dedicated packages. Everything else is expected to work through openaicompat, a generic OpenAI-compatible layer. That is a pragmatic design and also the main source of uncertainty: compatibility is a spectrum, and the README asks users to file an issue or open a PR when they find a provider that needs special treatment. If your provider is not on the dedicated list, you are the one who finds out whether it really fits.

Installing Fantasy and running a first agent

There is no binary and no CLI. Fantasy is a Go module, so installation means adding it to a module that already has Go 1.27.0 or newer, which is the version pinned in the repository's go.mod.

The module path is charm.land/fantasy, and the README's example imports the OpenRouter provider package as well. The README uses a placeholder for the API key; supply your own through whatever mechanism you already use for secrets.

go
import "charm.land/fantasy"
import "charm.land/fantasy/providers/openrouter"

// Choose your fave provider.
provider, err := openrouter.New(openrouter.WithAPIKey(myHotKey))
if err != nil {
	fmt.Fprintln(os.Stderr, "Whoops:", err)
	os.Exit(1)
}

ctx := context.Background()

With the provider in place, the next step is asking it for a model by name. The README uses the model name moonshotai/kimi-k2.

go
// Pick your fave model.
model, err := provider.LanguageModel(ctx, "moonshotai/kimi-k2")
if err != nil {
	fmt.Fprintln(os.Stderr, "Dang:", err)
	os.Exit(1)
}

Then a tool is defined and the agent is assembled from the model plus options. The README's example uses a tool named cute_dog_tool, and the tool function it references, fetchCuteDogInfoFunc, is not shown in the README. The examples directory is where the full implementations live.

go
// Make your own tools.
cuteDogTool := fantasy.NewAgentTool(
  "cute_dog_tool",
  "Provide up-to-date info on cute dogs.",
  fetchCuteDogInfoFunc,
)

// Equip your agent.
agent := fantasy.NewAgent(
  model,
  fantasy.WithSystemPrompt("You are a moderately helpful, dog-centric assistant."),
  fantasy.WithTools(cuteDogTool),
)

Finally the agent is given a prompt, and the result's response content is read as text.

go
// Put that agent to work!
const prompt = "Find all the cute dogs in Silver Lake, Los Angeles."
result, err := agent.Generate(ctx, fantasy.AgentCall{Prompt: prompt})
if err != nil {
    fmt.Fprintln(os.Stderr, "Oof:", err)
    os.Exit(1)
}
fmt.Println(result.Response.Content.Text())

One detail worth noticing before you build anything real: the API has a separate streaming path. The repository contains agent_stream_test.go and an examples/stream directory, so Generate and streaming are distinct entry points rather than one call with a flag. The README does not document how the two behave differently under errors, which matters if you plan to stream to a terminal.

What Fantasy does not support yet

The README carries a section titled Work in Progress, and it is unusually direct. Image models, audio models and PDF uploads are all listed as not yet supported. For a coding agent that reads source files, that set of gaps is survivable. For a document pipeline or a voice interface, Fantasy is simply the wrong tool, and no amount of provider coverage compensates.

The second limitation is subtler. The library is extracted from a single application, so the abstractions are shaped by what Crush needed. Tool definitions, content blocks and streaming are the areas where a coding agent spends its time, and those are the areas that look most developed. Anything outside that path is more likely to be missing or thin.

The version numbers are the third thing to weigh. Releases in the v0.43 range landed within days of each other in early September 2026, and the last push to the default branch was on 2026-09-10. Frequent minor releases at a pre-1.0 version mean the API can move. If you pin a version and upgrade deliberately, that is manageable. If you track main, expect to chase changes.

Fantasy compared with calling a vendor SDK directly

The real alternative is not another agent framework. It is the code you would write anyway: import github.com/anthropics/anthropic-sdk-go or github.com/openai/openai-go/v3 and build on that one vendor's types.

The difference in approach is where the abstraction lives. With a single SDK, your tool definitions, message history and streaming loop are written against that vendor's structs. Adding a second provider later means writing a translation layer between two type systems, and that translation layer is exactly what Fantasy ships as its core. The trade-off runs the other way too: a vendor SDK exposes every parameter that vendor supports, while a shared API can only expose what all providers can express. If you depend on a provider-specific feature, the shared layer is a ceiling, not a floor.

Fantasy's answer to that ceiling is openaicompat. Instead of writing a bespoke package per provider, it treats OpenAI-compatible HTTP as the common denominator and reserves dedicated packages for the large clouds. That keeps the surface small, at the cost of the compatibility questions described above.

Maintenance, licensing and the cost of upgrading

The repository is not archived, and the last push was on 2026-09-10. Releases arrive often: v0.43.1 on 2026-09-07, v0.43.0 on 2026-09-04 and v0.42.1 on 2026-09-01. That cadence is the upgrade cost. At v0.43, a minor bump can carry a breaking change, and the README does not document a deprecation policy or a compatibility guarantee across minor versions.

The dependency footprint is the other ongoing cost. The go.mod requires the Anthropic, OpenAI and Google genai SDKs plus the AWS SDK, and the indirect block pulls in a long chain through the Google Cloud packages. A project that only ever talks to one provider still compiles the dependencies for the others unless the build is trimmed. The examples live in their own module, with examples/go.mod and examples/go.sum, which keeps them out of the main module's dependency graph.

The licence is Apache-2.0, and the repository includes both a LICENSE and a NOTICE file. The NOTICE file is the part teams forget. Apache-2.0 permits commercial use and modification, and it also carries attribution and notice-retention obligations. Read both files with whoever handles licensing at your organisation; this is a description of what is in the repository, not legal advice.

Editorial conclusion

Adopt Fantasy if you are writing a Go agent and you want the provider choice to stay a runtime decision rather than a rewrite, which is the problem it was built to solve for Crush. Do not adopt it if you need image models, audio models or PDF uploads, because the README lists all three as unsupported. Before committing, verify the two things the README leaves open: whether the provider you need has a dedicated package or only works through openaicompat, and whether the pinned Go 1.27.0 toolchain fits your build images.

Frequently asked questions

What is charmbracelet/fantasy?

It is a Go library for building AI agents against multiple model providers through a single API. The README describes it as multi-provider, multi-model, one API, and notes it was built to power Crush.

How do I install charmbracelet/fantasy?

It is a Go module rather than a binary, so you add charm.land/fantasy to a Go module and import the provider package you want, such as charm.land/fantasy/providers/openrouter. The repository's go.mod pins Go 1.27.0.

Does charmbracelet/fantasy support image or audio models?

No. The README's Work in Progress section lists image models, audio models and PDF uploads as things Fantasy does not yet support, and invites pull requests for them.

Which providers does charmbracelet/fantasy support?

Microsoft Azure, Amazon Bedrock and OpenRouter have dedicated packages, and the README says many other providers work through openaicompat, the generic OpenAI-compatible layer. Providers that need special treatment are handled through issues or pull requests.

What licence does charmbracelet/fantasy use?

The repository is licensed under Apache-2.0 and includes both a LICENSE and a NOTICE file. The NOTICE file carries attribution obligations that are worth reviewing before redistribution.

Official sources

  1. charmbracelet/fantasy on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/charmbracelet-fantasy.svg)](https://hysenlabs.com/projects/charmbracelet-fantasy)