Repository navigation
AI provider contract: community providers, any model, capped routes and Grida Models facts (0.7.0) - #106
Merged
Conversation
spec/adapters.md is the adapter contract, version 1: where adapters run, the transport, outcomes, refusals before sending, costs, long jobs, downloads and reasons, moved from spec/providers.md unchanged in substance. spec/providers.md keeps keys and endpoints, gains the built-in adapters as a normative table with each contract's adapter and behaviour in exact JSON, and keeps the route table and renamed features. Each adapter's wire details move beside its code, in crates/grida-fx-providers/src/<provider>/README.md. Every reference follows, and check_spec gains (l): every '<spec file> §N' reference names a heading that exists. No behaviour changes.
FX's adapters name their contract as an Identity. Around an adapter that names one, the engine makes the contract check, then the adapter's own judgement of its other contract members, then the request's fit to its capability, inside the attempt before each send or submit; and it runs the capability's every-route checks after the adapter's check of each answer. Adapters keep a guard for direct callers, built on the same functions. No sentence, order, event, record, wire byte or identity changes; stand-ins are unchanged.
The engine fills a route contract's missing adapter and adapter_behavior from the built-in adapter table, keyed by capability and provider, before the fingerprint; no built-in fingerprint moves (a test pins all 29), and a project route declared without a full contract recomputes once. extends copies a route of the same capability and provider (the table's own earlier entries, the catalog so far, or the built-in table, even under --routes), merges contract members one level deep and is never hashed. fal's and Tripo's adapters now check adapter_behavior like every other. digest.py fills and resolves as the engine does, with new route vectors and a route-extends conformance case. jpeg_output, webp_output and hosted_url_reference_input leave the feature vocabulary.
price: {cap_usd: N} stands alone: each call on the route holds the cap and
is charged what the provider reports, else the whole cap. A price that
states no amount is refused instead of meaning $0. The plan notes each
capped route, the graph marks a capped instance capped: true, and the
viewer shows it as up to $N.
A reported cost above a call's hold is charged in full, so the docs no
longer promise that a run never crosses its ceiling: each call is reserved
against it before it is made.
Every paid capability takes an optional provider_options, keyed by provider, and a route's contract.provider_options merges under the step's. An adapter reads only its route's provider's member and sends it after its own members, so with none no byte changes. Members an adapter owns, file values and a non-object are refused before anything leaves; Codex and Grok Build refuse their own. Each adapter's reference lists what it owns. movie-sprite turns off Wan's prompt expansion through provider_options.fal, the parameter fal's published schema names.
The registry keys adapters by capability, provider and the contract's adapter; a route without one takes its provider's default for the capability, and a contract naming an adapter FX does not have is no_route. OpenRouter and fal images get generic default adapters, openrouter-image and fal-image, which send the prompt, the references and the route's provider_options, refuse what they cannot send faithfully, and convert a non-PNG answer to PNG. The built-in GPT Image routes keep their adapters. fal-queue takes its tiers and length from the route and sends the first frame only when given; a route that declares text_to_video draws without one. GG image edits take the route's picture limit, 16 on the built-in route. No built-in fingerprint or previously sent byte moves.
… on OpenRouter tools/make_models.py pins Grida Models v1.20261013.6: it downloads the release's catalogue, verifies it against the release's SHA256SUMS and manifest, validates it against its schema, and writes a projection of the schema-defined facts FX's routes use to routes/models.json; --check verifies the projection offline and runs in CI's spec job. tests/models.rs holds every built-in route to it: wire and endpoint ids, GG's 3D endpoints, features both ways (documented and honoured), picture and size limits, and text prices as bounds over the published token rates, with every exception named and failing once it is no longer needed. OpenRouter's GPT Image routes now declare alpha and send background: transparent under the same adapter_behavior. GG's video route declares text_to_video, which Models documents and the adapter already served. providers.md says GG's planning prices are FX's allowances.
fx.yaml's providers: names a provider's credential (variable, header, scheme; optional, for a keyless local server), its base URL (an optional variable and a default, https unless loopback), the hosts its results may be downloaded from, and its family. A declaration may not take a built-in or runtime name, a variable another declaration or a built-in claims, or a reserved variable (GRIDA_FX_*, proxies, certificates, PATH, HOME). FX's own providers become built-in declarations, so the key loader, the .env reader, the redactor and the environment scrubs (Codex and Grok Build children, the service, the browser opener) read one computed list, with every built-in sentence, doctor line and credential byte unchanged. One download-host helper serves fal and Tripo. doctor lists each declared provider with its key state and base-URL host. A conformance case declares acme and a keyless provider.
A declaration with family: openai-compatible gets FX's
openai-compatible-structured and openai-compatible-tool-loop adapters
(behaviour 1) for structured.generate and agent.turn, over
{base}/chat/completions on the shared chat module: only the OpenAI Chat
Completions reference's members, the default status table and never a
$0 refusal, no cost read (the hold or the cap is charged), token usage
recorded, and no credential for a keyless declaration.
The contract fill extends to a declared family's routes, keyed on the
planning project's declarations, never on a provider name: digest.py,
vectors and a conformance workflow follow, and no undeclared route moves.
llms.txt gains a recipe for a provider FX does not ship.
A declaration may name adapter: <path>#<attribute> in place of a family. Planning loads the adapter on the planning host (adapter.load) for its version and each capability's kind, fills the route contract with the adapter's name and its version or source digest, and locks it in fx.lock. A live run starts one adapter host per declaration on its first call, with only that declaration's variables, and calls adapter.send, adapter.submit and adapter.collect within protocol v1; the adapter may reach the network through the engine's fetch, which attaches the credential only on the provider and upload lanes to the declaration's base-URL host. A lost host is a billed, unretried failure mid-send, Uncertain mid-submit and Unreachable mid-collect; the engine keeps every check and the retry rule. grida.fx and @grida/fx gain an adapter authoring API with outcome types, concurrent calls and fetch. Conformance cases adapter-py and adapter-ts run a synthetic acme adapter live with the network off: an answer, a refusal and a long job.
Guide page 15, Community providers, is the one place a user learns to define their own provider: choosing the path (a route for a new model on a provider FX speaks, a declaration for an OpenAI-compatible server, a declaration and an adapter for anything else), the declaration and its refusals, routes and caps, a complete adapter in Python and TypeScript, outcomes and what each costs, fetch, versions and fx.lock, checking it offline before any paid call, identity and the cache, sharing it, and its limits. Every example was run through the engine. Page 13 keeps new models and provider_options and points to page 15. llms.txt gains a recipe for a model FX does not list, the community provider term and the refusal sentences this branch added; the skill teaches agents both jobs. AGENT_READINESS AR-20 is current in source, with its evidence, and the overview marks decision 15 implemented.
The workspace version and every manifest and lockfile that repeats it move to 0.7.0 (check_versions.py: 25 places agree), with the grida stand-in's pin and llms.txt's Version line and @grida/fx pins. Nothing is tagged or published: a release follows RELEASING.md.
The follow-ups are filed as #91 to #105 and gridaco/models#21, grouped by theme, and M11's boxes are ticked with the verification list's results.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
The latest release, with the same schema byte for byte. It gives the built-in fal video route a card, google/gemini-omni-1.1-flash, whose fal endpoint is the route's id and whose parameters are fal-queue's constants, so CARDS maps it and the test exception for its missing card goes. The references that quote the release follow: fal's image endpoint counts, and Ideogram 4.5's edit, which takes one image_url and so needs a project's own adapter. Of the gaps filed as gridaco/models#21, only that card is filled.
This was referenced Oct 15, 2026
This was referenced Oct 15, 2026
softmarshmallow
added a commit
to gridaco/grida
that referenced
this pull request
Oct 15, 2026
Pins @grida/fx to 0.7.0 (from 0.6.1), so `grida fx` runs FX 0.7.0 (gridaco/fx#106), and bumps grida to 0.5.4 for the release workflow. Host contract unchanged. - packages/grida-cli/package.json: @grida/fx 0.7.0, version 0.5.4 - crates/grida-cli/Cargo.toml, Cargo.lock: 0.5.4; notices regenerated - apps/docs/sources.lock.json: FX guide at gridaco/fx@v0.7.0 - pnpm-lock.yaml, pnpm-workspace.yaml: minimumReleaseAgeExclude names the FX 0.7.0 packages
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #69. Addresses #79 as far as Grida Models' schema allows; the rest continues in #99 and gridaco/models#21.
FX will always be behind new models and providers, so a user can now run any model on a provider FX speaks, and add a provider FX does not ship, without waiting for an FX release. FX's own providers use the same adapter contract users build on. The plan, its decisions (R1–R15) and the record of each milestone are in docs/wg/provider-contract.md. The new guide page Community providers teaches the whole job.
What users can do now
extendsto copy a built-in route,price: {cap_usd: N}when its price is unknownprovider_options, keyed by provider, on a step or a route's contractproviders:withfamily: openai-compatibleadapter(...)fromgrida.fx/@grida/fx)Commits, one per milestone
spec/adapters.md; per-adapter references beside the code;check_spec(l) holds every§Nreference to a real heading. No behaviour changeextends, route vectors; all 29 built-in fingerprints pinned and unchangedprice: {cap_usd: N}; a price with no amount is refusedopenrouter-imageandfal-imageadapters; fal video limits from the routetools/make_models.pypinsv1.20261013.6(verified against the release's SHA256SUMS),tests/models.rs, CIproviders:infx.yaml; FX's own providers become built-in declarationsadapter.load/send/submit/collectandfetchwithin protocol v1; SDK authoring APIs; conformanceadapter-py,adapter-tsllms.txt, the skillWhat changes for existing users
Each is in
llms.txt's upgrade notes andspec/identity.md§7:adapteroradapter_behavior, gets them filled in: its fingerprint moves once (re-run, not resumable). No built-in fingerprint moves.adapternow gets the generic adapter, not GPT Image's request; namingadapter: fx-openrouter-image-v1/fx-fal-image-v1keeps it.price: {}(any price without an amount) is refused instead of meaning $0.adapter_behavior, as every other adapter does.provider_optionsis now sent, under the same fingerprint.no_route/ doctorno adapter.alpha; GG's video route declarestext_to_video.Verification
The whole AGENTS.md list, green on the branch with
GRIDA_FX_NETWORK=off: 2,250 Rust tests in 74 binaries, 1,612 Python tests, 508 TypeScript tests, 66 conformance cases (also throughgrida fxviacheck_host), plus viewer, www, examples, showcase,check_llms,check_spec,make_models.py --checkandcheck_versions.py. No provider was called and nothing was spent: every path is proven over replay fixtures, stand-ins, synthetic adapters and loopback fakes.Follow-ups
Filed, grouped by theme: #91 to #105 here, and gridaco/models#21 for the Models gaps.