Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`~/.amplifier/openai-chatgpt-oauth.json`, refreshing them itself. Requires "Sign in with
device code" enabled in the account's ChatGPT Security settings. Like `github-copilot`,
`auth set openai-chatgpt` is refused — there is no static key to store.
- **Chat Completions provider.** `provider.module: "chat-completions"` is now a valid
host-config value, backed by `amplifier-module-provider-chat-completions`. It integrates
any server speaking the OpenAI Chat Completions wire format
(`/v1/chat/completions`) — llama.cpp, vLLM, LM Studio, LocalAI, SGLang, TGI, and other
OpenAI-compatible endpoints — distinct from `openai`, which uses the OpenAI Responses
API. Its credential is an endpoint, not a key: `CHAT_COMPLETIONS_BASE_URL` (required)
selects the server, and `CHAT_COMPLETIONS_API_KEY` (optional) is sent only when set,
since local servers commonly need none. Both are environment-only; the persisted
credentials file is not consulted for this provider. Default model is `default`.

## [0.12.0] — 2026-07-29

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Public integrations run opencode, paperclip, and NanoClaw on it: see [who has in

`amplifier-agent` ships with:

- Six providers behind one interface: Anthropic, OpenAI, Azure OpenAI, Ollama, GitHub Copilot, and ChatGPT (a Plus/Pro/Team subscription via OAuth device-code, no API key), with credentials read from the environment or a cached OAuth session
- Seven providers behind one interface: Anthropic, OpenAI, Azure OpenAI, Ollama, GitHub Copilot, ChatGPT (a Plus/Pro/Team subscription via OAuth device-code, no API key), and Chat Completions (any OpenAI Chat Completions-compatible endpoint, e.g. llama.cpp, vLLM, LM Studio), with credentials read from the environment or a cached OAuth session
- Role-based model routing, so a sub-agent gets a model matched to its job rather than the frontier model for everything, re-matched when you switch providers
- Context management that keeps long sessions running, compacting history before it overruns the window
- Tools for filesystem, bash, web, search, todo, and MCP
Expand Down Expand Up @@ -83,7 +83,7 @@ amplifier-agent auth status # diagnose env-vs-file precedence per p
amplifier-agent models list # enumerate available models from providers
```

Full precedence rules, GitHub Copilot's environment-only caveat, and the host config file schema are in [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md).
Full precedence rules, GitHub Copilot's environment-only caveat, the `chat-completions` provider's required `CHAT_COMPLETIONS_BASE_URL`, and the host config file schema are in [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md).

## Use it from your code

Expand Down
20 changes: 15 additions & 5 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ Provider is auto-detected from environment variables in this precedence:
3. `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_ENDPOINT`
4. `OLLAMA_HOST` (defaults to `http://localhost:11434`)

`github-copilot` and `openai-chatgpt` are excluded from this auto-detect chain -- neither resolves
from a single API-key environment variable. `github-copilot` reads its own token chain from the
environment (see below). `openai-chatgpt` has no credential env var at all: it authenticates via
OAuth device-code, caching tokens to `~/.amplifier/openai-chatgpt-oauth.json`. Both must be
selected explicitly with `provider.module` in a host config file.
`github-copilot`, `openai-chatgpt`, and `chat-completions` are excluded from this auto-detect chain
-- none of them resolves from a single API-key environment variable. `github-copilot` reads its own
token chain from the environment (see below). `openai-chatgpt` has no credential env var at all: it
authenticates via OAuth device-code, caching tokens to `~/.amplifier/openai-chatgpt-oauth.json`.
`chat-completions` needs an endpoint rather than a key (`CHAT_COMPLETIONS_BASE_URL`, plus optional
`CHAT_COMPLETIONS_API_KEY`) and has no implicit default endpoint to fall back to. All three must be
selected explicitly with `provider.module` in a host config file, rather than silently winning a
"first match" race.

Override by passing `--config <path>` at a host config file that names a provider explicitly.

Expand Down Expand Up @@ -73,6 +76,13 @@ This matters for hosts that spawn `amplifier-agent` as a subprocess: once you ha
>
> An existing `gh` or VS Code login may already authenticate it through the SDK's cached OAuth, so try it before exporting anything. This is a temporary limitation: the real fix is in the provider module, whose token resolver needs to read the agent-delivered credential from its config before falling back to the environment. `auth set` support returns once that lands.

> **`chat-completions` is environment-only too, but for a different reason.** Its "credential" is the target `base_url`, not an API key, so it is read purely from the environment: `CHAT_COMPLETIONS_BASE_URL` (required — the server to talk to) and `CHAT_COMPLETIONS_API_KEY` (optional; local servers like llama.cpp, vLLM, LM Studio, and LocalAI commonly need none). The persisted credentials file is not consulted for this provider.
>
> ```bash
> export CHAT_COMPLETIONS_BASE_URL=http://localhost:8000/v1
> # export CHAT_COMPLETIONS_API_KEY=... # only if your server requires one
> ```

The file format is a versioned JSON envelope:

```jsonc
Expand Down
2 changes: 1 addition & 1 deletion docs/INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The engine runs **one turn per invocation** and exits. Continuity across turns c

`amplifier-agent` is a standalone binary. You do not need the Amplifier CLI, bundles, or any other repository in the `microsoft/amplifier*` family, and none of them is a substitute for it here.

Use it when your software needs to run an agent: a loop with tools, file access, sub-agents, and/or multi-turn state. It also works for plain LLM calls, where you get routing across six providers behind one interface.
Use it when your software needs to run an agent: a loop with tools, file access, sub-agents, and/or multi-turn state. It also works for plain LLM calls, where you get routing across seven providers behind one interface.

Then pick a surface below, install the engine ([INSTALL.md](INSTALL.md)), and finish with the [checklist](#checklist-for-a-new-integration).

Expand Down
2 changes: 1 addition & 1 deletion docs/LAYERS_AND_RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ The engine ships with `bundle.md` baked into the wheel. It declares which module

**Pre-wired modules:**

- **Providers:** `provider-anthropic`, `provider-openai`, `provider-azure-openai`, `provider-ollama`, `provider-github-copilot`, `provider-openai-chatgpt`
- **Providers:** `provider-anthropic`, `provider-openai`, `provider-azure-openai`, `provider-ollama`, `provider-github-copilot`, `provider-openai-chatgpt`, `provider-chat-completions`
- **Orchestrator:** `loop-streaming` (with `extended_thinking: true`)
- **Context:** `context-simple` (300K tokens, auto-compact at 80%)
- **Tools:** `tool-filesystem`, `tool-bash`, `tool-web`, `tool-search`, `tool-todo`, `tool-apply-patch`, `tool-delegate`, `tool-mcp`, `tool-skills`, `tool-mode`, `tool-recipes`
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/architecture.dot
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ digraph amplifier_agent {

node [fillcolor="#d9d9d9"];
kernel [label="amplifier-foundation\nbundle / session kernel"];
providers [label="LLM providers\nanthropic · openai · azure\nollama · copilot · chatgpt"];
providers [label="LLM providers\nanthropic · openai · azure\nollama · copilot · chatgpt\nchat-completions"];
mcp [label="MCP servers"];
}

Expand Down
3 changes: 2 additions & 1 deletion docs/spec/bundle-and-cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ default_provider: anthropic REQUIRED, engine-level, top-level key

providers: install-only stubs, no config and no credentials
provider-anthropic, provider-openai, provider-azure-openai,
provider-ollama, provider-github-copilot, provider-openai-chatgpt
provider-ollama, provider-github-copilot, provider-openai-chatgpt,
provider-chat-completions

session.orchestrator: loop-streaming extended_thinking: true
session.context: context-simple max_tokens 300000, auto_compact
Expand Down
3 changes: 2 additions & 1 deletion docs/spec/host-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ approval.patterns must be a list of strings
`provider` selects the provider module and carries its config.

```
provider.module one of: anthropic, openai, azure-openai, ollama, github-copilot, openai-chatgpt
provider.module one of: anthropic, openai, azure-openai, ollama, github-copilot,
openai-chatgpt, chat-completions
provider.config free-form; belongs to the provider module
```

Expand Down
55 changes: 37 additions & 18 deletions docs/spec/providers-and-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ routes a wire `model` field to a provider (see `http-face.md`).

## Supported providers

Six providers are supported, and only six. The provider name is the value used in configuration,
Seven providers are supported, and only seven. The provider name is the value used in configuration,
in `auth` subcommands, and in `models list --provider`.

```
anthropic provider-anthropic
openai provider-openai
azure-openai provider-azure-openai
ollama provider-ollama
github-copilot provider-github-copilot
openai-chatgpt provider-openai-chatgpt
anthropic provider-anthropic
openai provider-openai
azure-openai provider-azure-openai
ollama provider-ollama
github-copilot provider-github-copilot
openai-chatgpt provider-openai-chatgpt
chat-completions provider-chat-completions
```

Each module is installed from `git+https://github.com/microsoft/amplifier-module-<module>@main`.
All six are declared by the shipped bundle as install-only, so preparing the bundle makes every
provider importable before any session exists.
All seven are declared by the shipped bundle (`bundle.md`'s top-level `providers:` stub list) as
install-only, so preparing the bundle makes every provider importable before any session exists.

The agent holds no static table of default models, credential field shapes, or display names. Those
come from the provider module at runtime, so they cannot drift from provider truth.
Expand All @@ -44,12 +45,13 @@ configuration.
Primary variables, in the order consulted:

```
anthropic ANTHROPIC_API_KEY
openai OPENAI_API_KEY
azure-openai AZURE_OPENAI_API_KEY, then AZURE_OPENAI_KEY
ollama OLLAMA_HOST, then OLLAMA_BASE_URL
github-copilot GITHUB_TOKEN
openai-chatgpt (none -- OAuth device-code)
anthropic ANTHROPIC_API_KEY
openai OPENAI_API_KEY
azure-openai AZURE_OPENAI_API_KEY, then AZURE_OPENAI_KEY
ollama OLLAMA_HOST, then OLLAMA_BASE_URL
github-copilot GITHUB_TOKEN
openai-chatgpt (none -- OAuth device-code)
chat-completions CHAT_COMPLETIONS_BASE_URL, plus optional CHAT_COMPLETIONS_API_KEY
```

`AZURE_OPENAI_KEY` is the only deprecated alias. Consulting it emits a one-time warning on stderr.
Expand All @@ -68,13 +70,25 @@ openai-chatgpt has no environment variable at all. It resolves from a cached OAu
flow (`login_on_mount`) and refreshed automatically thereafter. Its resolution reports source
`"file"` when a token is cached and `"none"` otherwise -- never `"env"`.

chat-completions has its own dedicated resolution branch, distinct from the generic
env-then-file chain above: its primary variable's value lands in `fields["base_url"]`, not
`fields["api_key"]`, because the thing it needs to know is *which server to talk to*, not a
secret. `CHAT_COMPLETIONS_API_KEY` is consulted only when set (local servers such as llama.cpp,
vLLM, LM Studio, and LocalAI commonly need none) and lands in `fields["api_key"]` alongside it.
Unlike every other provider, the persisted credentials file is never consulted for
chat-completions -- with no `CHAT_COMPLETIONS_BASE_URL` in the environment it resolves
unconditionally to `source == "none"`, with no file fallback and no usable default to fall back
to (unlike ollama's built-in localhost).

A resolution reports the provider, whether it resolved, the source (`env`, `file`, `default`, or
`none`), the variable consulted, and the resolved fields. Ollama backed only by the built-in default
host reports unresolved on purpose, so auto-enrollment does not enlist a local daemon that may not
be running.

Requesting credentials for a key-based provider that resolves to `none` is an error. Ollama and
unrecognized provider names never raise.
Requesting credentials for a key-based provider that resolves to `none` is an error. Ollama,
chat-completions, and unrecognized provider names never raise -- chat-completions is not one of
the key-based providers this rule applies to, so a missing `CHAT_COMPLETIONS_BASE_URL` is left for
the provider module itself to reject at call time, not raised here.

## The credentials file

Expand Down Expand Up @@ -115,6 +129,11 @@ to `~/.amplifier/openai-chatgpt-oauth.json`, refreshed by the provider module it
are enumerated in the same `_CONFIG_CREDENTIAL_UNSUPPORTED` gate; this is temporary and specific to
these two providers.

`auth set chat-completions` is accepted (unlike github-copilot, it is not refused), but as noted
above the chat-completions resolution branch never reads the credentials file -- only
`CHAT_COMPLETIONS_BASE_URL` / `CHAT_COMPLETIONS_API_KEY` in the environment are consulted. Set
those instead of relying on `auth set` for this provider.

`auth clear` without `--force` exits 2.

## Provider selection at boot
Expand All @@ -125,7 +144,7 @@ these two providers.
3. no further fallback: a bundle declaring neither is a hard error at boot
```

`provider.module` is closed to the six supported names. Any other value fails validation with
`provider.module` is closed to the seven supported names. Any other value fails validation with
error code `config_invalid_provider_module`. `"auto"` is not a valid value.

There is no `--provider` flag and no environment-based provider auto-detection. See Non-goals.
Expand Down
6 changes: 3 additions & 3 deletions skills/amplifier-agent/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ metadata:

`amplifier-agent` is an agent engine that other software runs on. Give it a prompt and it runs the full loop, with tools, sub-agents, skills, and MCP, then returns a result. Anything that can spawn a subprocess can use it; Python hosts can embed the engine library in-process instead.

Reach for it when the project needs an *agent* (a tool loop, file access, sub-agents, multi-turn state) rather than a single completion. You can also use it for plain LLM calls, with routing across six providers behind one interface.
Reach for it when the project needs an *agent* (a tool loop, file access, sub-agents, multi-turn state) rather than a single completion. You can also use it for plain LLM calls, with routing across seven providers behind one interface.

**The engine runs one turn per invocation and exits.** Continuity across turns comes from a session id, not from a long-lived process. Every surface below is a different way of delivering a prompt to that same engine.

Expand Down Expand Up @@ -54,7 +54,7 @@ The installer needs `uv` and `curl` and will not bootstrap them silently; it tel

Install as **the same user that runs the host process**; a host spawning a subprocess inherits that user's `PATH`. `amplifier-agent doctor` is the check that the install actually works, so run it before writing any integration code.

Credentials are read from the environment, first match wins: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `AZURE_OPENAI_API_KEY` plus `AZURE_OPENAI_ENDPOINT`, `OLLAMA_HOST`. GitHub Copilot is environment-only (`COPILOT_AGENT_TOKEN`, `COPILOT_GITHUB_TOKEN`, `GH_TOKEN`, `GITHUB_TOKEN`). ChatGPT (`openai-chatgpt`) has no credential env var at all: it authenticates via OAuth device-code, caching tokens to `~/.amplifier/openai-chatgpt-oauth.json`. Or store a static key with `amplifier-agent auth set anthropic sk-ant-...` (not supported for github-copilot or openai-chatgpt).
Credentials are read from the environment, first match wins: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `AZURE_OPENAI_API_KEY` plus `AZURE_OPENAI_ENDPOINT`, `OLLAMA_HOST`. GitHub Copilot is environment-only (`COPILOT_AGENT_TOKEN`, `COPILOT_GITHUB_TOKEN`, `GH_TOKEN`, `GITHUB_TOKEN`). ChatGPT (`openai-chatgpt`) has no credential env var at all: it authenticates via OAuth device-code, caching tokens to `~/.amplifier/openai-chatgpt-oauth.json`. The `chat-completions` provider is environment-only too, for any OpenAI Chat Completions-compatible endpoint (llama.cpp, vLLM, LM Studio, LocalAI, and similar): `CHAT_COMPLETIONS_BASE_URL` (required) plus optional `CHAT_COMPLETIONS_API_KEY`. Or store a static key with `amplifier-agent auth set anthropic sk-ant-...` (not supported for github-copilot or openai-chatgpt).

## Pick a surface

Expand Down Expand Up @@ -191,7 +191,7 @@ A per-instance config file looks like this:
| `config_unreadable`, `config_malformed_json` | The `--config` file could not be opened, or is not a JSON object | Check the path the host wrote, and that it serialized an object |
| `config_unknown_key` | Unrecognized **top-level** config key | The top level is closed: `approval`, `provider`, `providers`, `mcp`, `skills`, `debug`, `allowProtocolSkew` |
| `config_invalid_type` | A known key has the wrong type, or an unknown sub-key in a closed inner shape | `skills.*` and `debug.*` are closed and raise this rather than `config_unknown_key`, which is reserved for the top level and `providers.<id>` entries |
| `config_invalid_provider_module` | `provider.module` is not a known provider | One of `anthropic`, `openai`, `azure-openai`, `ollama`, `github-copilot`, `openai-chatgpt`. `"auto"` is not valid |
| `config_invalid_provider_module` | `provider.module` is not a known provider | One of `anthropic`, `openai`, `azure-openai`, `ollama`, `github-copilot`, `openai-chatgpt`, `chat-completions`. `"auto"` is not valid |
| `protocol_version_mismatch` | Wrapper and engine protocol versions differ | Update the lagging side. `allowProtocolSkew` is an unblock, not a fix |
| `lifecycle_unsupported` | `submit()` called twice on one handle | New handle per turn, same `sessionId` with `resume` |
| `env_injection_rejected` | The wrapper refused the environment you asked it to inject | Check the key against the wrapper's allowlist and blocked-key list |
Expand Down
Loading