mirror of
https://github.com/vercel/eve.git
synced 2026-09-20 05:35:39 +08:00
fix(eve): serve gateway SDK model instances through the /login connection
A source-backed gateway(...) instance authenticates from AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN and never saw the connection /login saved, so skipping the agent.ts rewrite alone made /login report success and the first request fail with GatewayAuthenticationError. In eve dev, resolve such an instance through localGatewayModel(modelId), as a string model id already is; outside dev the authored instance is unchanged. Signed-off-by: Rui Conti <ruiconti@gmail.com>
This commit is contained in:
@@ -2,4 +2,4 @@
|
||||
"eve": patch
|
||||
---
|
||||
|
||||
`/login` now connects a Vercel account or AI Gateway key to an agent whose `model` is a gateway SDK call (such as `gateway(...)`) without trying to rewrite `agent.ts`. Previously the failed source edit discarded the connection and returned to the connection picker, leaving the agent unusable.
|
||||
`/login` now works for agents whose `model` is a gateway SDK call such as `gateway("anthropic/claude-sonnet-5")`. In `eve dev`, a gateway-routed model instance is served through the connection saved by `/login` (Vercel account, AI Gateway key, or project), the same way a string model id is, and `/login` no longer tries to rewrite `agent.ts` for it. Previously the failed source edit sent you back to the connection picker after every sign-in.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { LanguageModel } from "ai";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { ROOT_COMPILED_AGENT_NODE_ID } from "#compiler/manifest.js";
|
||||
import type { CompiledModuleMap } from "#compiler/module-map.js";
|
||||
@@ -72,6 +72,58 @@ describe("dynamic runtime model resolution", () => {
|
||||
expect(model.modelId).toBe("eve-mock/dynamic-subagent");
|
||||
});
|
||||
|
||||
describe("source-backed models", () => {
|
||||
const source = {
|
||||
logicalPath: "agent.ts",
|
||||
sourceId: "agent-config",
|
||||
sourceKind: "module" as const,
|
||||
};
|
||||
const resolve = (model: LanguageModel) =>
|
||||
resolveRuntimeModelReference(
|
||||
{ id: typeof model === "string" ? model : `${model.provider}/${model.modelId}`, source },
|
||||
{ moduleMap: createModuleMap({ default: { model } }), nodeId: undefined },
|
||||
);
|
||||
|
||||
beforeEach(() => vi.stubEnv("NODE_ENV", "production"));
|
||||
|
||||
it("serves a gateway SDK instance through the /login connection in eve dev", async () => {
|
||||
vi.stubEnv("EVE_DEV", "1");
|
||||
vi.stubEnv("EVE_MODEL_CONNECTION", "vercel");
|
||||
const authored = createLanguageModel("gateway", "anthropic/claude-sonnet-5");
|
||||
|
||||
const model = await resolve(authored);
|
||||
|
||||
expect(model).not.toBe(authored);
|
||||
if (typeof model === "string") throw new Error("expected a model instance");
|
||||
expect(model.provider).toBe("gateway");
|
||||
expect(model.modelId).toBe("anthropic/claude-sonnet-5");
|
||||
});
|
||||
|
||||
it("returns the authored gateway instance when no connection is active", async () => {
|
||||
vi.stubEnv("EVE_DEV", "1");
|
||||
vi.stubEnv("EVE_MODEL_CONNECTION", "");
|
||||
const authored = createLanguageModel("gateway", "anthropic/claude-sonnet-5");
|
||||
|
||||
expect(await resolve(authored)).toBe(authored);
|
||||
});
|
||||
|
||||
it("returns the authored gateway instance outside eve dev", async () => {
|
||||
vi.stubEnv("EVE_DEV", "");
|
||||
vi.stubEnv("EVE_MODEL_CONNECTION", "vercel");
|
||||
const authored = createLanguageModel("gateway", "anthropic/claude-sonnet-5");
|
||||
|
||||
expect(await resolve(authored)).toBe(authored);
|
||||
});
|
||||
|
||||
it("leaves a direct provider instance untouched", async () => {
|
||||
vi.stubEnv("EVE_DEV", "1");
|
||||
vi.stubEnv("EVE_MODEL_CONNECTION", "vercel");
|
||||
const authored = createLanguageModel("openai.responses", "gpt-5.5");
|
||||
|
||||
expect(await resolve(authored)).toBe(authored);
|
||||
});
|
||||
});
|
||||
|
||||
it("loads resolver-only definitions and normalizes explicit metadata", async () => {
|
||||
const moduleMap = createModuleMap({
|
||||
default: {
|
||||
|
||||
@@ -110,7 +110,23 @@ async function loadSourceBackedRuntimeModelReference(
|
||||
);
|
||||
}
|
||||
|
||||
return model;
|
||||
return withLocalGatewayConnection(model);
|
||||
}
|
||||
|
||||
/**
|
||||
* A gateway-routed SDK instance (`gateway("openai/gpt-5.6")`) authenticates
|
||||
* from `AI_GATEWAY_API_KEY`/`VERCEL_OIDC_TOKEN` and never sees the connection
|
||||
* `/login` saved. While such a connection is active, serve the same model id
|
||||
* through it, as a string model would be. Outside `eve dev` the authored
|
||||
* instance is returned untouched.
|
||||
*/
|
||||
function withLocalGatewayConnection(model: LanguageModel): LanguageModel {
|
||||
if (typeof model === "string") return model;
|
||||
if (typeof model.provider !== "string" || model.provider.split(".")[0] !== "gateway") {
|
||||
return model;
|
||||
}
|
||||
if (typeof model.modelId !== "string" || model.modelId === "") return model;
|
||||
return localGatewayModel(model.modelId) ?? model;
|
||||
}
|
||||
|
||||
function isSourceBackedRuntimeModelReference(
|
||||
|
||||
Reference in New Issue
Block a user