Skip to content

feat: surface per-model cost and limit from static catalog#2

Merged
manojp99 merged 2 commits into
mainfrom
feat/surface-cost-and-limit
Jun 21, 2026
Merged

feat: surface per-model cost and limit from static catalog#2
manojp99 merged 2 commits into
mainfrom
feat/surface-cost-and-limit

Conversation

@manojp99

@manojp99 manojp99 commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Re-enables cost and limit fields in build_provider_block(). Both were stripped earlier to dodge opencode's silent config-decode failure mode (a single malformed entry wipes the whole provider's models block).

Why a static catalog is necessary

opencode bundles the models.dev registry — the catalog has rich pricing for canonical provider IDs (anthropic, openai, gemini, etc.). But opencode keys catalog lookups by (providerID, modelID), not just modelID. Our custom amplifier provider serves the same model IDs Anthropic uses (claude-haiku-4-5, claude-sonnet-4-6, ...) but opencode treats them as un-cataloged because the providerID doesn't match.

Empirically confirmed: Without per-model cost in our config, opencode renders every turn at $0.00:

// our config (limit only):
"claude-haiku-4-5-20251001": {"name": "...", "limit": {...}}

// opencode's loaded state:
"cost": {"input": 0, "output": 0, "cache": {"read": 0, "write": 0}}    ← zeros

Two alternatives we tested first

Alternative Result
opencode's provider.api: 'anthropic' field as catalog-inheritance hint Did NOT cause inheritance. cost remained zeros after restart.
Submit amplifier to models.dev (sst/models) External review cycle, and our served model set varies by amplifier-agent's host_config.json — can't declare a fixed list upstream.

Hardcoded catalog in the adapter is the self-contained path. Maintenance: when a provider changes prices, update MODEL_PRICING_PER_MILLION. Source of truth referenced inline: github.com/sst/models.

Generated config — before vs after

Before:

"claude-haiku-4-5-20251001": { "name": "Claude Haiku 4.5" }

After:

"claude-haiku-4-5-20251001": {
  "name":  "Claude Haiku 4.5",
  "cost":  { "input": 1.0, "output": 5.0, "cache_read": 0.1, "cache_write": 1.25 },
  "limit": { "context": 200000, "output": 64000 }
}

opencode normalizes flat cache_read/cache_write to nested cache: {read, write} internally — confirmed via /provider endpoint probe.

Initial coverage

Provider Models Source
Anthropic Claude Haiku 4.5, Sonnet 4.6, Opus 4.8 models.dev / Anthropic pricing page
OpenAI Placeholder (commented) — fill when amplifier-agent serves them n/a

Compatibility with the cost telemetry path

microsoft/amplifier-agent#68 surfaces real per-turn cost_usd on the chat-completions response (Decimal-as-string, from provider modules' compute_cost). That ships on the wire today; this PR is for opencode's TUI picker display (per-model rate) and the rolling cost calc opencode does from tokens × rates. The two complement each other:

  • wire cost_usd: authoritative $$ for cost-aware clients (curl, custom wrappers, telemetry tools)
  • catalog rates (this PR): opencode's TUI rendering

amplifier-app-opencode hard-depends on three things added in amplifier-agent
0.8.0 (PR #65 + the release commit ee04f9f):

  - The `serve chat-completions` HTTP face (the wire amplifier-opencode talks to)
  - Multi-provider routing in lifespan (so `/v1/models` lists more than anthropic)
  - The `auth` subcommand (so credentials persist across terminals)

Older amplifier-agent versions silently skip these features, which surfaces
as cryptic 'amplifier-agent server did not become ready' or '/v1/models
returned 0 models' errors during `amplifier-opencode` startup.

Make the version requirement explicit in the README's prerequisites
section so users upgrade BEFORE running the adapter rather than after
debugging the first failed launch.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
@manojp99 manojp99 force-pushed the feat/surface-cost-and-limit branch from 0c2a131 to b17cb9e Compare June 21, 2026 17:58
@manojp99 manojp99 changed the title feat: surface per-model cost and limit from amplifier-agent feat: surface per-model limit from amplifier-agent Jun 21, 2026
@manojp99 manojp99 force-pushed the feat/surface-cost-and-limit branch from b17cb9e to 020c6c5 Compare June 21, 2026 18:47
@manojp99 manojp99 changed the title feat: surface per-model limit from amplifier-agent feat: surface per-model cost and limit from static catalog Jun 21, 2026
Re-enables per-model cost and limit in build_provider_block(). Both
fields were previously stripped to avoid opencode's silent
config-decode-failure mode (a single malformed cost or limit entry
wipes the entire provider's models block).

## Why a static catalog

opencode's bundled models.dev registry has pricing for canonical
providers (anthropic, openai, gemini, etc.) but it keys lookups by
(providerID, modelID). Our amplifier provider has its own providerID
even though we serve the SAME model IDs anthropic uses (claude-haiku-4-5,
claude-sonnet-4-6, etc.), so opencode never matches our entries to the
catalog -- it renders our turns at $0.00 unless cost is declared in
the opencode.json model block.

We tested two alternatives first:

  1. opencode's `provider.api: 'anthropic'` field as a catalog-
     inheritance hint -- empirically does NOT cause inheritance from
     anthropic's catalog entries. cost remained zeros after restart.
  2. Submitting amplifier as a provider to models.dev (sst/models) --
     would require external review, and our served model set varies
     by amplifier-agent's host_config.json, so we can't declare a
     fixed list upstream.

Hardcoded MODEL_PRICING_PER_MILLION catalog in cli.py is the
self-contained path. Initial entries cover Anthropic's Claude 4
family; OpenAI placeholders left commented for when amplifier-agent
serves them. Source of truth: https://github.com/sst/models (same
registry opencode bundles).

## Companion telemetry path

amplifier-agent#68 surfaces real per-turn `cost_usd` on the
chat-completions response. opencode's @ai-sdk/openai-compatible
adapter doesn't read it today (non-standard OpenAI field), but it
ships on the wire for any cost-aware client.

## Generated config -- before vs after

  before:  { 'name': 'Claude Haiku 4.5' }
  after:   { 'name':  'Claude Haiku 4.5',
             'cost':  { 'input': 1.0, 'output': 5.0,
                        'cache_read': 0.1, 'cache_write': 1.25 },
             'limit': { 'context': 200000, 'output': 64000 } }

Verified end-to-end against opencode v1.17.8: the picker now shows
real per-million rates per model.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
@manojp99 manojp99 force-pushed the feat/surface-cost-and-limit branch from 020c6c5 to 3a527c5 Compare June 21, 2026 18:58
@manojp99 manojp99 merged commit c7cefbf into main Jun 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants