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
14 changes: 14 additions & 0 deletions .amplifier/digital-twin-universe/profiles/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
# GITEA_URL e.g. http://localhost:10110 (Gitea mirror base)
# GITEA_TOKEN admin token for the url_rewrites proxy auth
# AA_E2E_BASE_IMAGE container image (a pre-baked image can replace the stock one)
# VLLM_BASE_URL vllm suite only; e.g. http://localhost:8007/v1 (may be empty)
# VLLM_MODEL vllm suite only; model id to pin (may be empty)
# VLLM_API_KEY vllm suite only; usually empty for a local server
#
# The VLLM_* trio are vars, not passthrough entries, precisely because they must be
# localhost-rewritten: the vLLM server runs on the host, and DTU rewrites localhost ->
# bridge gateway IP in var values but copies passthrough values verbatim. See
# tests/e2e/framework/dtu_manager.py::_build_varmap.

base:
image: "${AA_E2E_BASE_IMAGE}"
Expand Down Expand Up @@ -46,7 +54,13 @@ provision:
files:
- { src: ./dtu/install-amplifier-agent.sh, dest: /root/e2e/install-amplifier-agent.sh }
- { src: ./dtu/host-config.json, dest: /root/e2e/host-config.json }
- { src: ./dtu/setup-vllm-env.sh, dest: /root/e2e/setup-vllm-env.sh }
setup_cmds:
# Only the vllm suite needs this, and it is a no-op for everyone else: with the
# vars unset the script writes empty exports, the provider reports source "none",
# and the suite skips itself. Runs before the install so the environment is in
# place for anything the install step might consult.
- "bash /root/e2e/setup-vllm-env.sh '${VLLM_BASE_URL}' '${VLLM_MODEL}' '${VLLM_API_KEY}'"
# Insurance for the GitHub Copilot provider, which spawns a Node-based Copilot CLI.
# DTU exports SSL_CERT_FILE / REQUESTS_CA_BUNDLE / UV_NATIVE_TLS but never
# NODE_EXTRA_CA_CERTS, so a Node process would not trust the interception CA.
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
env read also accepts `GEMINI_API_KEY`, `GOOGLE_API_KEY` taking precedence when both
are set), and `auth set gemini` is accepted and stores the key like any other keyed
provider. Default model is `gemini-2.5-flash`.
- **vLLM provider.** `provider.module: "vllm"` is now a valid host-config value, backed
by `amplifier-module-provider-vllm`. It integrates a self-hosted or remote vLLM server
for open-weight models (e.g. gpt-oss), talking vLLM's OpenAI-compatible **Responses
API** (`/v1/responses`) rather than the Chat Completions wire — distinct from
`chat-completions`, and a sibling of `openai`/`azure-openai` on the wire shape, while
remaining endpoint-agnostic like `chat-completions`. Supports reasoning models,
reasoning-block separation, and tool calling. Its credential is an endpoint, not a key:
`VLLM_BASE_URL` (required) selects the server, and `VLLM_API_KEY` (optional) is sent
only when set, since a local vLLM server commonly needs none. When it is unset, the
same placeholder the provider module itself defaults to is supplied, so `run` and
`models list --provider vllm` both work against a keyless server; a key set in host
config's `provider.config` still takes precedence over that placeholder. Both
variables are environment-only; the persisted credentials file is not consulted for
this provider.

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion 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:

