@opencode/ai 0.0.0-dev-19993 → 0.0.0-dev-19996

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -66,7 +66,17 @@ console.log(response.answers.refund.probability)
66
66
  ```
67
67
 
68
68
  `TypeSafeAI` reads `TYPESAFE_API_KEY`. `OpenCodeZen` exposes the same selector and reads
69
- `OPENCODE_API_KEY`. The common API uses `boolean`; System One routes lower it to native `noul`.
69
+ `OPENCODE_API_KEY`. OpenRouter and Vercel AI Gateway use the same provider shape:
70
+
71
+ ```ts
72
+ import { OpenRouter, VercelAIGateway } from "@opencode/ai/providers"
73
+
74
+ OpenRouter.configure().experimental.evaluation("typesafe/jev-1.13")
75
+ VercelAIGateway.configure().experimental.evaluation("typesafe-ai/jev")
76
+ ```
77
+
78
+ OpenRouter reads `OPENROUTER_API_KEY`. Vercel reads `AI_GATEWAY_API_KEY`, then `VERCEL_OIDC_TOKEN`.
79
+ The common API uses `boolean`; System One routes lower it to native `noul`.
70
80
  Choice and score confidence plus score legends remain available in provider metadata, and the
71
81
  provider's rounded probabilities are returned unchanged.
72
82
 
@@ -1,6 +1,6 @@
1
1
  import { Context, Effect, Layer } from "effect";
2
2
  import { RequestExecutor } from "../route/executor.js";
3
- import { type AIError } from "../schema/index.js";
3
+ import { AIError } from "../schema/index.js";
4
4
  import { type EvaluationOptions, type EvaluationQuestions, type EvaluationRequestFor, type EvaluationResponseFor } from "./evaluation.js";
5
5
  export type Execute = RequestExecutor.Interface["execute"];
6
6
  export interface Interface {
@@ -1,6 +1,6 @@
1
1
  import { Context, Effect, Layer } from "effect";
2
2
  import { RequestExecutor } from "../route/executor.js";
3
- import { mergeHttpOptions } from "../schema/index.js";
3
+ import { AIError, InvalidProviderOutputError, mergeHttpOptions } from "../schema/index.js";
4
4
  import { sanitizeSurrogates } from "../utils/sanitize.js";
5
5
  import {} from "./evaluation.js";
6
6
  export class Service extends Context.Service()("@opencode/AI/Experimental/EvaluationClient") {
@@ -9,14 +9,50 @@ export const evaluate = (request) => Effect.flatMap(Service, (client) => client.
9
9
  export const layer = Layer.effect(Service, Effect.gen(function* () {
10
10
  const executor = yield* RequestExecutor.Service;
11
11
  return Service.of({
12
- evaluate: (request) => request.model.route.evaluate({
12
+ evaluate: (request) => request.model.route
13
+ .evaluate({
13
14
  ...sanitizeSurrogates({
14
15
  ...request,
15
16
  model: undefined,
16
17
  http: mergeHttpOptions(request.model.http, request.http),
17
18
  }),
18
19
  model: request.model,
19
- }, executor.execute),
20
+ }, executor.execute)
21
+ .pipe(Effect.flatMap((response) => {
22
+ const questions = Object.entries(request.questions);
23
+ if (questions.length === Object.keys(response.answers).length &&
24
+ questions.every(([id, question]) => {
25
+ const answer = response.answers[id];
26
+ if (question.type === "boolean")
27
+ return answer?.type === "boolean";
28
+ if (question.type === "choice") {
29
+ if (answer?.type !== "choice" || !Object.hasOwn(question.criteria, answer.choice))
30
+ return false;
31
+ if (answer.probabilities === undefined)
32
+ return true;
33
+ const keys = Object.keys(question.criteria);
34
+ const probabilities = answer.probabilities;
35
+ return (Object.keys(probabilities).length === keys.length &&
36
+ keys.every((key) => Object.hasOwn(probabilities, key)));
37
+ }
38
+ if (answer?.type !== "score" || answer.score < 0 || answer.score > question.criteria.length - 1)
39
+ return false;
40
+ if (answer.probabilities === undefined)
41
+ return true;
42
+ const keys = question.criteria.map((_, index) => String(index));
43
+ const probabilities = answer.probabilities;
44
+ return (Object.keys(probabilities).length === keys.length &&
45
+ keys.every((key) => Object.hasOwn(probabilities, key)));
46
+ }))
47
+ return Effect.succeed(response);
48
+ return Effect.fail(new AIError({
49
+ reason: new InvalidProviderOutputError({
50
+ route: request.model.route.id,
51
+ message: "Evaluation answers do not match the requested questions",
52
+ cause: response.answers,
53
+ }),
54
+ }));
55
+ })),
20
56
  });
21
57
  }));
22
58
  export const fetchLayer = layer.pipe(Layer.provide(RequestExecutor.fetchLayer));
@@ -96,7 +96,7 @@ export type AnswersFor<Questions extends EvaluationQuestions> = {
96
96
  export type EvaluationOptions = Record<string, unknown>;
97
97
  export interface EvaluationRoute<Options extends EvaluationOptions = EvaluationOptions> {
98
98
  readonly id: string;
99
- readonly evaluate: <const Questions extends EvaluationQuestions>(request: EvaluationRequestFor<Options, Questions>, execute: Execute) => Effect.Effect<EvaluationResponseFor<Questions>, AIError>;
99
+ readonly evaluate: (request: EvaluationRequestFor<Options>, execute: Execute) => Effect.Effect<EvaluationResponse, AIError>;
100
100
  }
101
101
  export declare class EvaluationModel<Options extends EvaluationOptions = EvaluationOptions> {
102
102
  protected readonly _Options: (options: Options) => Options;
@@ -1,4 +1,4 @@
1
- import { EvaluationModel } from "./evaluation.js";
1
+ import { EvaluationModel, type EvaluationOptions } from "./evaluation.js";
2
2
  import { type Definition as AuthDefinition } from "../route/auth.js";
3
3
  import { HttpOptions, ModelID } from "../schema/index.js";
4
4
  export interface ModelInput {
@@ -10,7 +10,7 @@ export interface ModelInput {
10
10
  readonly headers?: Record<string, string>;
11
11
  readonly http?: HttpOptions;
12
12
  }
13
- export declare const model: (cfg: ModelInput) => EvaluationModel<import("./evaluation.js").EvaluationOptions>;
13
+ export declare const model: <Options extends EvaluationOptions = EvaluationOptions>(cfg: ModelInput) => EvaluationModel<Options>;
14
14
  export declare const SystemOne: {
15
- readonly model: (cfg: ModelInput) => EvaluationModel<import("./evaluation.js").EvaluationOptions>;
15
+ readonly model: <Options extends EvaluationOptions = EvaluationOptions>(cfg: ModelInput) => EvaluationModel<Options>;
16
16
  };
@@ -1,6 +1,6 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import { Headers, HttpClientRequest } from "effect/unstable/http";
3
- import { BooleanAnswer, ChoiceAnswer, ChoiceQuestion, EvaluationInput, EvaluationModel, EvaluationResponse, EvaluationRounding, ScoreAnswer, ScoreQuestion, } from "./evaluation.js";
3
+ import { ChoiceQuestion, EvaluationInput, EvaluationModel, EvaluationResponse, EvaluationRounding, ScoreQuestion, } from "./evaluation.js";
4
4
  import { Auth } from "../route/auth.js";
5
5
  import { AIError, HttpContext, HttpOptions, InvalidProviderOutputError, InvalidRequestError, ModelID, Usage, mergeJsonRecords, } from "../schema/index.js";
6
6
  const Noul = Schema.Struct({
@@ -40,19 +40,29 @@ const Score = Schema.Struct({
40
40
  legend: Schema.optional(Schema.Record(Schema.String, Schema.Json)),
41
41
  confidence: Schema.optional(Probability),
42
42
  });
43
- const NativeUsage = Schema.Struct({
43
+ const Answer = Schema.Union([NoulAnswer, Choice, Score]).pipe(Schema.toTaggedUnion("type"));
44
+ const NativeUsage = Schema.StructWithRest(Schema.Struct({
44
45
  input_tokens: Schema.optional(Schema.Number),
45
46
  output_tokens: Schema.optional(Schema.Number),
47
+ }), [Schema.Record(Schema.String, Schema.Unknown)]);
48
+ const Response = Schema.Struct({
49
+ model: Schema.String,
50
+ answers: Schema.Record(Schema.String, Answer),
51
+ usage: Schema.optional(NativeUsage),
52
+ id: Schema.optional(Schema.String),
53
+ provider: Schema.optional(Schema.String),
54
+ provider_metadata: Schema.optional(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Unknown))),
46
55
  });
47
- const encode = Schema.encodeUnknownEffect(Schema.fromJsonString(Request));
48
- const exact = (x, keys) => Object.keys(x).length === keys.length && keys.every((key) => Object.hasOwn(x, key));
49
- export const model = (cfg) => {
50
- const route = {
56
+ export const model = (cfg) => EvaluationModel.make({
57
+ id: cfg.id,
58
+ provider: cfg.provider,
59
+ http: cfg.http,
60
+ route: {
51
61
  id: "system-one",
52
62
  evaluate: (req, send) => Effect.gen(function* () {
53
63
  const url = new URL(`${cfg.baseURL.replace(/\/$/, "")}/systemone`);
54
64
  Object.entries(req.http?.query ?? {}).forEach(([key, value]) => url.searchParams.set(key, value));
55
- const body = yield* encode({
65
+ const body = yield* Schema.encodeUnknownEffect(Schema.fromJsonString(Request))({
56
66
  ...mergeJsonRecords(req.options, req.http?.body),
57
67
  model: req.model.id,
58
68
  state: req.state,
@@ -67,56 +77,36 @@ export const model = (cfg) => {
67
77
  });
68
78
  const res = yield* send(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(body, "application/json")));
69
79
  const http = new HttpContext({ url: res.request.url, status: res.status, headers: res.headers });
70
- const fail = (message, cause, body) => new AIError({ reason: new InvalidProviderOutputError({ route: route.id, message, body, http, cause }) });
80
+ const fail = (message, cause, body) => new AIError({ reason: new InvalidProviderOutputError({ route: "system-one", message, body, http, cause }) });
71
81
  const text = yield* res.text.pipe(Effect.mapError((cause) => fail("Failed to read the System One response", cause)));
72
- const entries = Object.entries(req.questions);
73
- const output = Schema.Struct({
74
- model: Schema.String,
75
- answers: Schema.Struct(Object.fromEntries(entries.map(([id, question]) => {
76
- if (question.type === "boolean")
77
- return [id, NoulAnswer];
78
- if (question.type === "choice") {
79
- const keys = Object.keys(question.criteria);
80
- return [
81
- id,
82
- Choice.pipe(Schema.refine((x) => Object.hasOwn(question.criteria, x.choice) && exact(x.probabilities, keys), { message: `Question "${id}" returned an invalid choice answer` })),
83
- ];
84
- }
85
- const keys = question.criteria.map((_, index) => String(index));
86
- return [
87
- id,
88
- Score.pipe(Schema.refine((x) => x.score >= 0 && x.score <= question.criteria.length - 1 && exact(x.probabilities, keys), { message: `Question "${id}" returned an invalid score answer` })),
89
- ];
90
- }))),
91
- usage: Schema.optional(NativeUsage),
92
- });
93
- const data = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(output))(text).pipe(Effect.mapError((cause) => fail("System One returned an invalid response", cause, text)));
82
+ const data = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(Response))(text).pipe(Effect.mapError((cause) => fail("System One returned an invalid response", cause, text)));
94
83
  const confidence = {};
95
84
  const legend = {};
96
- const answers = Object.fromEntries(entries.map(([id, question]) => {
97
- const answer = data.answers[id];
98
- if (question.type === "boolean")
85
+ const answers = Object.fromEntries(Object.entries(data.answers).map(([id, answer]) => {
86
+ if (answer.type === "noul")
87
+ return [id, { type: "boolean", probability: answer.noul }];
88
+ if (answer.type === "choice") {
89
+ if (answer.confidence !== undefined)
90
+ confidence[id] = answer.confidence;
99
91
  return [
100
92
  id,
101
- { type: "boolean", probability: answer.noul },
102
- ];
103
- if (question.type === "choice") {
104
- const value = answer;
105
- if (value.confidence !== undefined)
106
- confidence[id] = value.confidence;
107
- return [
108
- id,
109
- { type: "choice", choice: value.choice, probabilities: value.probabilities },
93
+ {
94
+ type: "choice",
95
+ choice: answer.choice,
96
+ probabilities: answer.probabilities,
97
+ },
110
98
  ];
111
99
  }
112
- const value = answer;
113
- if (value.confidence !== undefined)
114
- confidence[id] = value.confidence;
115
- if (value.legend !== undefined)
116
- legend[id] = value.legend;
117
- return [id, { type: "score", score: value.score, probabilities: value.probabilities }];
100
+ if (answer.confidence !== undefined)
101
+ confidence[id] = answer.confidence;
102
+ if (answer.legend !== undefined)
103
+ legend[id] = answer.legend;
104
+ return [id, { type: "score", score: answer.score, probabilities: answer.probabilities }];
118
105
  }));
119
106
  const meta = {
107
+ ...(data.id === undefined ? {} : { responseId: data.id }),
108
+ ...(data.provider === undefined ? {} : { provider: data.provider }),
109
+ ...data.provider_metadata?.[cfg.providerMetadataKey],
120
110
  ...(Object.keys(confidence).length === 0 ? {} : { confidence }),
121
111
  ...(Object.keys(legend).length === 0 ? {} : { legend }),
122
112
  };
@@ -137,7 +127,6 @@ export const model = (cfg) => {
137
127
  providerMetadata: Object.keys(meta).length === 0 ? undefined : { [cfg.providerMetadataKey]: meta },
138
128
  });
139
129
  }),
140
- };
141
- return EvaluationModel.make({ id: cfg.id, provider: cfg.provider, route, http: cfg.http });
142
- };
130
+ },
131
+ });
143
132
  export const SystemOne = { model };
@@ -28,6 +28,7 @@ export * as OpenCodeZen from "./opencode-zen.js";
28
28
  export * as OpenRouter from "./openrouter.js";
29
29
  export * as TogetherAI from "./togetherai.js";
30
30
  export * as TypeSafeAI from "./typesafe-ai.js";
31
+ export * as VercelAIGateway from "./vercel-ai-gateway.js";
31
32
  export * as XAI from "./xai.js";
32
33
  export * as ZAI from "./zai.js";
33
34
  export * as ZAICodingPlan from "./zai-coding-plan.js";
@@ -28,6 +28,7 @@ export * as OpenCodeZen from "./opencode-zen.js";
28
28
  export * as OpenRouter from "./openrouter.js";
29
29
  export * as TogetherAI from "./togetherai.js";
30
30
  export * as TypeSafeAI from "./typesafe-ai.js";
31
+ export * as VercelAIGateway from "./vercel-ai-gateway.js";
31
32
  export * as XAI from "./xai.js";
32
33
  export * as ZAI from "./zai.js";
33
34
  export * as ZAICodingPlan from "./zai-coding-plan.js";
@@ -73,6 +73,13 @@ export interface OpenRouterOptions {
73
73
  }>;
74
74
  }
75
75
  export type OpenRouterProviderOptionsInput = OpenRouterOptions;
76
+ export interface OpenRouterEvaluationOptions {
77
+ readonly [key: string]: unknown;
78
+ readonly provider?: OpenRouterProviderRouting;
79
+ readonly session_id?: string;
80
+ readonly trace?: Readonly<Record<string, unknown>>;
81
+ readonly user?: string;
82
+ }
76
83
  export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> & ProviderAuthOption<"optional"> & {
77
84
  readonly baseURL?: string;
78
85
  readonly providerOptions?: OpenRouterProviderOptionsInput;
@@ -548,16 +555,28 @@ export declare const routes: Route<{
548
555
  export declare const configure: (input?: LanguageModelOptions) => {
549
556
  id: string & import("effect/Brand").Brand<"AI.ProviderID">;
550
557
  model: (modelID: string | ModelID) => import("../schema/options.js").LanguageModel<OpenRouterOptions, import("../route/client.js").CompactionOperations | undefined>;
558
+ experimental: {
559
+ evaluation: (modelID: string | ModelID) => import("../experimental.js").EvaluationModel<OpenRouterEvaluationOptions>;
560
+ };
551
561
  configure: (input?: LanguageModelOptions) => /*elided*/ any;
552
562
  };
553
563
  export declare const provider: {
554
564
  id: string & import("effect/Brand").Brand<"AI.ProviderID">;
555
565
  model: (modelID: string | ModelID) => import("../schema/options.js").LanguageModel<OpenRouterOptions, import("../route/client.js").CompactionOperations | undefined>;
566
+ experimental: {
567
+ evaluation: (modelID: string | ModelID) => import("../experimental.js").EvaluationModel<OpenRouterEvaluationOptions>;
568
+ };
556
569
  configure: (input?: LanguageModelOptions) => {
557
570
  id: string & import("effect/Brand").Brand<"AI.ProviderID">;
558
571
  model: (modelID: string | ModelID) => import("../schema/options.js").LanguageModel<OpenRouterOptions, import("../route/client.js").CompactionOperations | undefined>;
572
+ experimental: {
573
+ evaluation: (modelID: string | ModelID) => import("../experimental.js").EvaluationModel<OpenRouterEvaluationOptions>;
574
+ };
559
575
  configure: /*elided*/ any;
560
576
  };
561
577
  };
578
+ export declare const experimental: {
579
+ evaluation: (modelID: string | ModelID) => import("../experimental.js").EvaluationModel<OpenRouterEvaluationOptions>;
580
+ };
562
581
  export declare const model: ProviderPackage.Definition<Settings, OpenRouterProviderOptionsInput>["model"];
563
582
  export {};
@@ -3,7 +3,8 @@ import { Route } from "../route/client.js";
3
3
  import { Endpoint } from "../route/endpoint.js";
4
4
  import { Protocol } from "../route/protocol.js";
5
5
  import { AuthOptions } from "../route/auth-options.js";
6
- import { ProviderID } from "../schema/index.js";
6
+ import { HttpOptions, ProviderID } from "../schema/index.js";
7
+ import { SystemOne } from "../experimental/system-one.js";
7
8
  import { OpenAIChat } from "../protocols/openai-chat.js";
8
9
  import { newBreakpoints, ttlBucket } from "../protocols/utils/cache.js";
9
10
  import { isRecord } from "../protocols/shared.js";
@@ -98,13 +99,24 @@ const configuredRoute = (input) => {
98
99
  };
99
100
  export const configure = (input = {}) => {
100
101
  const route = configuredRoute(input);
102
+ const evaluation = (modelID) => SystemOne.model({
103
+ id: modelID,
104
+ provider: id,
105
+ providerMetadataKey: "openrouter",
106
+ auth: AuthOptions.bearer(input, "OPENROUTER_API_KEY"),
107
+ baseURL: input.baseURL ?? baseURL,
108
+ headers: input.headers,
109
+ http: input.http === undefined ? undefined : HttpOptions.make(input.http),
110
+ });
101
111
  return {
102
112
  id,
103
113
  model: (modelID) => route.model({ id: modelID, compatibility: { supportsPromptCacheKey: true } }),
114
+ experimental: { evaluation },
104
115
  configure,
105
116
  };
106
117
  };
107
118
  export const provider = configure();
119
+ export const experimental = provider.experimental;
108
120
  export const model = (modelID, { apiKey, baseURL, body, headers, ...providerOptions }) => configure({
109
121
  apiKey,
110
122
  baseURL,
@@ -0,0 +1,41 @@
1
+ import { EvaluationModel } from "../experimental/evaluation.js";
2
+ import { type ProviderAuthOption } from "../route/auth-options.js";
3
+ import { HttpOptions, ModelID } from "../schema/index.js";
4
+ export declare const id: string & import("effect/Brand").Brand<"AI.ProviderID">;
5
+ export interface EvaluationOptions {
6
+ readonly [key: string]: unknown;
7
+ readonly gateway?: Readonly<{
8
+ readonly [key: string]: unknown;
9
+ readonly zeroDataRetention?: boolean;
10
+ readonly only?: ReadonlyArray<string>;
11
+ }>;
12
+ }
13
+ export type Options = ProviderAuthOption<"optional"> & {
14
+ readonly baseURL?: string;
15
+ readonly headers?: Record<string, string>;
16
+ readonly http?: HttpOptions.Input;
17
+ };
18
+ export declare const configure: (input?: Options) => {
19
+ id: string & import("effect/Brand").Brand<"AI.ProviderID">;
20
+ experimental: {
21
+ evaluation: (modelID: string | ModelID) => EvaluationModel<EvaluationOptions>;
22
+ };
23
+ configure: (input?: Options) => /*elided*/ any;
24
+ };
25
+ export declare const provider: {
26
+ id: string & import("effect/Brand").Brand<"AI.ProviderID">;
27
+ experimental: {
28
+ evaluation: (modelID: string | ModelID) => EvaluationModel<EvaluationOptions>;
29
+ };
30
+ configure: (input?: Options) => {
31
+ id: string & import("effect/Brand").Brand<"AI.ProviderID">;
32
+ experimental: {
33
+ evaluation: (modelID: string | ModelID) => EvaluationModel<EvaluationOptions>;
34
+ };
35
+ configure: /*elided*/ any;
36
+ };
37
+ };
38
+ export declare const experimental: {
39
+ evaluation: (modelID: string | ModelID) => EvaluationModel<EvaluationOptions>;
40
+ };
41
+ export * as VercelAIGateway from "./vercel-ai-gateway.js";
@@ -0,0 +1,85 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Headers, HttpClientRequest } from "effect/unstable/http";
3
+ import { EvaluationAnswer, EvaluationInput, EvaluationModel, EvaluationQuestion, EvaluationResponse, EvaluationRounding, } from "../experimental/evaluation.js";
4
+ import { Auth } from "../route/auth.js";
5
+ import { AuthOptions } from "../route/auth-options.js";
6
+ import { AIError, HttpContext, HttpOptions, InvalidProviderOutputError, InvalidRequestError, ModelID, ProviderID, ProviderMetadata, Usage, } from "../schema/index.js";
7
+ export const id = ProviderID.make("vercel-ai-gateway");
8
+ const baseURL = "https://ai-gateway.vercel.sh/v1";
9
+ const Request = Schema.StructWithRest(Schema.Struct({
10
+ model: Schema.String,
11
+ state: EvaluationInput,
12
+ questions: Schema.Record(Schema.String, EvaluationQuestion),
13
+ providerOptions: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
14
+ }), [Schema.Record(Schema.String, Schema.Any)]);
15
+ const Response = Schema.Struct({
16
+ model: Schema.optional(Schema.String),
17
+ answers: Schema.Record(Schema.String, EvaluationAnswer),
18
+ usage: Schema.optional(Schema.Struct({
19
+ inputTokens: Schema.optional(Schema.Number),
20
+ outputTokens: Schema.optional(Schema.Number),
21
+ })),
22
+ rounding: Schema.optional(EvaluationRounding),
23
+ providerMetadata: Schema.optional(ProviderMetadata),
24
+ });
25
+ export const configure = (input = {}) => {
26
+ const evaluation = (modelID) => EvaluationModel.make({
27
+ id: modelID,
28
+ provider: id,
29
+ http: input.http === undefined ? undefined : HttpOptions.make(input.http),
30
+ route: {
31
+ id: "vercel-evaluation",
32
+ evaluate: (req, send) => Effect.gen(function* () {
33
+ const url = new URL(`${(input.baseURL ?? baseURL).replace(/\/$/, "")}/evaluate`);
34
+ Object.entries(req.http?.query ?? {}).forEach(([key, value]) => url.searchParams.set(key, value));
35
+ const body = yield* Schema.encodeUnknownEffect(Schema.fromJsonString(Request))({
36
+ ...req.http?.body,
37
+ model: req.model.id,
38
+ state: req.state,
39
+ questions: req.questions,
40
+ providerOptions: req.options,
41
+ }).pipe(Effect.mapError((cause) => new AIError({ reason: new InvalidRequestError({ message: cause.message, cause }) })));
42
+ const headers = yield* Auth.toEffect(AuthOptions.bearer(input, ["AI_GATEWAY_API_KEY", "VERCEL_OIDC_TOKEN"]))({
43
+ request: req,
44
+ method: "POST",
45
+ url: url.toString(),
46
+ body,
47
+ headers: Headers.fromInput({ ...input.headers, ...req.http?.headers }),
48
+ });
49
+ const res = yield* send(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(body, "application/json")));
50
+ const http = new HttpContext({ url: res.request.url, status: res.status, headers: res.headers });
51
+ const fail = (message, cause, body) => new AIError({
52
+ reason: new InvalidProviderOutputError({
53
+ route: "vercel-evaluation",
54
+ message,
55
+ body,
56
+ http,
57
+ cause,
58
+ }),
59
+ });
60
+ const text = yield* res.text.pipe(Effect.mapError((cause) => fail("Failed to read the Vercel AI Gateway evaluation response", cause)));
61
+ const data = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(Response))(text).pipe(Effect.mapError((cause) => fail("Vercel AI Gateway returned an invalid evaluation response", cause, text)));
62
+ return new EvaluationResponse({
63
+ model: ModelID.make(data.model ?? req.model.id),
64
+ answers: data.answers,
65
+ usage: data.usage
66
+ ? new Usage({
67
+ inputTokens: data.usage.inputTokens,
68
+ outputTokens: data.usage.outputTokens,
69
+ totalTokens: data.usage.inputTokens === undefined && data.usage.outputTokens === undefined
70
+ ? undefined
71
+ : (data.usage.inputTokens ?? 0) + (data.usage.outputTokens ?? 0),
72
+ providerMetadata: { gateway: data.usage },
73
+ })
74
+ : undefined,
75
+ rounding: data.rounding,
76
+ providerMetadata: data.providerMetadata,
77
+ });
78
+ }),
79
+ },
80
+ });
81
+ return { id, experimental: { evaluation }, configure };
82
+ };
83
+ export const provider = configure();
84
+ export const experimental = provider.experimental;
85
+ export * as VercelAIGateway from "./vercel-ai-gateway.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-dev-19993",
3
+ "version": "0.0.0-dev-19996",
4
4
  "name": "@opencode/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@clack/prompts": "1.0.0-alpha.1",
32
32
  "@effect/platform-node": "4.0.0-rc.112",
33
- "@opencode/http-recorder": "0.0.0-dev-19993",
33
+ "@opencode/http-recorder": "0.0.0-dev-19996",
34
34
  "@tsconfig/bun": "1.0.9",
35
35
  "@types/bun": "1.4.0",
36
36
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -40,7 +40,7 @@
40
40
  "@aws-sdk/credential-providers": "3.1057.0",
41
41
  "@smithy/eventstream-codec": "4.2.14",
42
42
  "@smithy/util-utf8": "4.2.2",
43
- "@opencode/schema": "0.0.0-dev-19993",
43
+ "@opencode/schema": "0.0.0-dev-19996",
44
44
  "aws4fetch": "1.0.20",
45
45
  "effect": "4.0.0-rc.112",
46
46
  "google-auth-library": "10.5.0"