-
Notifications
You must be signed in to change notification settings - Fork 0
feat(infra): enable Claude/Anthropic in the gateway with agent-default alias (ADR 09) #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,26 @@ | ||
| # Routing Claude Code CLI Through Sluice | ||
|
|
||
| Status: Open — awaiting decision | ||
| Date: 2026-04-30 | ||
| Status: Accepted — 2026-07-03 | ||
| Date: 2026-04-30 (analysis) · 2026-07-03 (decision) | ||
|
|
||
| ## Decision | ||
|
|
||
| CI and other **non-interactive** agents route Claude Code (and any Anthropic-SDK | ||
| caller) through Sluice on API-key billing, requesting the **`agent-default`** | ||
| alias. Sluice resolves `agent-default` to a concrete Claude model (currently | ||
| `claude-haiku-4-5`, degrading to `claude-sonnet-4-6` then Azure `premium`), so | ||
| the model can be re-routed centrally without touching any CI workflow. | ||
| **Interactive** Claude Code use backed by a Claude Max subscription stays on | ||
| **direct-Anthropic** auth — the flat-fee Max economics dominate per-token API | ||
| rates for human-driven sessions (see the analysis and recommendation below, | ||
| which this decision adopts unchanged). | ||
|
|
||
| Implemented via: an `anthropic` entry in the LiteLLM `model_list` | ||
| (`infra/modules/sluice_aca/main.tf`), the `LITELLM_ANTHROPIC_API_KEY` secret | ||
| wired through the standard `extra_providers` path, provider activation in the | ||
| deploy workflows (`ANTHROPIC_API_KEY` GitHub secret → `TF_VAR_extra_providers`), | ||
| and a dedicated `mystira-ci` virtual key allow-listing `agent-default` | ||
| (`scripts/keys.yaml`). | ||
|
|
||
| ## Question | ||
|
|
||
|
|
@@ -109,30 +128,43 @@ spending subscription quota through a self-hosted gateway. | |
| on subscription-first (Claude Code in particular). Routing through API | ||
| could miss features for weeks. | ||
|
|
||
| ## If we proceed — what setup looks like | ||
|
|
||
| ### Operator (one-time) | ||
|
|
||
| 1. Generate an Anthropic Console API key (pay-per-token billing on the | ||
| org's Console account) | ||
| 2. Store it in KV: `az keyvault secret set --vault-name pvc-prod-sluice-kv | ||
| --name anthropic-api-key --value <key>` | ||
| 3. Add a `model_list` entry to the LiteLLM YAML in | ||
| `infra/modules/sluice_aca/main.tf` — e.g. | ||
| ```yaml | ||
| - model_name: claude-sonnet-4-6 | ||
| litellm_params: | ||
| model: anthropic/claude-sonnet-4-6 | ||
| api_key: os.environ/LITELLM_ANTHROPIC_API_KEY | ||
| model_info: | ||
| input_cost_per_token: 0.000003 | ||
| output_cost_per_token: 0.000015 | ||
| ``` | ||
| 4. Mount `LITELLM_ANTHROPIC_API_KEY` from the `anthropic-api-key` KV | ||
| secret on the LiteLLM container | ||
| 5. Update each consumer's `keys.yaml` entry to include the new Claude | ||
| model in `models: [...]` | ||
| 6. `python scripts/manage_keys.py apply` — re-applies model allowlists | ||
| ## Setup (implemented) | ||
|
|
||
| Steps 2–5 of the original analysis are now **in code** — the `model_list` | ||
| entries, the `LITELLM_ANTHROPIC_API_KEY` env wiring (via the standard | ||
| `extra_providers` path), provider activation in the deploy workflows, and the | ||
| `mystira-ci` allow-list all ship in this repo. The remaining operator work is | ||
| placing the key and running one deploy. | ||
|
|
||
| ### Operator (one-time — the single manual step to go live) | ||
|
|
||
| 1. Generate an Anthropic Console API key (pay-per-token billing on the org's | ||
| Console account). | ||
| 2. Make the key available to Terraform. Two equivalent options: | ||
| - **Preferred — GitHub environment secret** (matches how every other | ||
| provider key is supplied): set `ANTHROPIC_API_KEY` as a `prod` environment | ||
| secret. The `Configure optional model providers` step folds it into | ||
| `TF_VAR_extra_providers` as | ||
| `{ anthropic = { api_key = <key>, env_name = "LITELLM_ANTHROPIC_API_KEY" } }`, | ||
| and Terraform then **creates** the KV secret `provider-anthropic-key` and | ||
| mounts it as the `LITELLM_ANTHROPIC_API_KEY` env var on the LiteLLM | ||
| container. | ||
| - **Direct KV** (matches the runbook convention below): | ||
| ``` | ||
| az keyvault secret set --vault-name pvc-prod-sluice-kv \ | ||
| --name anthropic-api-key --value <key> | ||
| ``` | ||
|
Comment on lines
+152
to
+156
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If an operator follows this Direct KV option, the deploy will still be Azure-only: the deploy workflows only populate Useful? React with 👍 / 👎. |
||
| Use this only if wiring `TF_VAR_extra_providers` from that KV secret rather | ||
| than a GitHub secret; the `extra_providers`-managed secret is | ||
| `provider-anthropic-key`, so keep the two out of sync deliberately. | ||
| 3. Deploy: run the `deploy` workflow (or merge to trigger it) so Terraform | ||
| provisions `provider-anthropic-key`, injects the env var, and renders the | ||
| Claude models + `agent-default` alias into the LiteLLM config. | ||
| 4. `python scripts/manage_keys.py apply` — applies the `mystira-ci` allow-list | ||
| (and any other `keys.yaml` changes) on the running proxy. | ||
|
|
||
| CI consumers then set the two env vars from the per-consumer snippet below, | ||
| using `vkey-mystira-ci`, and request `model="agent-default"`. | ||
|
Comment on lines
+166
to
+167
Comment on lines
+166
to
+167
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Split the CI and interactive paths. Line 166 says CI consumers should use 🤖 Prompt for AI Agents |
||
|
|
||
| ### Per-consumer (each service / user) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -67,6 +67,23 @@ keys: | |||||
| operator: jurie@phoenixvc.tech | ||||||
| use: interactive | ||||||
|
|
||||||
| # ── Mystira CI (non-interactive agents via Sluice — ADR 09) ────────────── | ||||||
| # Claude Code CLI in CI / automation routes through Sluice on API-key | ||||||
| # billing. Requests model="agent-default"; Sluice maps it to the cheap | ||||||
| # Claude tier and can re-route later without a workflow change. Separate | ||||||
| # from claude-code-cli (interactive, Max-subscription — stays direct). | ||||||
| - alias: mystira-ci | ||||||
| max_budget: 10 | ||||||
| budget_duration: 7d | ||||||
| rpm_limit: 120 | ||||||
| tpm_limit: 200000 | ||||||
| models: [agent-default, claude-haiku-4-5, claude-sonnet-4-6, gpt-4o, text-embedding-3-large, auto, cheap-fast, cheap-reasoning, premium] | ||||||
|
Comment on lines
+75
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Missing
🔧 Proposed fix- models: [agent-default, claude-haiku-4-5, claude-sonnet-4-6, gpt-4o, text-embedding-3-large, auto, cheap-fast, cheap-reasoning, premium]
+ models: [agent-default, claude-haiku-4-5, claude-sonnet-4-6, gpt-4o, text-embedding-3-large, auto, cheap-fast, cheap-reasoning, cheap-long-context, premium]📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| metadata: | ||||||
| project: mystira-workspace | ||||||
| service: ci | ||||||
| use: non-interactive | ||||||
| contact: jurie@phoenixvc.tech | ||||||
|
|
||||||
| # ── Dashboard Test Playground (small, public-key style) ────────────────── | ||||||
| - alias: dashboard-playground | ||||||
| max_budget: 1 | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Label the shell snippet.
The fenced command block at Line 153 is missing a language identifier, which violates the Markdown docs guideline and trips markdownlint. Add
bash(orsh) to the fence.Suggested fix
As per coding guidelines,
docs/**/*.md: Include code blocks with language identifiers in Markdown documentation.📝 Committable suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 153-153: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Sources: Coding guidelines, Linters/SAST tools