- Eight providers behind one interface: Anthropic, OpenAI, Azure OpenAI, Ollama, GitHub Copilot, ChatGPT (a Plus/Pro/Team subscription via OAuth device-code, no API key), Chat Completions (any OpenAI Chat Completions-compatible endpoint, e.g. llama.cpp, vLLM, LM Studio), and Gemini (Google's Gemini API, large context windows plus thinking/reasoning support), with credentials read from the environment or a cached OAuth session
- Nine providers behind one interface: Anthropic, OpenAI, Azure OpenAI, Ollama, GitHub Copilot, ChatGPT (a Plus/Pro/Team subscription via OAuth device-code, no API key), Chat Completions (any OpenAI Chat Completions-compatible endpoint, e.g. llama.cpp, vLLM, LM Studio), Gemini (Google's Gemini API, large context windows plus thinking/reasoning support), and vLLM (a self-hosted or remote vLLM server via its OpenAI-compatible Responses API, for open-weight models like gpt-oss), 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
20 changes: 14 additions & 6 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ Provider is auto-detected from environment variables in this precedence:
4. `OLLAMA_HOST` (defaults to `http://localhost:11434`)
5. `GOOGLE_API_KEY` (`GEMINI_API_KEY` is also accepted by the provider module, `GOOGLE_API_KEY` takes precedence)

`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
`github-copilot`, `openai-chatgpt`, `chat-completions`, and `vllm` 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.
`CHAT_COMPLETIONS_API_KEY`) and has no implicit default endpoint to fall back to. `vllm` is the same
shape, pointed at a self-hosted or remote vLLM server instead: `VLLM_BASE_URL` (required), plus
optional `VLLM_API_KEY`, with no implicit default endpoint either. All four 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 @@ -85,6 +86,13 @@ This matters for hosts that spawn `amplifier-agent` as a subprocess: once you ha
> # export CHAT_COMPLETIONS_API_KEY=... # only if your server requires one
> ```

> **`vllm` is the same shape as `chat-completions`, environment-only for the same reason.** Its "credential" is the target `base_url` -- the self-hosted or remote vLLM server to talk to -- not an API key: `VLLM_BASE_URL` (required) and `VLLM_API_KEY` (optional; a local vLLM server needs none, and both the module and the agent fall back to the placeholder `"EMPTY"` when it is unset, so a keyless server works for both `run` and `models list`). The persisted credentials file is not consulted for this provider either. Unlike `chat-completions`, which speaks the OpenAI Chat Completions wire, `vllm` talks vLLM's OpenAI-compatible **Responses API**, and supports reasoning models, reasoning-block separation, and tool calling.
>
> ```bash
> export VLLM_BASE_URL=http://localhost:8000/v1
> # export VLLM_API_KEY=... # only if your server requires one
> ```

The file format is a versioned JSON envelope:

```jsonc
Expand Down
25 changes: 25 additions & 0 deletions docs/E2E_TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,31 @@ snapshot-at-launch reason. `GOOGLE_API_KEY` is the canonical variable even thoug
Google GenAI SDK also accepts `GEMINI_API_KEY`: it takes precedence, and it is the one
`providers list` and `models list` consult.

`VLLM_BASE_URL` is optional and only the `vllm` suite uses it. It names a vLLM server you
are running yourself, so that suite skips unless you point it at one — and skips again if
the endpoint is set but not answering, since a server being down is a fact about your
machine rather than a defect in amplifier-agent.

```bash
export VLLM_BASE_URL=http://localhost:8007/v1 # required to run the suite
export VLLM_MODEL=your-org/your-model # optional; see below
export VLLM_API_KEY=... # optional; local servers rarely need one
uv run python tests/e2e/framework/cli.py run vllm
```

Write the URL exactly as you would use it on the host: `localhost` is rewritten to the
container bridge gateway IP at launch, because inside the DTU `localhost` is the container
itself. That rewriting is why these three travel as DTU `--var` values rather than
`passthrough` entries — passthrough copies host values verbatim, and a verbatim
`localhost` would resolve to the wrong machine. The plumbing is
`dtu_manager._build_varmap()` plus `provisioning/setup-vllm-env.sh`, which also exempts
the vLLM host from the interception proxy so streaming is not buffered.

`VLLM_MODEL` is optional. When unset, the suite uses the first model id the server
advertises on `/v1/models`, which is the right answer for a single-model vLLM process.
Set it when your server hosts more than one. Your server must bind `0.0.0.0` rather than
`127.0.0.1`, or the container cannot reach it.

## Running

```bash
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 eight 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 nine 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`, `provider-chat-completions`, `provider-gemini`
- **Providers:** `provider-anthropic`, `provider-openai`, `provider-azure-openai`, `provider-ollama`, `provider-github-copilot`, `provider-openai-chatgpt`, `provider-chat-completions`, `provider-gemini`, `provider-vllm`
- **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\nchat-completions · gemini"];
providers [label="LLM providers\nanthropic · openai · azure\nollama · copilot · chatgpt\nchat-completions · gemini · vllm"];
mcp [label="MCP servers"];
}

Expand Down
2 changes: 1 addition & 1 deletion docs/spec/bundle-and-cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ 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-chat-completions, provider-gemini
provider-chat-completions, provider-gemini, provider-vllm

session.orchestrator: loop-streaming extended_thinking: true
session.context: context-simple max_tokens 300000, auto_compact
Expand Down
4 changes: 2 additions & 2 deletions docs/spec/host-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ approval.patterns must be a list of strings

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

Expand All @@ -102,7 +102,7 @@ module config. Closed per-entry schema:
}
```

`module` defaults to the entry's own id when omitted and must be one of the eight valid module names.
`module` defaults to the entry's own id when omitted and must be one of the nine valid module names.
`config` must be an object. Unknown keys inside an entry raise `config_unknown_key`. An empty
`providers` object passes validation; HTTP startup rejects it separately at boot so single-turn mode
never trips on a stale block.
Expand Down
36 changes: 30 additions & 6 deletions docs/spec/providers-and-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ routes a wire `model` field to a provider (see `http-face.md`).

## Supported providers

Eight providers are supported, and only eight. The provider name is the value used in configuration,
Nine providers are supported, and only nine. The provider name is the value used in configuration,
in `auth` subcommands, and in `models list --provider`.

```
Expand All @@ -21,10 +21,11 @@ github-copilot provider-github-copilot
openai-chatgpt provider-openai-chatgpt
chat-completions provider-chat-completions
gemini provider-gemini
vllm provider-vllm
```

Each module is installed from `git+https://github.com/microsoft/amplifier-module-<module>@main`.
All eight are declared by the shipped bundle (`bundle.md`'s top-level `providers:` stub list) as
All nine 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
Expand Down Expand Up @@ -54,6 +55,7 @@ github-copilot GITHUB_TOKEN
openai-chatgpt (none -- OAuth device-code)
chat-completions CHAT_COMPLETIONS_BASE_URL, plus optional CHAT_COMPLETIONS_API_KEY
gemini GOOGLE_API_KEY
vllm VLLM_BASE_URL (required), plus optional VLLM_API_KEY
```

`AZURE_OPENAI_KEY` is the only deprecated alias. Consulting it emits a one-time warning on stderr.
Expand Down Expand Up @@ -82,6 +84,23 @@ chat-completions -- with no `CHAT_COMPLETIONS_BASE_URL` in the environment it re
unconditionally to `source == "none"`, with no file fallback and no usable default to fall back
to (unlike ollama's built-in localhost).

vllm has the same shape of dedicated resolution branch, for the same reason: `VLLM_BASE_URL`
lands in `fields["base_url"]`, and `VLLM_API_KEY` lands in `fields["api_key"]` alongside it.
`VLLM_API_KEY` differs from chat-completions' optional key in one way: when it is unset,
`fields["api_key"]` is still populated, with the same `"EMPTY"` placeholder the provider module
defaults to. A local vLLM server commonly needs no auth, but the OpenAI SDK the module wraps
still requires some value, and `models list` builds the provider straight from these fields
rather than through the module's `mount()` — so omitting the field entirely would hand that
path an empty key and fail against a keyless server. The placeholder is not a credential: a
key supplied through host config's `provider.config` takes precedence over it, while a real
`VLLM_API_KEY` from the environment is re-asserted over host config as usual. The persisted
credentials file
is never consulted for vllm either -- with no `VLLM_BASE_URL` in the environment it resolves
unconditionally to `source == "none"`, with no file fallback and no usable default to fall back
to. The distinction from chat-completions is the wire, not the credential shape: vllm targets
vLLM's OpenAI-compatible **Responses API** (`/v1/responses`), not the Chat Completions API, which
is what lets it support reasoning models, reasoning-block separation, and tool calling.

gemini lists only `GOOGLE_API_KEY` here. The Google GenAI SDK also accepts `GEMINI_API_KEY`
(`GOOGLE_API_KEY` takes precedence when both are set), and the provider module's own env read
honours that; listing `GEMINI_API_KEY` in this table would mark it deprecated, which it is not.
Expand All @@ -95,9 +114,10 @@ host reports unresolved on purpose, so auto-enrollment does not enlist a local d
be running.

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.
chat-completions, vllm, and unrecognized provider names never raise -- chat-completions and vllm
are not among the key-based providers this rule applies to, so a missing `CHAT_COMPLETIONS_BASE_URL`
or `VLLM_BASE_URL` is left for the respective provider module to reject at call time, not raised
here.

## The credentials file

Expand Down Expand Up @@ -143,6 +163,10 @@ above the chat-completions resolution branch never reads the credentials file --
`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 set vllm` is likewise accepted and likewise ignored at resolution time -- only
`VLLM_BASE_URL` / `VLLM_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 @@ -153,7 +177,7 @@ those instead of relying on `auth set` for this provider.
3. no further fallback: a bundle declaring neither is a hard error at boot
```

`provider.module` is closed to the eight supported names. Any other value fails validation with
`provider.module` is closed to the nine 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
Loading