Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ all entries as endpoint configs (no comment keys).
M1 wiring proof: both placeholder endpoints resolve to one local llama-server
(OpenAI-compatible, `http://127.0.0.1:8091/v1`, no cloud keys).

buzz-agent env contract (crates/buzz-agent/src/config.rs, pinned at the M1
binary SHA): `provider=openai` reads `OPENAI_COMPAT_API_KEY` +
buzz-agent env contract (crates/buzz-agent/src/config.rs):
`provider=openai-compat` reads the optional `OPENAI_COMPAT_API_KEY` and required
`OPENAI_COMPAT_BASE_URL`; the runtime sets `BUZZ_AGENT_MODEL` from the
manifest endpoint name, which overrides `OPENAI_COMPAT_MODEL` — llama-server
ignores the model name, so the placeholder value is harmless there.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"local/placeholder-orchestrator": {
"provider": "openai",
"provider": "openai-compat",
"api_key_env": "OPENAI_COMPAT_API_KEY",
"env": {
"OPENAI_COMPAT_BASE_URL": "http://127.0.0.1:8091/v1"
}
},
"local/placeholder-worker": {
"provider": "openai",
"provider": "openai-compat",
"api_key_env": "OPENAI_COMPAT_API_KEY",
"env": {
"OPENAI_COMPAT_BASE_URL": "http://127.0.0.1:8091/v1"
Expand Down
22 changes: 22 additions & 0 deletions crates/buzz-acp/src/setup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ pub(crate) enum RequirementPayload {
NormalizedField { field: String },
/// An env-backed credential that is absent.
EnvKey { key: String },
/// Invalid or conflicting configuration with actionable remediation copy.
ConfigInvalid { message: String },
/// A CLI authentication step that must be completed interactively.
CliLogin {
probe_args: Vec<String>,
Expand Down Expand Up @@ -127,6 +129,7 @@ impl RequirementPayload {
RequirementPayload::EnvKey { key } => {
format!("set `{}` in Edit Agent → Environment variables", key)
}
RequirementPayload::ConfigInvalid { message } => message.clone(),
RequirementPayload::CliLogin {
setup_copy,
availability,
Expand Down Expand Up @@ -725,6 +728,25 @@ mod tests {
assert!(body.contains("Fizz"), "nudge body should name the agent");
}

#[test]
fn nudge_body_explains_conflicting_openai_origin() {
let payload = SetupPayload {
agent_name: "OpenAI Agent".to_string(),
agent_pubkey: "test".to_string(),
requirements: vec![RequirementPayload::ConfigInvalid {
message:
"remove `OPENAI_COMPAT_BASE_URL` or switch the provider to `openai-compat`"
.to_string(),
}],
};

let body = payload.nudge_body();

assert!(body.contains("remove `OPENAI_COMPAT_BASE_URL`"));
assert!(body.contains("switch the provider to `openai-compat`"));
assert!(body.contains(r#""surface":"config_invalid""#));
}

#[test]
fn nudge_body_codex_copy_does_not_mention_openai_api_key() {
let payload = SetupPayload {
Expand Down
33 changes: 20 additions & 13 deletions crates/buzz-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,17 @@ ANTHROPIC_API_KEY=sk-ant-... \
ANTHROPIC_MODEL=claude-sonnet-4-5 \
./target/release/buzz-agent

# Or any OpenAI-compatible endpoint
# Or OpenAI
BUZZ_AGENT_PROVIDER=openai \
OPENAI_COMPAT_API_KEY=sk-... \
OPENAI_API_KEY=sk-... \
OPENAI_COMPAT_MODEL=gpt-5 \
OPENAI_COMPAT_BASE_URL=https://api.openai.com/v1 \
./target/release/buzz-agent

# Or an OpenAI-compatible endpoint (the key is optional)
BUZZ_AGENT_PROVIDER=openai-compat \
OPENAI_COMPAT_API_KEY=local-secret \
OPENAI_COMPAT_MODEL=llama3 \
OPENAI_COMPAT_BASE_URL=http://localhost:11434/v1 \
./target/release/buzz-agent

# Or OpenRouter
Expand Down Expand Up @@ -135,14 +141,15 @@ Everything is environment variables. No flags, no config files. (We are a subpro

| Variable | Default | Notes |
|---|---|---|
| `BUZZ_AGENT_PROVIDER` | — | Required. `anthropic`, `openai`, `openrouter`, `databricks`, or `databricks_v2`. No implicit fallback — the agent errors at startup when this is unset. |
| `BUZZ_AGENT_PROVIDER` | — | Required. `anthropic`, `openai`, `openai-compat`, `openrouter`, `databricks`, or `databricks_v2`. No implicit fallback — the agent errors at startup when this is unset. |
| `ANTHROPIC_API_KEY` | — | Required when provider=anthropic. |
| `ANTHROPIC_MODEL` | — | Required when provider=anthropic. |
| `ANTHROPIC_BASE_URL` | `https://api.anthropic.com` | |
| `ANTHROPIC_API_VERSION` | `2023-06-01` | |
| `OPENAI_COMPAT_API_KEY` | — | Required when provider=openai. |
| `OPENAI_COMPAT_MODEL` | — | Required when provider=openai. |
| `OPENAI_COMPAT_BASE_URL` | `https://api.openai.com/v1` | Point at vLLM, llama.cpp, Ollama, etc. |
| `OPENAI_API_KEY` | — | Required when provider=openai. Never used by provider=openai-compat. |
| `OPENAI_COMPAT_API_KEY` | — | Optional when provider=openai-compat. Never used by provider=openai. |
| `OPENAI_COMPAT_MODEL` | — | Required when provider=openai or provider=openai-compat. |
| `OPENAI_COMPAT_BASE_URL` | — | Required for provider=openai-compat. Custom values are rejected by provider=openai, which is pinned to `https://api.openai.com/v1`. |
| `OPENAI_COMPAT_API` | `auto` | `auto` \| `chat` \| `responses`. `auto` picks Responses for `*.openai.com`, Chat Completions everywhere else. |
| `OPENROUTER_API_KEY` | — | Required when provider=openrouter. |
| `OPENROUTER_MODEL` | — | Required when provider=openrouter. Use OpenRouter's `vendor/model` id, e.g. `anthropic/claude-sonnet-4.5`. |
Expand Down Expand Up @@ -235,17 +242,17 @@ lifecycle hook — see [MCP_DRIVEN_HOOKS.md](../../docs/MCP_DRIVEN_HOOKS.md).
|---|---|---|---|
| Anthropic | `anthropic` | `POST {base}/v1/messages` | claude-sonnet-4-5, claude-opus-4 |
| OpenAI | `openai` | `POST {base}/responses` | gpt-5, gpt-5-mini, o4-mini, gpt-4o |
| vLLM | `openai` | `POST {base}/chat/completions` | any tool-calling model |
| llama.cpp | `openai` | `POST {base}/chat/completions` | any tool-calling GGUF |
| Ollama | `openai` | `POST {base}/chat/completions` | llama3.1, qwen2.5-coder |
| Block Gateway | `openai` | `POST {base}/chat/completions` | gpt-5, claude |
| vLLM | `openai-compat` | `POST {base}/chat/completions` | any tool-calling model |
| llama.cpp | `openai-compat` | `POST {base}/chat/completions` | any tool-calling GGUF |
| Ollama | `openai-compat` | `POST {base}/chat/completions` | llama3.1, qwen2.5-coder |
| Block Gateway | `openai-compat` | `POST {base}/chat/completions` | gpt-5, claude |
| OpenRouter | `openrouter` | `POST {base}/chat/completions` | anything they route (extended-thinking replay, provider-agnostic tool calling) |
| Databricks | `databricks` | `POST {host}/serving-endpoints/{model}/invocations` | goose-claude-4-6-sonnet |
| Databricks AI Gateway v2 | `databricks_v2` | `POST {host}/ai-gateway/{provider}/v1/...` | databricks-gpt-5-5, databricks-claude-opus-4-7 |

If `BUZZ_AGENT_PROVIDER=anthropic` is selected without `ANTHROPIC_API_KEY`, `BUZZ_AGENT_PROVIDER=openai` is selected without `OPENAI_COMPAT_API_KEY`, or `BUZZ_AGENT_PROVIDER=openrouter` is selected without `OPENROUTER_API_KEY`, the agent returns an error — there is no implicit fallback to another provider.
If `BUZZ_AGENT_PROVIDER=anthropic` is selected without `ANTHROPIC_API_KEY`, `BUZZ_AGENT_PROVIDER=openai` is selected without `OPENAI_API_KEY`, or `BUZZ_AGENT_PROVIDER=openrouter` is selected without `OPENROUTER_API_KEY`, the agent returns an error — there is no implicit fallback to another provider.

`provider=openai` speaks two HTTP dialects: the [Responses API](https://platform.openai.com/docs/api-reference/responses) (`/v1/responses`, required for GPT-5 / o-series tool-calling on OpenAI's own service) and the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat) (`/chat/completions`, the broadly-supported OpenAI-compatible wire format).
`provider=openai` and `provider=openai-compat` share the same OpenAI transport implementation, which speaks the [Responses API](https://platform.openai.com/docs/api-reference/responses) (`/v1/responses`, required for GPT-5 / o-series tool-calling on OpenAI's own service) and the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat) (`/chat/completions`, the broadly-supported OpenAI-compatible wire format). They do not share credentials or origins: official OpenAI reads only `OPENAI_API_KEY` and is pinned to `https://api.openai.com/v1`; compatible endpoints read only the optional `OPENAI_COMPAT_API_KEY` and require an explicit `OPENAI_COMPAT_BASE_URL`. Selecting `provider=openai` with a custom compatible URL fails closed with an instruction to select `openai-compat`.

By default (`OPENAI_COMPAT_API=auto`) the agent picks **Responses** when `OPENAI_COMPAT_BASE_URL` points at an `*.openai.com` host and **Chat Completions** everywhere else. Pin the choice explicitly with `OPENAI_COMPAT_API=chat` or `OPENAI_COMPAT_API=responses` for providers that diverge from the default (e.g. a Responses-compatible self-hosted gateway).

Expand Down
Loading
Loading