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
6 changes: 3 additions & 3 deletions contracts/model-discovery-filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

Issue #456. Plan: [456-model-discovery-filters](../docs/plans/456-model-discovery-filters.md).

GET /api/v1/models accepts optional singleton output_modalities, supported_parameters and context alongside offset/limit and the [input/search/order extension](model-discovery-exploration.md). Output modality is one of text/image/embeddings/audio/video/rerank/decisions/speech/transcription, or all. Supported parameter is one exact lower_snake_case identifier of 1..128 characters. Context is a canonical positive decimal safe integer. Duplicate keys, comma lists, blanks, unknown fields, casing/whitespace variants and invalid bounds reject safely before catalog reads. These are local syntax restrictions, not bounds invented in the source schema. /v1 continues rejecting every nonempty query.
GET /api/v1/models accepts optional output_modalities lists, singleton supported_parameters and context alongside offset/limit and the [input/search/order extension](model-discovery-exploration.md). Output modalities are one to nine distinct values from text/image/embeddings/audio/video/rerank/decisions/speech/transcription, matching any, or standalone all; see [output list contract](model-output-filters.md). Supported parameter is one exact lower_snake_case identifier of 1..128 characters. Context is a canonical positive decimal safe integer. Duplicate keys/items, unsupported input/parameter comma lists, blanks, unknown fields, casing/whitespace variants and invalid bounds reject safely before catalog reads. These are local syntax restrictions, not bounds invented in the source schema. /v1 continues rejecting every nonempty query.

No filter is implicit. Filter-only requests return all matching aliases without a 500-item page default. Supplying offset or limit uses existing offset=0/limit=500 defaults and limit1..1000. The complete current catalog is validated first, including disabled/denied/out-of-page records. Only enabled aliases passing model AND final-provider IAM are eligible; then all supplied metadata predicates must hold, then optional stable discovery ordering precedes paging; omitted sort retains catalog order.

Use immutable administrator-published metadata snapshots: exact output/parameter array membership, and known context_length >= context. Missing metadata fails asserted conditions; null context_length fails a context condition. output_modalities=all imposes no modality condition and retains basic aliases if no other condition excludes them. Conjunction is a documented local subset; upstream multi-filter combination behavior is unspecified. Do not infer capabilities from aliases, route kind, top-provider limits, defaults or string modality labels. Model-level parameter metadata may be a union across providers, so a match cannot establish support on an authorized or selected endpoint.
Use immutable administrator-published metadata snapshots: any-member output and exact singleton parameter array membership, and known context_length >= context. Missing metadata fails asserted conditions; null context_length fails a context condition. output_modalities=all imposes no modality condition and retains basic aliases if no other condition excludes them. Conjunction is a documented local subset; upstream multi-filter combination behavior is unspecified. Do not infer capabilities from aliases, route kind, top-provider limits, defaults or string modality labels. Model-level parameter metadata may be a union across providers, so a match cannot establish support on an authorized or selected endpoint.

total_count counts only currently authorized matching aliases. Next links have fixed relative /api/v1/models destination and validated offset/limit plus every accepted filter/search/order field. No incoming host, hidden model metadata or unknown filter can influence the link. Full lists, exhausted/empty pages and beyond-end offsets have next=null. Each continuation authenticates, rereads current catalog and reevaluates IAM; changes can shift offsets without snapshot/cursor guarantees.

Required models-listed audit records only returned count and established attribution before output. Invalid queries and catalog/audit failure have sanitized existing errors/events; neither filter values nor private metadata enters operational records. Listing invokes no inference routes, provider secrets, limit or usage ports. Informational metadata does not grant invocation rights or certify live capability/prices. Installed OpenRouter 1.4.18 socket tests verify scalar query serialization, filtered pagination and fresh denial; OpenAI 7.23.0 sockets can consume the standard list envelope through the same filtered URL.

The version-30 chat pin stays unchanged. Separate model-query pin version2 structurally tracks the eight implemented query objects. Bounded input/search/order behavior is defined in its extension contract; multi-values/category/other sorts/provider/region filters, metadata refresh/provisioning, broader external-client workflows, #116 and unresolved #7 remain open.
The version-31 chat pin stays unchanged by the output-list extension. Separate model-query pin version2 structurally tracks the eight implemented query objects. Bounded input/search/order behavior is defined in its extension contract; input/parameter multi-values/category/other sorts/provider/region filters, metadata refresh/provisioning, broader external-client workflows, #116 and unresolved #7 remain open.

