Replace the Rust provider with a declarative one, and add PKCE sign-in - #2
Merged
Conversation
Co-authored-by: Lifei Zhou <lifei@squareup.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…un (aaif-goose#11673) Signed-off-by: Michael Neale <michael.neale@gmail.com>
Signed-off-by: Michael Neale <michael.neale@gmail.com> Co-authored-by: Galadriel <galadriel@buzz.agent>
Co-authored-by: Alphaxiaoteng <230277249+Alphaxiaoteng@users.noreply.github.com>
Signed-off-by: Jasper Hugo <jasper@spiral.xyz>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#11662) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ose#11661) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…11659) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Jasper Hugo <jasper@spiral.xyz>
…e#11404) Signed-off-by: Jasper Hugo <jasper@spiral.xyz>
Signed-off-by: Jasper Hugo <jasper@spiral.xyz>
Signed-off-by: Jasper Hugo <jasper@spiral.xyz>
Signed-off-by: Jasper Hugo <jasper@spiral.xyz>
Two pieces that stand on their own: - a declarative provider definition (aimlapi.json). 300+ chat models behind one OpenAI-compatible key; the eight seeded ids and their context limits are read from the live catalog, not guessed. It sorts first in the registry only because of the alphabet. - an authorization-code + PKCE sign-in, built on the same shape as signup_openrouter. AI/ML API starts the request server-side, so the CLI POSTs the challenge and its loopback redirect first, then opens the consent screen and exchanges the returned code for a key. Three deliberate differences from the OpenRouter flow it mirrors: - the state is verified. A redirect whose state is not the one we sent did not come from the request we started, so its code is not ours to redeem (RFC 6749 10.12). - the loopback port is 53682, not 3000, which is commonly already taken by a dev server on a developer's machine. - an exchange failure is reported exactly as the server words it. The server does not distinguish an unknown code from a bad verifier from a real expiry, and neither should the client. There is no compiled-in partner id: shipping another integration's id, or a staging-only test id, would silently attribute this traffic to the wrong place. The flow requires AIMLAPI_PARTNER_ID and says so until goose's own production partner exists. Verified end to end against a live environment: request registered, browser consent, redirect caught on the loopback listener, state matched, code exchanged, key issued.
goose now has a registered AI/ML API partner, so the id compiles in and a normal install needs no configuration. The environment variable stays, but only as the testing escape hatch it was meant to be — alongside the two URL overrides — rather than a hard requirement. The previous commit deliberately shipped no default: the id that had been there was another integration's, and sending it would have attributed every goose sign-in to them.
Carried over from the fork's earlier Rust provider, which this definition replaces. X-AIMLAPI-Source and X-AIMLAPI-Partner-ID are what let AI/ML API attribute the traffic to goose; without them a request serves fine and is silently untagged. http-referer / x-title follow what the other aggregator definitions here already send.
Brings the fork up to current upstream and replaces the earlier Rust provider with a declarative definition plus a PKCE sign-in. The old crates/goose/src/providers/aimlapi.rs is removed rather than kept alongside: it registered the same provider name, so the two could not coexist, and it was already broken against current upstream - it built on super::openai_compatible::OpenAiCompatibleProvider, a module upstream has since deleted. Its four hardcoded model ids had also gone stale. What was worth keeping came across: the X-AIMLAPI-Source and X-AIMLAPI-Partner-ID attribution headers now ride on the declarative definition's headers block, so traffic stays tagged. Also drops the AIMLAPI_HOST row from the provider docs - that was the old provider's knob and no longer exists.
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.
Syncs the fork to current upstream and reworks the AI/ML API integration.
Why the existing provider had to go rather than stay alongside
crates/goose/src/providers/aimlapi.rs(from PR #1) registered the provider nameaimlapi. The new declarative definition registers the same name, so the two could not coexist — one of them had to win.Two things decided which:
super::openai_compatible::OpenAiCompatibleProvider, and upstream has since deleted that module. The fork last synced on 2026-08-27; the breakage arrived with upstream changes after that, so it would have surfaced at the next sync regardless of this work.Its four model ids had also gone stale (
google/gemini-3-7-flash,deepseek/deepseek-v4-pro-0813).What was worth keeping came across: the
X-AIMLAPI-SourceandX-AIMLAPI-Partner-IDattribution headers now ride on the definition'sheadersblock. Without them a request serves fine and is silently untagged, which is how partner attribution quietly stops working.What lands
definitions/aimlapi.jsonconfig/signup_aimlapi/signup_openrouterconfigure.rsexamples/aimlapi_auth.rstetrate_authexampleThree deliberate departures from the OpenRouter flow it copies: the
stateis verified (OpenRouter's does not), the loopback port is 53682 rather than the commonly-occupied 3000, and an exchange failure is worded exactly as the server words it — the server does not distinguish an unknown code from a bad verifier from a real expiry, so neither does the client.Verified, and not
Verified against a live environment, through this code rather than curl: authorization request registered, browser consent, redirect caught on the loopback listener, state matched, code exchanged, key issued. Then a real completion through the declarative provider with vendor-prefixed ids —
anthropic/claude-sonnet-5andopenai/gpt-5-nanoboth round-tripped, so the prefix survives goose's wire path.Not verified: the
configure.rsedit does not typecheck here.goose-clicannot build on this machine at all —bindgenneeds libclang, which is absent, and it fails before reaching any of this code.gooseandgoose-providersboth check clean. The CLI edit is a menu item plus a handler modelled directly on the OpenRouter one next to it, but it is unchecked, and CI should be the one to confirm it.Before the upstream PR
signup_openrouter/tests.rs