2026-11-03 15:24:06 +00:00
|
|
|
# Bundled OpenRouter Implementation Plan
|
|
|
|
|
|
|
|
|
|
Implement [bundled-openrouter.md](bundled-openrouter.md) in the sequential stages
|
|
|
|
|
below. Each stage is one Luna-sized assignment. The feature roadmap owns scope
|
|
|
|
|
and consumer policy; this document owns the implementation sequence and choices.
|
|
|
|
|
|
|
|
|
|
## Baseline And Working Rules
|
|
|
|
|
|
|
|
|
|
The baseline is `9e05c3a`: Go 1.27.0, ConfigKit v1.1.0, recorded field
|
|
|
|
|
normalization, and no installed backend/profile catalog. The facade in
|
|
|
|
|
`engine.go` applies options; `internal/engine.New` passes caller configuration
|
|
|
|
|
to `internal/configuration.Load` before assembling execution collaborators.
|
|
|
|
|
|
|
|
|
|
Read `docs/development.md` and applicable agent, architecture, testing,
|
|
|
|
|
documentation, OSS, and release policies. Follow
|
|
|
|
|
[ADR 0001](../adr/0001-configuration-boundary.md). Reuse current source-loading,
|
|
|
|
|
generation, and smoke-diagnostic contracts. Preserve unrelated changes.
|
|
|
|
|
|
|
|
|
|
This is one end-to-end sprint: intermediate stages need not compile or form a
|
|
|
|
|
deployable state. Do not introduce compatibility scaffolding or intermediate
|
|
|
|
|
human-review/publication gates. Record incomplete checks honestly and reuse
|
|
|
|
|
successful checks for unchanged content. Do not publish a release or make paid
|
|
|
|
|
provider requests. No dependency upgrade, configuration-version change, or
|
|
|
|
|
provider-specific catalog module is required.
|
|
|
|
|
|
|
|
|
|
## Implementation Choices
|
|
|
|
|
|
|
|
|
|
- Add `internal/configuration/bundled/openrouter.yaml` and embed it from a small
|
|
|
|
|
`bundled.go` in that package. Provide an internal exported
|
|
|
|
|
`BundledOpenRouterUnderlay() configkit.Underlay` for engine assembly. It returns
|
|
|
|
|
one input created through `configkit.NewFilesystem` and its `File` method,
|
|
|
|
|
with a meaningful label such as `Promptkit bundled OpenRouter`.
|
|
|
|
|
- Use the existing source loader once. Do not independently parse, validate,
|
|
|
|
|
normalize, or cache a separate bundle registry before final composition.
|
|
|
|
|
Tests inspect the assembled effective configuration, not raw-source validity.
|
|
|
|
|
- Prepend the bundle as its own underlay in `internal/engine.New`. Allocate a
|
|
|
|
|
new outer slice and copy caller groups so spare slice capacity cannot let
|
|
|
|
|
assembly mutate caller-owned input. Preserve every caller group's contents,
|
|
|
|
|
order, main input, imports, and source context.
|
|
|
|
|
- Add `WithBundledOpenRouter(enabled bool) Option` in the facade. Add
|
|
|
|
|
`disableBundledOpenRouter bool` to internal engine options and a setter
|
|
|
|
|
`SetBundledOpenRouter(bool)` following existing setter style. The zero internal
|
|
|
|
|
options value enables the bundle; the setter stores `!enabled`. Apply options
|
|
|
|
|
in the existing order, so the last supplied bundle option wins. The facade
|
|
|
|
|
only delegates; embedding and composition stay internal.
|
|
|
|
|
- Disabled means no bundle source or definitions are installed. It does not
|
|
|
|
|
reserve names, remove caller definitions, or disable the OpenAI-compatible
|
|
|
|
|
client. Scoped views share the engine's captured configuration; they do not
|
|
|
|
|
reload the bundle.
|
|
|
|
|
- Keep the root backend and profiles described below. No built-in collection,
|
|
|
|
|
prompts, implicit execution profile, tuning defaults, catalog download, or
|
|
|
|
|
constructor-time provider/credential access is added.
|
|
|
|
|
|
|
|
|
|
## Exact Data To Install
|
|
|
|
|
|
|
|
|
|
The root backend is `openrouter`, with endpoint `https://openrouter.ai/api/v1`,
|
|
|
|
|
`api_key_env: OPENROUTER_API_KEY`, and:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
smoke_tests:
|
|
|
|
|
default_profile: gpt-luna-latest
|
|
|
|
|
authentication:
|
|
|
|
|
kind: openrouter_key
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Leave concurrency, queue, provider parameters, and diagnostic endpoint overrides
|
|
|
|
|
unset. Existing reachability and authentication endpoint fallback applies.
|
|
|
|
|
|
|
|
|
|
Use these exact root profile IDs and model values. Each non-cost-tier profile
|
|
|
|
|
sets `backend: openrouter`. The ten family mappings were checked against the
|
|
|
|
|
linked provider pages on 2026-11-03; this is authoring evidence, not a guarantee
|
|
|
|
|
of future availability or a recurring release check.
|
|
|
|
|
|
|
|
|
|
| Profile ID | Model | Provider reference |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| `gpt-astra-latest` | `~openai/gpt-astra-latest` | [Alias](https://openrouter.ai/~openai/gpt-astra-latest) |
|
|
|
|
|
| `gpt-sol-latest` | `~openai/gpt-sol-latest` | [Alias](https://openrouter.ai/~openai/gpt-sol-latest) |
|
|
|
|
|
| `gpt-terra-latest` | `~openai/gpt-terra-latest` | [Alias](https://openrouter.ai/~openai/gpt-terra-latest) |
|
|
|
|
|
| `gpt-luna-latest` | `~openai/gpt-luna-latest` | [Alias](https://openrouter.ai/~openai/gpt-luna-latest) |
|
|
|
|
|
| `claude-fable-latest` | `~anthropic/claude-fable-latest` | [Alias](https://openrouter.ai/~anthropic/claude-fable-latest) |
|
|
|
|
|
| `claude-opus-latest` | `~anthropic/claude-opus-latest` | [Alias](https://openrouter.ai/~anthropic/claude-opus-latest) |
|
|
|
|
|
| `claude-sonnet-latest` | `~anthropic/claude-sonnet-latest` | [Alias](https://openrouter.ai/~anthropic/claude-sonnet-latest) |
|
|
|
|
|
| `claude-haiku-latest` | `~anthropic/claude-haiku-latest` | [Alias](https://openrouter.ai/~anthropic/claude-haiku-latest) |
|
|
|
|
|
| `gemini-pro-latest` | `~google/gemini-pro-latest` | [Alias](https://openrouter.ai/~google/gemini-pro-latest) |
|
|
|
|
|
| `gemini-flash-latest` | `~google/gemini-flash-latest` | [Alias](https://openrouter.ai/~google/gemini-flash-latest) |
|
|
|
|
|
| `auto` | `openrouter/auto` | [Router](https://openrouter.ai/docs/guides/routing/routers/auto-router) |
|
|
|
|
|
| `free` | `openrouter/free` | [Router](https://openrouter.ai/docs/guides/routing/routers/free-router) |
|
|
|
|
|
|
|
|
|
|
The five remaining profiles are `auto-low`, `auto-medium`, `auto-high`,
|
|
|
|
|
`auto-xhigh`, and `auto-max`. Each uses `extends: {id: auto}` and only adds
|
|
|
|
|
`extra_params.plugins: [{id: auto-router, cost_tier: TIER}]`, where TIER is its
|
|
|
|
|
suffix. Do not duplicate backend/model in these children: they inherit the
|
|
|
|
|
effective parent, including an application replacement. Base `auto` sets no
|
|
|
|
|
cost tier. OpenRouter documents these five values and plugin spelling in its
|
|
|
|
|
[cost-tier contract](https://openrouter.ai/docs/guides/routing/routers/auto-router#cost-tier).
|
|
|
|
|
The [current-key endpoint](https://openrouter.ai/docs/api/api-reference/api-keys/get-current-api-key)
|
|
|
|
|
documents the existing authentication adapter's destination.
|
|
|
|
|
|
|
|
|
|
Preserve `~` and `/` in model values; neither makes the profile namespaced.
|
|
|
|
|
Do not add sampling, reasoning, timeout, token, or capability settings. If
|
|
|
|
|
contradictory provider evidence appears, report it rather than silently selecting
|
|
|
|
|
a fixed model or dropping a requested profile. A credentialed generation probe
|
|
|
|
|
is not needed to implement this data-backed plan.
|
|
|
|
|
|
|
|
|
|
## Stage 1: Add The Embedded Configuration Source
|
|
|
|
|
|
|
|
|
|
Scope: new YAML and small helper in `internal/configuration`.
|
|
|
|
|
|
|
|
|
|
1. Define one root backend and seventeen root profiles exactly as above. Use
|
|
|
|
|
ordinary identified lists and ConfigKit sibling inheritance for the five
|
|
|
|
|
cost-tier children. No collection wrapper or prompt definitions are needed.
|
|
|
|
|
2. Embed the file with `embed.FS`; expose only the internal underlay helper.
|
|
|
|
|
Use a fresh public ConfigKit source/context for loading, without mutable
|
|
|
|
|
package-level decoded data or filesystem discovery.
|
|
|
|
|
3. Add a focused loader-level test composing that underlay through `Load`.
|
|
|
|
|
Check the effective backend, the selected profile/model mapping, cost-tier
|
|
|
|
|
inheritance, smoke metadata, and meaningful source evidence. These assertions
|
|
|
|
|
protect the chosen catalog contents, not upstream model availability.
|
|
|
|
|
4. Do not create raw-YAML validation or live-provider tests. No public catalog
|
|
|
|
|
export, custom parser, catalog dependency, or parallel registry is needed.
|
|
|
|
|
|
|
|
|
|
Validation: `go test ./internal/configuration` after the helper is wired.
|
|
|
|
|
|
2026-11-03 15:27:26 +00:00
|
|
|
Completion: the embedded OpenRouter underlay and loader-level catalog test are
|
|
|
|
|
in place; the focused configuration suite passed with Go 1.27.0.
|
|
|
|
|
|
2026-11-03 15:24:06 +00:00
|
|
|
## Stage 2: Enable The Bundle Through Engine Construction
|
|
|
|
|
|
|
|
|
|
Scope: facade/internal options, assembly, and default/disabled contract tests.
|
|
|
|
|
|
|
|
|
|
1. Implement the option and default-on internal flag as specified. Prepend the
|
|
|
|
|
underlay only when enabled, using a new outer slice. Keep schema/client/
|
|
|
|
|
artifact options and main-source handling unchanged.
|
|
|
|
|
2. Add external-package facade tests showing `NewEngine(Config{})` exposes the
|
|
|
|
|
root backend and all seventeen profiles, but creates no prompts or synthetic
|
|
|
|
|
`openrouter` collection. Exercise representative `InspectProfile` values and
|
|
|
|
|
`InspectConfiguration` source explanations.
|
|
|
|
|
3. Test false disables installation, true retains it, and option ordering follows
|
|
|
|
|
ordinary last-option behavior. With false, caller-owned `openrouter` records
|
|
|
|
|
remain available. Construction succeeds with the API-key variable unset and
|
|
|
|
|
an HTTP transport that rejects unexpected calls.
|
|
|
|
|
4. Verify assembly leaves caller underlay slice/backing-array contents unchanged.
|
|
|
|
|
5. Adapt preexisting tests whose intended fixture is an application-only or empty
|
|
|
|
|
catalog by explicitly disabling the bundle in their local helpers. Update
|
|
|
|
|
default-inventory expectations where that is the behavior under test. Do not
|
|
|
|
|
globally hide default-on behavior or weaken not-found/validation assertions.
|
|
|
|
|
Internal direct configuration-loader tests do not need to install the bundle.
|
|
|
|
|
|
|
|
|
|
Validation: focused construction/listing/inspection tests, then
|
|
|
|
|
`go test ./internal/engine ./...` when intermediate wiring is coherent.
|
|
|
|
|
|
2026-11-03 15:30:47 +00:00
|
|
|
Completion: engine construction installs the bundle by default, with an
|
|
|
|
|
ordered opt-out option and a copied underlay slice. Facade contracts and the
|
|
|
|
|
full Go suite pass with Go 1.27.0.
|
|
|
|
|
|
2026-11-03 15:24:06 +00:00
|
|
|
## Stage 3: Protect Composition And Application Control
|
|
|
|
|
|
|
|
|
|
Scope: focused real-loader integration tests through the facade.
|
|
|
|
|
|
|
|
|
|
1. Verify a caller underlay and then the main input can replace a bundled ID in
|
|
|
|
|
order, and caller strict groups remain strict. Demonstrate complete record
|
|
|
|
|
replacement: a model-only replacement does not recover the old backend.
|
|
|
|
|
Preserve partial-profile support rather than demanding construction readiness.
|
|
|
|
|
2. Verify a distinct profile extending a bundled profile retains inherited
|
|
|
|
|
fields and applies selected overrides. Add an application-provided OpenRouter
|
|
|
|
|
collection with qualified references; root and collection profiles coexist.
|
|
|
|
|
3. Replace root backend `openrouter` with a complete local endpoint definition.
|
|
|
|
|
Confirm surviving bundled profiles use the replacement backend's settings.
|
|
|
|
|
No reserved-ID checks or special merge semantics are added.
|
|
|
|
|
4. Replace root `auto` with a profile selecting a different caller backend and
|
|
|
|
|
model. All five cost-tier children must inherit that target and retain their
|
|
|
|
|
plugin entries. Do not reject this customization or strip plugin settings.
|
|
|
|
|
5. Exercise explicit sequence modes using ConfigKit's existing controls:
|
|
|
|
|
`config.sequences: [{destination: [profiles], mode: append}]` with a duplicate
|
|
|
|
|
bundled ID fails normally; `mode: replace` with an application profile list
|
|
|
|
|
replaces all lower profiles. Include a nonconflicting append case. Keep
|
|
|
|
|
backend/profile references valid where testing successful replacement.
|
|
|
|
|
6. Show that clearing/replacing backend definitions without maintaining surviving
|
|
|
|
|
profile references follows existing validation, not a hidden fallback.
|
|
|
|
|
Disabling the bundle remains the simple application-only configuration path.
|
|
|
|
|
|
|
|
|
|
Validation: targeted new composition tests plus existing source, inheritance,
|
|
|
|
|
namespace, and profile-completion tests. Avoid reproducing ConfigKit's generic
|
|
|
|
|
sequence implementation suite.
|
|
|
|
|
|
2026-11-03 15:32:25 +00:00
|
|
|
Completion: facade integration tests cover caller precedence, whole-record
|
|
|
|
|
replacement, inheritance, scoped resources, sequence modes, strict conflicts,
|
|
|
|
|
and missing backend rejection. Targeted tests pass on Go 1.27.0.
|
|
|
|
|
|
2026-11-03 15:24:06 +00:00
|
|
|
## Stage 4: Verify Preparation And HTTP Request Construction
|
|
|
|
|
|
|
|
|
|
Scope: bundled-profile execution integration with existing runner/client paths.
|
|
|
|
|
|
|
|
|
|
1. With a minimal caller prompt and an injected deterministic client, prepare
|
|
|
|
|
and run using an explicit bundled profile. Check selected profile/backend,
|
|
|
|
|
model slug, and ordinary defaults/overrides. A prompt with no default and
|
|
|
|
|
no request profile must still produce the existing profile-required error.
|
|
|
|
|
2. For every cost tier, use the built-in HTTP client and an offline recording
|
|
|
|
|
RoundTripper or local server to inspect the outbound JSON. Assert the model
|
|
|
|
|
is `openrouter/auto` and `plugins` is a top-level array with the matching
|
|
|
|
|
`auto-router`/`cost_tier` entry, not nested under an `extra_params` member.
|
|
|
|
|
3. Verify base `auto` sends no explicit cost-tier plugin and `free` preserves
|
|
|
|
|
`openrouter/free`. Check at least one `~`-prefixed model reaches the request
|
|
|
|
|
unchanged. Return a minimal valid synthetic chat response.
|
|
|
|
|
4. Reuse existing runtime credential tests and override tests. Add only missing
|
|
|
|
|
bundle integration assertions, such as selecting `OPENROUTER_API_KEY` from
|
|
|
|
|
the installed backend. No real credentials, calls, provider capability
|
|
|
|
|
assumptions, hardcoded prices, or changes to optional-key behavior are needed.
|
|
|
|
|
|
|
|
|
|
Validation: new request cases and existing generation/profile/override tests.
|
|
|
|
|
Offline HTTP assertions prove payload construction, not provider acceptance.
|
|
|
|
|
|
|
|
|
|
## Stage 5: Verify Bundled Smoke-Test Defaults
|
|
|
|
|
|
|
|
|
|
Scope: facade-level smoke integration with a synthetic HTTP boundary.
|
|
|
|
|
|
|
|
|
|
1. On a default engine, explicitly request reachability with
|
|
|
|
|
`BackendID: openrouter`. Verify the existing backend-endpoint fallback.
|
|
|
|
|
Authentication should select the `openrouter_key` adapter and
|
|
|
|
|
`https://openrouter.ai/api/v1/key`, using an isolated synthetic key and a
|
|
|
|
|
recording transport. Return the existing adapter's minimal success fixture.
|
|
|
|
|
2. For text and structured inference, specify the backend without prompt/profile.
|
|
|
|
|
Assert fallback to `gpt-luna-latest` and its exact model slug. Return suitable
|
|
|
|
|
private-case responses using the existing smoke-test fixtures. Preserve
|
|
|
|
|
factual reports and nil-error concluded-failure semantics.
|
|
|
|
|
3. Show explicit profile selection wins over prompt default and backend metadata,
|
|
|
|
|
and a prompt default wins over backend metadata when no profile is supplied.
|
|
|
|
|
Smoke tests must still use their private cases, not execute caller prompt
|
|
|
|
|
content or output contracts.
|
|
|
|
|
4. Replace `gpt-luna-latest` through application configuration and verify fallback
|
|
|
|
|
uses the effective replacement. Replace backend smoke metadata to select a
|
|
|
|
|
different caller profile and confirm the ordinary selection path follows it.
|
|
|
|
|
5. Verify listing/inspection are not modified by diagnostics. Keep the existing
|
|
|
|
|
rule that diagnostics run only on explicit invocation; no new constructor,
|
|
|
|
|
ordinary-run, CI, or release probe is introduced.
|
|
|
|
|
|
|
|
|
|
Validation: targeted bundled smoke tests and existing smoke contract/integration
|
|
|
|
|
tests. Reuse existing endpoint/auth fixtures instead of adding an adapter or
|
|
|
|
|
duplicating the full diagnostic suite.
|
|
|
|
|
|
|
|
|
|
## Stage 6: Update Documentation And Verify The Sprint
|
|
|
|
|
|
|
|
|
|
Scope: public GoDoc, current guidance/examples, and end-to-end verification.
|
|
|
|
|
|
|
|
|
|
1. Update `ConfigurationSources`, `Config`/`NewEngine`, and new-option GoDoc to
|
|
|
|
|
describe the dedicated lower underlay, default-on behavior, disabling, and
|
|
|
|
|
changed inventories. Keep exact option semantics in GoDoc.
|
|
|
|
|
2. Update `docs/formats.md` and `docs/consumers/pkg-promptkit.md` with the bundle,
|
|
|
|
|
root layout, explicit selection, optional credentials, smoke fallback, and
|
|
|
|
|
disabling/whole-record replacement/inherited-variant examples. Make one
|
|
|
|
|
canonical bundle inventory in the format reference; link rather than repeat
|
|
|
|
|
it throughout guidance. Link provider docs for alias/router behavior.
|
|
|
|
|
3. Remove stale claims of no installed catalogs from current migration guidance
|
|
|
|
|
and internal source/assembly documentation. Explain that an external
|
|
|
|
|
collection is still supported, and replacing `auto` changes inherited child
|
|
|
|
|
targets while retaining plugins. Do not add a rule that external catalogs
|
|
|
|
|
must be namespaced or upstream aliases must have fixed capabilities.
|
|
|
|
|
4. Keep the three maintained examples offline. Where their caller-only inventories
|
|
|
|
|
are intentional, pass `WithBundledOpenRouter(false)` and explain that choice;
|
|
|
|
|
retain useful fixture assertions rather than expanding them with unrelated
|
|
|
|
|
default inventory. Add a short consumer-guide bundled-profile example instead
|
|
|
|
|
of another live example program. Recheck README quickstart claims.
|
|
|
|
|
5. Add the feature and compatibility effects to the currently untagged
|
|
|
|
|
`docs/releases/v0.18.0.md`, including how to retain application-only behavior.
|
|
|
|
|
Explain that previously unresolved references matching bundled IDs can now
|
|
|
|
|
resolve, alongside the changed default inventories and inspection sources.
|
|
|
|
|
If that release has been published by implementation time, use the next
|
|
|
|
|
unreleased minor note instead; preserve published notes/tags. Follow the
|
|
|
|
|
release procedure for the note/footer. No release operation is part of this
|
|
|
|
|
sprint.
|
|
|
|
|
6. Format changes, prepare dependency caches, and run with the compatible local
|
|
|
|
|
Go toolchain:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
go test -race -vet=off ./...
|
|
|
|
|
go vet ./...
|
|
|
|
|
go run ./examples/go-library/prepare
|
|
|
|
|
go run ./examples/go-library/run
|
|
|
|
|
go run ./examples/go-library/observe
|
|
|
|
|
git diff --check
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
7. Review the complete diff for unexpected API changes, caller mutation, new
|
|
|
|
|
source/history validation, implicit selection, network access, credential
|
|
|
|
|
values, and stale links. Confirm only intended documentation and inventories
|
|
|
|
|
changed. Follow the focused commit policy and record completion in the
|
|
|
|
|
feature roadmap; retire completed plans during release preparation.
|
|
|
|
|
|
|
|
|
|
Completion requires default availability, full disabling, ordinary caller
|
|
|
|
|
composition and provenance, correct cost-tier payloads and explicit diagnostic
|
|
|
|
|
fallbacks, and current documentation. There are no unresolved product choices,
|
|
|
|
|
external publication prerequisites, or new recurring release gates.
|