Sources: [official models reference](https://openrouter.ai/docs/api/api-reference/models/get-models), [official schema](https://openrouter.ai/openapi.json).
15 changes: 15 additions & 0 deletions contracts/model-output-filters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Authorized output modality lists

Issue #472. Plan: [472-model-output-filters](../docs/plans/472-model-output-filters.md).

GET /api/v1/models accepts output_modalities as one comma-separated string containing one to nine distinct values from text/image/embeddings/audio/video/rerank/decisions/speech/transcription. A model matches if its captured administrator-published output_modalities contains any requested value. all remains a standalone sentinel imposing no output condition; mixing it with a modality rejects. Omission retains the complete current authorized list without an implicit text default. Missing/empty metadata cannot satisfy an explicit list.

Empty items, duplicate items, unknown values, casing/whitespace variants and repeated query keys reject400 before catalog reads. This strict syntax is a local subset: the source schema is a plain string, and the upstream accepts duplicates. Input modalities and supported parameters remain singleton. Different supplied fields still combine conjunctively, followed by established ordering and optional paging.

Validate the entire catalog, then enabled model AND final-provider IAM, before any-member filtering. Denied/disabled aliases never affect matching totals or offsets. Fixed relative continuation links retain every accepted field and the output list in its original validated order as one URL-encoded scalar. Each page authenticates and reevaluates current catalog/IAM without snapshot guarantees. Filter-only lists retain full-list behavior above500; paging bounds/defaults remain unchanged. /v1 still rejects query strings.

Required sanitized audit precedes output. Listing calls no route, inference, provider-secret, limit or usage ports. Errors/events exclude filters and private metadata; whole-catalog, audit failure and immutable metadata guarantees remain shared. A metadata match grants no invocation permission or selected-provider capability guarantee.

Installed OpenRouter1.4.18 and OpenAI7.23.0 socket cases exercise union selection, retained pagination and fresh Deny. All three source pins remain unchanged. Input/parameter lists, other discovery filters, metadata refresh, full #116 and unresolved #7 remain open.

Sources checked2026-10-06: [official models guide](https://openrouter.ai/docs/guides/overview/models), [official models reference](https://openrouter.ai/docs/api/api-reference/models/get-models). Union semantics are supported by the guide example and credential-free official response observations containing both text-only and image-only models; exact implementation and local syntax bounds are not encoded in the structural schema.
8 changes: 7 additions & 1 deletion docs/PRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,7 @@ Version 30 adds only the raw nullable service_tier enum and unknown-value extens

## Authorized model discovery filters (#456)

GET /api/v1/models supports bounded singleton output_modalities (one documented modality or all), supported_parameters (one exact lower_snake_case token, at most 128 characters), and context (canonical positive safe integer minimum). Omitted filters preserve the current list without an implicit text default. Multi-values and undocumented normalization/combination behavior remain unimplemented; conjunction is an explicit local subset. Filter-only requests retain full-list behavior; offset/limit keep existing defaults and bounds. /v1 still rejects queries.
GET /api/v1/models supports bounded output_modalities (one to nine distinct documented modalities, matching any, or standalone all; extended by #472), supported_parameters (one exact lower_snake_case token, at most 128 characters), and context (canonical positive safe integer minimum). Omitted filters preserve the current list without an implicit text default. Input/parameter multi-values and undocumented normalization remain unimplemented; conjunction across different filters is an explicit local subset. Output list union is defined in the [output contract](../contracts/model-output-filters.md). Filter-only requests retain full-list behavior; offset/limit keep existing defaults and bounds. /v1 still rejects queries.

Validate the whole catalog and fresh enabled model/final-provider IAM before applying every asserted condition to frozen administrator metadata and then paging in catalog order. Missing metadata/null context cannot establish a filtered capability; output all imposes no modality condition. total_count and fixed relative continuation links describe only authorized matching aliases and preserve all accepted filters. Required audit precedes delivery; filters/metadata stay out of operational events/errors. Listing calls no secrets, limits, inference routes or usage ports, and cannot grant routing authority. Metadata may describe a model-level union across providers; it does not certify feature support on the selected authorized endpoint.

Expand Down Expand Up @@ -973,3 +973,9 @@ All three source pins remain byte-identical. Jev text disclosure still rejects i
Measure OpenCode 1.18.5 local PNG attachments through delegated OpenRouter and managed OpenAI/Anthropic/Gemini on both bases. Use an explicitly image-capable fixture alias, omitted detail and exact inline/native bytes; require rendered text and image-preserving actual read-function/result continuation. Verify initial model/provider Deny, fresh result-follow-up provider Deny, disconnect accounting and private metadata. Keep isolated temporary configuration, fixed mocked hosts and bounded children. This expands named-client conformance and fixes advisory session-header scope; runtime image policy and unsupported native body session_id remain unchanged. It does not certify live models, remote images, general image capability or complete #116. See [contract](../contracts/opencode-inline-images.md).

OpenCode automatically sends X-Session-Id. Validate and capture bounded selected headers before awaits, but turn header-only identifiers into session_id only for delegated OpenRouter routes after resolving the approved route. Managed calls omit header-derived identifiers, while explicit native body session_id still rejects before secrets. This header never controls authentication, IAM, route choice, limits or accounting identity.

## Authorized output modality lists (#472)

GET /api/v1/models accepts one comma-separated output_modalities value with one to nine distinct exact modalities. Select any matching captured published output after enabled model AND final-provider IAM; missing/empty metadata cannot establish a match. Keep all standalone and omitted-filter behavior without an implicit text default. Reject empty/duplicate/unknown/case/whitespace items, mixed all and repeated query keys before catalog reads. Duplicate rejection and finite vocabulary bounds are explicit local restrictions.

Retain conjunction with other fields, search/order/paging, full-list behavior above500, and fixed relative continuation links preserving validated list order. Each page reevaluates current catalog/IAM. Whole-catalog validation, required private audit, metadata capture, safe dependency errors and no inference/secret/limit/usage calls remain enforced. Installed OpenRouter1.4.18/OpenAI7.23.0 socket tests cover union, pagination and fresh Deny. All three source pins remain byte-identical; metadata freshness, input/parameter lists, full #116 and unresolved #7 remain open. See [plan](plans/472-model-output-filters.md) and [contract](../contracts/model-output-filters.md).
8 changes: 7 additions & 1 deletion docs/acceptance.md
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,7 @@ Version 30 adds only the raw nullable service_tier enum and unknown-value extens

## Authorized model discovery filters (#456)

GET /api/v1/models supports bounded singleton output_modalities (one documented modality or all), supported_parameters (one exact lower_snake_case token, at most 128 characters), and context (canonical positive safe integer minimum). Omitted filters preserve the current list without an implicit text default. Multi-values and undocumented normalization/combination behavior remain unimplemented; conjunction is an explicit local subset. Filter-only requests retain full-list behavior; offset/limit keep existing defaults and bounds. /v1 still rejects queries.
GET /api/v1/models supports bounded output_modalities (one to nine distinct documented modalities, matching any, or standalone all; extended by #472), supported_parameters (one exact lower_snake_case token, at most 128 characters), and context (canonical positive safe integer minimum). Omitted filters preserve the current list without an implicit text default. Input/parameter multi-values and undocumented normalization remain unimplemented; conjunction across different filters is an explicit local subset. Output list union is defined in the [output contract](../contracts/model-output-filters.md). Filter-only requests retain full-list behavior; offset/limit keep existing defaults and bounds. /v1 still rejects queries.

Validate the whole catalog and fresh enabled model/final-provider IAM before applying every asserted condition to frozen administrator metadata and then paging in catalog order. Missing metadata/null context cannot establish a filtered capability; output all imposes no modality condition. total_count and fixed relative continuation links describe only authorized matching aliases and preserve all accepted filters. Required audit precedes delivery; filters/metadata stay out of operational events/errors. Listing calls no secrets, limits, inference routes or usage ports, and cannot grant routing authority. Metadata may describe a model-level union across providers; it does not certify feature support on the selected authorized endpoint.

Expand Down Expand Up @@ -1378,3 +1378,9 @@ All three source pins remain byte-identical. Jev text disclosure still rejects i
- Default CI verifies socket/config/assertion cases; the explicit installed-client gate adds 48 image probes to the existing fifty. No live inference, automatic discovery, signed-image combinations or complete #116 certification is inferred; #7 remains unresolved.

- A bounded header-only X-Session-Id request succeeds natively without a session_id body field; explicit native body identifiers still reject before secrets, including with an invalid unselected header. Selected overlong headers reject before route lookup. Delegated body/header precedence and captured values survive asynchronous mutation. Header-only native success, auth/IAM/limit denial, missing usage, upstream failure and required persistence failures preserve their existing controls.

## Authorized output modality lists (#472)

GET /api/v1/models accepts one comma-separated output_modalities value with one to nine distinct exact modalities. Select any matching captured published output after enabled model AND final-provider IAM; missing/empty metadata cannot establish a match. Keep all standalone and omitted-filter behavior without an implicit text default. Reject empty/duplicate/unknown/case/whitespace items, mixed all and repeated query keys before catalog reads. Duplicate rejection and finite vocabulary bounds are explicit local restrictions.

Retain conjunction with other fields, search/order/paging, full-list behavior above500, and fixed relative continuation links preserving validated list order. Each page reevaluates current catalog/IAM. Whole-catalog validation, required private audit, metadata capture, safe dependency errors and no inference/secret/limit/usage calls remain enforced. Installed OpenRouter1.4.18/OpenAI7.23.0 socket tests cover union, pagination and fresh Deny. All three source pins remain byte-identical; metadata freshness, input/parameter lists, full #116 and unresolved #7 remain open. See [plan](plans/472-model-output-filters.md) and [contract](../contracts/model-output-filters.md).
Loading
Loading