-
Notifications
You must be signed in to change notification settings - Fork 6
fix(pi): preserve account model IDs with slashes #471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
117c02e
04662eb
e1da303
245bceb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { mkdtempSync, rmSync } from 'node:fs'; | ||
| import { tmpdir } from 'node:os'; | ||
| import { join } from 'node:path'; | ||
| import type { AgentEvent } from '@linkcode/schema'; | ||
| import { afterEach, describe, expect, it, vi } from 'vitest'; | ||
| import { PiAdapter } from '../native/pi'; | ||
|
|
||
| const roots: string[] = []; | ||
|
|
||
| afterEach(() => { | ||
| vi.unstubAllEnvs(); | ||
| for (const root of roots.splice(0)) rmSync(root, { force: true, recursive: true }); | ||
| }); | ||
|
|
||
| describe('Pi model registry integration', () => { | ||
| it.each([ | ||
| { | ||
| provider: 'anthropic', | ||
| modelId: 'claude-sonnet-4-6', | ||
| inputModel: 'claude-sonnet-4-6', | ||
| baseUrl: 'https://api.anthropic.com', | ||
| }, | ||
| { | ||
| provider: 'openrouter', | ||
| modelId: 'anthropic/claude-sonnet-4.6', | ||
| inputModel: 'anthropic/claude-sonnet-4.6', | ||
| baseUrl: 'https://openrouter.ai/api/v1', | ||
| }, | ||
| { | ||
| provider: 'vercel-ai-gateway', | ||
| modelId: 'anthropic/claude-sonnet-4.6', | ||
| inputModel: 'anthropic/claude-sonnet-4.6', | ||
| baseUrl: 'https://ai-gateway.vercel.sh/v1', | ||
| }, | ||
| { | ||
| provider: 'openrouter', | ||
| modelId: 'anthropic/claude-sonnet-4.6', | ||
| inputModel: 'openrouter/anthropic/claude-sonnet-4.6', | ||
| baseUrl: 'https://openrouter.ai/api/v1', | ||
| }, | ||
| ])('resolves $provider input $inputModel to $modelId', async (testCase) => { | ||
| const { provider, modelId, inputModel, baseUrl } = testCase; | ||
| const root = mkdtempSync(join(tmpdir(), 'pi-model-registry-')); | ||
| roots.push(root); | ||
| vi.stubEnv('PI_CODING_AGENT_DIR', root); | ||
| const adapter = new PiAdapter(); | ||
| const events: AgentEvent[] = []; | ||
| adapter.onEvent((event) => events.push(event)); | ||
|
|
||
| await adapter.start({ | ||
| kind: 'pi', | ||
| cwd: root, | ||
| model: inputModel, | ||
| config: { authToken: 'dummy', baseUrl, knownProvider: provider }, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file drives the installed SDK, so it's the one place the seeded path can actually be proven — the four unit-test mocks stub A fifth case here would cover it end-to-end: A companion case in |
||
| }); | ||
|
|
||
| // An account-bound session reflects the account's own id, unprefixed: the client's picker is | ||
| // built from the account's model list and has no other vocabulary to match against. | ||
| expect(events).toContainEqual({ type: 'model-update', model: modelId }); | ||
| await adapter.stop(); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The carve-out is reasonable in principle, but
pinnedEndpoint()keeps more than URLs a user typed — and the accounts it over-keeps are the ones this PR is trying to fix.A pre-variant Cloudflare account has a filled endpoint (
…/8f3a/prod/compat) written by the old add flow. It never equals the templated variantbaseUrl, sopinnedEndpointreturns it,resolveBindingtakes the earlybind(kind, protocol, baseUrl, knownProvider)branch at line 62 — still four arguments, noproviderEnv— and the adapter falls through to the flatregisterProvider(baseUrl). That pins every model to/compat, which is precisely the400 Compatibility endpoint: v1/messages is not supportedthe reporter filed.__tests__/resolve.test.ts:287pins this shape with acloudflare-gatewayaccount and the comment "the pre-variant behavior, which was never broken for these accounts". That claim was true before this PR; it isn't now — pi bound to such an account still can't reach the anthropic leg.Worth confirming how many real accounts are in that state. The current add flow doesn't write
endpoint(peradd-flow.test.tsx), so this is bounded to accounts created before the variant work — but there's no migration, and the only user-facing recovery is delete-and-re-add, which nothing tells them to do. Options: backfillproviderEnvwhen the pinned URL's origin+params match a templated variant, or narrowpinnedEndpointso a filled-template match against the catalog isn't treated as user-authored.