@opencode/ai 2.1.1 → 2.1.3

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.
Files changed (99) hide show
  1. package/dist/cache-policy.js +6 -5
  2. package/dist/experimental/evaluation.d.ts +7 -6
  3. package/dist/experimental/evaluation.js +2 -3
  4. package/dist/experimental/realtime.d.ts +59 -0
  5. package/dist/experimental/realtime.js +32 -19
  6. package/dist/experimental/system-one.js +8 -8
  7. package/dist/experimental.d.ts +1 -1
  8. package/dist/generation.js +1 -1
  9. package/dist/image-client.d.ts +3 -1
  10. package/dist/image.d.ts +10 -2
  11. package/dist/image.js +2 -2
  12. package/dist/llm.d.ts +2 -4
  13. package/dist/llm.js +2 -2
  14. package/dist/media-model.d.ts +2 -2
  15. package/dist/media-model.js +1 -1
  16. package/dist/media.js +3 -3
  17. package/dist/promise.d.ts +1 -1
  18. package/dist/promise.js +3 -5
  19. package/dist/protocols/alibaba-chat.d.ts +6 -6
  20. package/dist/protocols/alibaba-chat.js +3 -3
  21. package/dist/protocols/alibaba-messages.d.ts +1 -1
  22. package/dist/protocols/alibaba-messages.js +1 -1
  23. package/dist/protocols/alibaba-responses.d.ts +3 -3
  24. package/dist/protocols/anthropic-messages.js +10 -12
  25. package/dist/protocols/bedrock-converse.d.ts +6 -53
  26. package/dist/protocols/bedrock-converse.js +4 -3
  27. package/dist/protocols/bedrock-event-stream.d.ts +6 -1
  28. package/dist/protocols/bedrock-event-stream.js +9 -8
  29. package/dist/protocols/cohere-chat.d.ts +6 -2
  30. package/dist/protocols/cohere-chat.js +2 -2
  31. package/dist/protocols/gemini.d.ts +12 -2
  32. package/dist/protocols/gemini.js +10 -13
  33. package/dist/protocols/google-interactions.d.ts +2 -1
  34. package/dist/protocols/google-interactions.js +14 -9
  35. package/dist/protocols/meta-responses.d.ts +6 -6
  36. package/dist/protocols/mistral-chat.js +1 -4
  37. package/dist/protocols/open-responses.d.ts +21 -21
  38. package/dist/protocols/open-responses.js +3 -3
  39. package/dist/protocols/openai-chat.d.ts +8 -7
  40. package/dist/protocols/openai-chat.js +4 -5
  41. package/dist/protocols/openai-compatible-responses.d.ts +3 -3
  42. package/dist/protocols/openai-responses.d.ts +18 -18
  43. package/dist/protocols/utils/open-responses-options.d.ts +8 -8
  44. package/dist/protocols/utils/open-responses-options.js +4 -6
  45. package/dist/protocols/utils/partial-json.js +5 -4
  46. package/dist/protocols/utils/tool-schema.d.ts +0 -3
  47. package/dist/protocols/utils/tool-schema.js +0 -4
  48. package/dist/protocols/venice-chat.js +6 -6
  49. package/dist/protocols/xai-images.js +3 -4
  50. package/dist/protocols/xai-responses.d.ts +3 -3
  51. package/dist/provider-error.js +4 -5
  52. package/dist/providers/alibaba.d.ts +30 -30
  53. package/dist/providers/amazon-bedrock-mantle.d.ts +3 -3
  54. package/dist/providers/amazon-bedrock.d.ts +11 -8
  55. package/dist/providers/azure.d.ts +6 -6
  56. package/dist/providers/cloudflare-ai-gateway.d.ts +7 -7
  57. package/dist/providers/cloudflare-ai-gateway.js +2 -4
  58. package/dist/providers/cohere.d.ts +6 -2
  59. package/dist/providers/google-vertex-interactions.d.ts +3 -3
  60. package/dist/providers/google-vertex-messages.d.ts +18 -18
  61. package/dist/providers/google-vertex-mistral.d.ts +3 -3
  62. package/dist/providers/google-vertex-responses.d.ts +3 -3
  63. package/dist/providers/google.d.ts +66 -66
  64. package/dist/providers/meta.d.ts +21 -21
  65. package/dist/providers/minimax.d.ts +195 -195
  66. package/dist/providers/moonshot.d.ts +3 -3
  67. package/dist/providers/openai-compatible-responses.d.ts +3 -3
  68. package/dist/providers/openai-options.d.ts +3 -3
  69. package/dist/providers/openai.d.ts +3 -3
  70. package/dist/providers/openrouter.d.ts +135 -76
  71. package/dist/providers/openrouter.js +40 -33
  72. package/dist/providers/vercel-ai-gateway.js +7 -6
  73. package/dist/providers/zai-coding-plan.d.ts +3 -3
  74. package/dist/route/client.d.ts +0 -216
  75. package/dist/route/client.js +1 -5
  76. package/dist/route/executor-service.d.ts +6 -2
  77. package/dist/route/executor-service.js +7 -0
  78. package/dist/route/executor.d.ts +2 -3
  79. package/dist/route/executor.js +6 -11
  80. package/dist/route/media-protocol.d.ts +1 -1
  81. package/dist/route/media-protocol.js +3 -3
  82. package/dist/route/transport/http.js +5 -8
  83. package/dist/route/transport/websocket.d.ts +0 -24
  84. package/dist/route/transport/websocket.js +0 -50
  85. package/dist/schema/errors.d.ts +9 -8
  86. package/dist/schema/errors.js +9 -5
  87. package/dist/schema/messages.js +4 -7
  88. package/dist/speech-client.d.ts +3 -1
  89. package/dist/speech.d.ts +10 -2
  90. package/dist/speech.js +2 -2
  91. package/dist/transcription-client.d.ts +3 -1
  92. package/dist/transcription.d.ts +10 -2
  93. package/dist/transcription.js +2 -2
  94. package/dist/video-client.d.ts +3 -1
  95. package/dist/video.d.ts +10 -2
  96. package/dist/video.js +2 -2
  97. package/package.json +4 -4
  98. package/dist/protocols/utils/openai-options.d.ts +0 -16
  99. package/dist/protocols/utils/openai-options.js +0 -15
@@ -94,8 +94,9 @@ const markSystemBoundaries = (system, hint, budget) => {
94
94
  return changed ? next : system;
95
95
  };
96
96
  const lastIndexOfRole = (messages, role) => messages.findLastIndex((m) => m.role === role);
97
+ const isCacheablePart = (part) => part.type !== "compaction" && part.type !== "effort";
97
98
  // Mark the last text part of `messages[index]`. If no text part exists, mark
98
- // the last content part regardless of type — that's the breakpoint position
99
+ // the last cacheable content part — that's the breakpoint position
99
100
  // in tool-result-only messages too.
100
101
  const markMessageAt = (messages, index, hint, budget) => {
101
102
  if (index < 0 || index >= messages.length)
@@ -104,12 +105,12 @@ const markMessageAt = (messages, index, hint, budget) => {
104
105
  if (target.content.length === 0)
105
106
  return messages;
106
107
  const lastTextIndex = target.content.findLastIndex((part) => part.type === "text");
107
- const markAt = lastTextIndex >= 0 ? lastTextIndex : target.content.length - 1;
108
+ const markAt = lastTextIndex >= 0 ? lastTextIndex : target.content.findLastIndex(isCacheablePart);
108
109
  const existing = target.content[markAt];
109
- if (("cache" in existing && existing.cache) || budget.remaining === 0)
110
+ if (!existing || !isCacheablePart(existing) || existing.cache || budget.remaining === 0)
110
111
  return messages;
111
112
  budget.remaining -= 1;
112
- const nextContent = target.content.map((part, i) => (i === markAt ? { ...part, cache: hint } : part));
113
+ const nextContent = target.content.map((part, i) => (i === markAt ? { ...existing, cache: hint } : part));
113
114
  const next = new Message({ ...target, content: nextContent });
114
115
  // Single pass over `messages`, substituting the one updated entry. Long
115
116
  // conversations call this on every request, so avoid `.map()` here — its
@@ -141,7 +142,7 @@ const markMessages = (messages, strategy, hint, budget) => {
141
142
  const countHints = (request) => countToolHints(request.tools) +
142
143
  request.system.reduce((count, part) => count + (part.cache === undefined ? 0 : 1), 0) +
143
144
  request.messages.reduce((count, message) => count +
144
- message.content.reduce((contentCount, part) => contentCount + ("cache" in part && part.cache !== undefined ? 1 : 0), 0), 0);
145
+ message.content.reduce((contentCount, part) => contentCount + (isCacheablePart(part) && part.cache !== undefined ? 1 : 0), 0), 0);
145
146
  export const applyCachePolicy = (request) => {
146
147
  const policy = selectPolicy(request);
147
148
  if (!policy.tools && !policy.system && !policy.messages)
@@ -101,10 +101,10 @@ export type AnswersFor<Questions extends EvaluationQuestions> = {
101
101
  export type EvaluationOptions = Record<string, unknown>;
102
102
  export interface EvaluationRoute<Options extends EvaluationOptions = EvaluationOptions> {
103
103
  readonly id: string;
104
- readonly evaluate: (request: EvaluationRequestFor<Options>, execute: Execute) => Effect.Effect<EvaluationResponse, AIError>;
104
+ evaluate(request: EvaluationRequestFor<Options>, execute: Execute): Effect.Effect<EvaluationResponse, AIError>;
105
105
  }
106
106
  export declare class EvaluationModel<Options extends EvaluationOptions = EvaluationOptions> {
107
- protected readonly _Options: (options: Options) => Options;
107
+ protected readonly _Options: Options;
108
108
  readonly id: ModelID;
109
109
  readonly provider: ProviderID;
110
110
  readonly route: EvaluationRoute<Options>;
@@ -193,12 +193,12 @@ export type EvaluationRequestFor<Options extends EvaluationOptions = EvaluationO
193
193
  readonly questions: Questions;
194
194
  readonly options?: Options;
195
195
  };
196
- export type EvaluationRequestInput<Model extends object = EvaluationModel, Questions extends EvaluationQuestions = EvaluationQuestions> = Omit<ConstructorParameters<typeof EvaluationRequest>[0], "model" | "questions" | "options" | "http"> & {
196
+ export type EvaluationRequestInput<Model extends EvaluationModel = EvaluationModel, Questions extends EvaluationQuestions = EvaluationQuestions> = Omit<ConstructorParameters<typeof EvaluationRequest>[0], "model" | "questions" | "options" | "http"> & {
197
197
  readonly model: Model;
198
198
  readonly questions: Questions;
199
199
  readonly options?: NoInfer<EvaluationModelOptions<Model>>;
200
200
  readonly http?: HttpOptions.Input;
201
- } & (Model extends EvaluationModel<EvaluationModelOptions<Model>> ? unknown : never);
201
+ };
202
202
  declare const EvaluationRounding_base: Schema.Class<EvaluationRounding, Schema.Struct<{
203
203
  readonly probabilityDecimals: Schema.optional<Schema.Int>;
204
204
  readonly scoreDecimals: Schema.optional<Schema.Int>;
@@ -230,9 +230,10 @@ export declare class EvaluationResponse extends EvaluationResponse_base {
230
230
  export type EvaluationResponseFor<Questions extends EvaluationQuestions> = Omit<EvaluationResponse, "answers"> & {
231
231
  readonly answers: AnswersFor<Questions>;
232
232
  };
233
- export declare function request<const Model extends object, const Questions extends EvaluationQuestions>(input: EvaluationRequestInput<Model, Questions>): EvaluationRequestFor<EvaluationModelOptions<Model>, Questions>;
233
+ export declare function request<const Model extends EvaluationModel, const Questions extends EvaluationQuestions>(input: EvaluationRequestInput<Model, Questions>): EvaluationRequestFor<EvaluationModelOptions<Model>, Questions>;
234
234
  export declare function request(input: EvaluationRequest): EvaluationRequest;
235
- export declare function run<const Model extends object, const Questions extends EvaluationQuestions>(input: EvaluationRequestInput<Model, Questions>): Effect.Effect<EvaluationResponseFor<Questions>, AIError, Service>;
235
+ export declare function request(input: EvaluationRequest | EvaluationRequestInput): EvaluationRequest;
236
+ export declare function run<const Model extends EvaluationModel, const Questions extends EvaluationQuestions>(input: EvaluationRequestInput<Model, Questions>): Effect.Effect<EvaluationResponseFor<Questions>, AIError, Service>;
236
237
  export declare function run(input: EvaluationRequest): Effect.Effect<EvaluationResponse, AIError, Service>;
237
238
  export declare const Evaluation: {
238
239
  readonly request: typeof request;
@@ -95,20 +95,19 @@ export function request(input) {
95
95
  return input;
96
96
  return new EvaluationRequest({
97
97
  ...input,
98
- model: input.model,
99
98
  http: HttpOptions.make(input.http),
100
99
  });
101
100
  }
102
101
  export function run(input) {
103
102
  return Effect.try({
104
- try: () => (input instanceof EvaluationRequest ? input : request(input)),
103
+ try: () => request(input),
105
104
  catch: (cause) => new AIError({
106
105
  reason: new InvalidRequestError({
107
106
  message: cause instanceof Error ? cause.message : String(cause),
108
107
  cause,
109
108
  }),
110
109
  }),
111
- }).pipe(Effect.flatMap((request) => EvaluationClient.evaluate(request)));
110
+ }).pipe(Effect.flatMap(EvaluationClient.evaluate));
112
111
  }
113
112
  export const Evaluation = {
114
113
  request,
@@ -7,6 +7,7 @@ export type RealtimeTransportInput = {
7
7
  } | {
8
8
  readonly type: "websocket";
9
9
  };
10
+ export type RealtimeTransportType = RealtimeTransportInput["type"];
10
11
  export type RealtimeTransport = {
11
12
  readonly type: "webrtc";
12
13
  readonly sdp: string;
@@ -23,25 +24,83 @@ export interface RealtimeConnectInput {
23
24
  readonly session?: RealtimeSession;
24
25
  readonly transport: RealtimeTransportInput;
25
26
  }
27
+ export declare const engines: readonly [{
28
+ readonly family: "gpt-live";
29
+ readonly model: "gpt-live-1";
30
+ readonly id: "openai-live";
31
+ readonly provider: "openai";
32
+ readonly transports: readonly ["webrtc"];
33
+ }, {
34
+ readonly family: "gpt-realtime";
35
+ readonly model: "gpt-realtime-2.1";
36
+ readonly id: "openai-realtime";
37
+ readonly provider: "openai";
38
+ readonly transports: readonly ["webrtc", "websocket"];
39
+ }, {
40
+ readonly family: "grok-voice";
41
+ readonly model: "grok-voice-3.0";
42
+ readonly id: "xai-realtime";
43
+ readonly provider: "xai";
44
+ readonly transports: readonly ["websocket"];
45
+ }, {
46
+ readonly family: "gemini-3.8-live";
47
+ readonly model: "gemini-3.8-live";
48
+ readonly id: "gemini-live";
49
+ readonly provider: "google";
50
+ readonly transports: readonly ["websocket"];
51
+ }];
26
52
  export declare const engine: (model: string) => {
27
53
  readonly family: "gpt-live";
54
+ readonly model: "gpt-live-1";
28
55
  readonly id: "openai-live";
29
56
  readonly provider: "openai";
57
+ readonly transports: readonly ["webrtc"];
30
58
  } | {
31
59
  readonly family: "gpt-realtime";
60
+ readonly model: "gpt-realtime-2.1";
32
61
  readonly id: "openai-realtime";
33
62
  readonly provider: "openai";
63
+ readonly transports: readonly ["webrtc", "websocket"];
34
64
  } | {
35
65
  readonly family: "grok-voice";
66
+ readonly model: "grok-voice-3.0";
36
67
  readonly id: "xai-realtime";
37
68
  readonly provider: "xai";
69
+ readonly transports: readonly ["websocket"];
38
70
  } | {
39
71
  readonly family: "gemini-3.8-live";
72
+ readonly model: "gemini-3.8-live";
40
73
  readonly id: "gemini-live";
41
74
  readonly provider: "google";
75
+ readonly transports: readonly ["websocket"];
42
76
  } | undefined;
43
77
  export declare const connect: (input: RealtimeConnectInput) => Effect.Effect<RealtimeTransport, AIError, import("../route/executor-service.js").RequestExecutorService>;
44
78
  export declare const Realtime: {
79
+ readonly engines: readonly [{
80
+ readonly family: "gpt-live";
81
+ readonly model: "gpt-live-1";
82
+ readonly id: "openai-live";
83
+ readonly provider: "openai";
84
+ readonly transports: readonly ["webrtc"];
85
+ }, {
86
+ readonly family: "gpt-realtime";
87
+ readonly model: "gpt-realtime-2.1";
88
+ readonly id: "openai-realtime";
89
+ readonly provider: "openai";
90
+ readonly transports: readonly ["webrtc", "websocket"];
91
+ }, {
92
+ readonly family: "grok-voice";
93
+ readonly model: "grok-voice-3.0";
94
+ readonly id: "xai-realtime";
95
+ readonly provider: "xai";
96
+ readonly transports: readonly ["websocket"];
97
+ }, {
98
+ readonly family: "gemini-3.8-live";
99
+ readonly model: "gemini-3.8-live";
100
+ readonly id: "gemini-live";
101
+ readonly provider: "google";
102
+ readonly transports: readonly ["websocket"];
103
+ }];
45
104
  readonly engine: typeof engine;
46
105
  readonly connect: (input: RealtimeConnectInput) => Effect.Effect<RealtimeTransport, AIError, import("../route/executor-service.js").RequestExecutorService>;
47
106
  };
@@ -1,16 +1,28 @@
1
1
  import { Clock, Effect, Redacted, Schema } from "effect";
2
2
  import { HttpClientRequest } from "effect/http";
3
3
  import { RequestExecutor } from "../route/executor.js";
4
- import { AIError, HttpContext, InvalidProviderOutputError, InvalidRequestError } from "../schema/index.js";
4
+ import { AIError, InvalidProviderOutputError, InvalidRequestError } from "../schema/index.js";
5
5
  import { encodeJson } from "../utils/json.js";
6
6
  const OPENAI_BASE_URL = "https://api.openai.com/v1";
7
7
  const XAI_BASE_URL = "https://api.x.ai/v1";
8
8
  const GOOGLE_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
9
- const engines = [
10
- { family: "gpt-live", id: "openai-live", provider: "openai" },
11
- { family: "gpt-realtime", id: "openai-realtime", provider: "openai" },
12
- { family: "grok-voice", id: "xai-realtime", provider: "xai" },
13
- { family: "gemini-3.8-live", id: "gemini-live", provider: "google" },
9
+ export const engines = [
10
+ { family: "gpt-live", model: "gpt-live-1", id: "openai-live", provider: "openai", transports: ["webrtc"] },
11
+ {
12
+ family: "gpt-realtime",
13
+ model: "gpt-realtime-2.1",
14
+ id: "openai-realtime",
15
+ provider: "openai",
16
+ transports: ["webrtc", "websocket"],
17
+ },
18
+ { family: "grok-voice", model: "grok-voice-3.0", id: "xai-realtime", provider: "xai", transports: ["websocket"] },
19
+ {
20
+ family: "gemini-3.8-live",
21
+ model: "gemini-3.8-live",
22
+ id: "gemini-live",
23
+ provider: "google",
24
+ transports: ["websocket"],
25
+ },
14
26
  ];
15
27
  // Matches the bare family name (`gpt-realtime`) and its versions (`gpt-realtime-2.1`, `gpt-realtime-mini`).
16
28
  export const engine = (model) => engines.find((item) => model === item.family || model.startsWith(`${item.family}-`));
@@ -18,17 +30,20 @@ export const connect = Effect.fn("Realtime.connect")(function* (input) {
18
30
  const resolved = engine(input.model);
19
31
  if (!resolved)
20
32
  return yield* invalidRequest(`Unsupported voice model: ${input.model}`);
33
+ if (!resolved.transports.some((type) => type === input.transport.type))
34
+ return yield* invalidRequest(`${input.model} only supports ${resolved.transports.join(", ")} transport`);
21
35
  const session = { ...input.session, model: input.model };
22
36
  switch (resolved.id) {
23
37
  case "openai-live": {
24
- if (input.transport.type !== "webrtc")
25
- return yield* invalidRequest("OpenAI GPT-Live only supports WebRTC transport");
26
38
  const data = yield* post(resolved.id, `${OPENAI_BASE_URL}/live/sessions`, input.apiKey, { session, transport: input.transport }, LiveSessionResponse);
27
39
  return { type: "webrtc", sdp: data.transport.sdp };
28
40
  }
29
41
  case "openai-realtime": {
30
42
  if (input.transport.type === "websocket")
31
- return yield* clientSecret(resolved.id, OPENAI_BASE_URL, input.model, input.apiKey, session);
43
+ return yield* clientSecret(resolved.id, OPENAI_BASE_URL, input.model, input.apiKey, {
44
+ expires_after: { anchor: "created_at", seconds: 60 },
45
+ session,
46
+ });
32
47
  const form = new FormData();
33
48
  form.append("sdp", input.transport.sdp);
34
49
  form.append("session", encodeJson(session));
@@ -36,14 +51,11 @@ export const connect = Effect.fn("Realtime.connect")(function* (input) {
36
51
  const sdp = yield* post(resolved.id, `${OPENAI_BASE_URL}/realtime/calls${query}`, input.apiKey, form, Schema.String);
37
52
  return { type: "webrtc", sdp };
38
53
  }
39
- case "xai-realtime": {
40
- if (input.transport.type !== "websocket")
41
- return yield* invalidRequest("xAI Grok Voice only supports WebSocket transport");
42
- return yield* clientSecret(resolved.id, XAI_BASE_URL, input.model, input.apiKey);
43
- }
54
+ case "xai-realtime":
55
+ return yield* clientSecret(resolved.id, XAI_BASE_URL, input.model, input.apiKey, {
56
+ expires_after: { seconds: 60 },
57
+ });
44
58
  case "gemini-live": {
45
- if (input.transport.type !== "websocket")
46
- return yield* invalidRequest("Google Gemini Live only supports WebSocket transport");
47
59
  const now = yield* Clock.currentTimeMillis;
48
60
  const expiresAt = now + 60_000;
49
61
  const data = yield* post(resolved.id, `${GOOGLE_BASE_URL}/auth_tokens`, input.apiKey, {
@@ -62,6 +74,7 @@ export const connect = Effect.fn("Realtime.connect")(function* (input) {
62
74
  }
63
75
  });
64
76
  export const Realtime = {
77
+ engines,
65
78
  engine,
66
79
  connect,
67
80
  };
@@ -69,7 +82,7 @@ const LiveSessionResponse = Schema.fromJsonString(Schema.Struct({
69
82
  transport: Schema.Struct({ sdp: Schema.String }),
70
83
  }));
71
84
  // xAI's ephemeral-token docs (https://docs.x.ai/developers/model-capabilities/audio/ephemeral-tokens)
72
- // document the request body (`{ expires_after: { seconds } }`) but not the response payload; we assume
85
+ // document the request body (`{ expires_after: { seconds } }`, no `anchor` or `session`) but not the response payload; we assume
73
86
  // it mirrors OpenAI's `{ value, expires_at }` response shape.
74
87
  const ClientSecretResponse = Schema.fromJsonString(Schema.Struct({
75
88
  value: Schema.String,
@@ -86,12 +99,12 @@ const post = (route, url, apiKey, body, response, authHeader) => Effect.gen(func
86
99
  : HttpClientRequest.bearerToken(apiKey), body instanceof FormData
87
100
  ? HttpClientRequest.bodyFormData(body)
88
101
  : HttpClientRequest.bodyText(encodeJson(body), "application/json")));
89
- const http = new HttpContext({ url: res.request.url, status: res.status, headers: res.headers });
102
+ const http = RequestExecutor.responseHttp(res);
90
103
  const fail = (message, cause, raw) => new AIError({ reason: new InvalidProviderOutputError({ route, message, body: raw, http, cause }) });
91
104
  const text = yield* res.text.pipe(Effect.mapError((cause) => fail(`Failed to read ${route} response`, cause)));
92
105
  return yield* Schema.decodeEffect(response)(text).pipe(Effect.mapError((cause) => fail(`${route} returned an invalid response`, cause, text)));
93
106
  });
94
- const clientSecret = (route, baseURL, model, apiKey, session) => post(route, `${baseURL}/realtime/client_secrets`, apiKey, { expires_after: { seconds: 60 }, session }, ClientSecretResponse).pipe(Effect.map((data) => ({
107
+ const clientSecret = (route, baseURL, model, apiKey, body) => post(route, `${baseURL}/realtime/client_secrets`, apiKey, body, ClientSecretResponse).pipe(Effect.map((data) => ({
95
108
  type: "websocket",
96
109
  url: `${baseURL.replace(/^http/, "ws")}/realtime?model=${encodeURIComponent(model)}`,
97
110
  token: data.value,
@@ -2,7 +2,9 @@ import { Effect, Schema } from "effect";
2
2
  import { Headers, HttpClientRequest } from "effect/http";
3
3
  import { ChoiceQuestion, EvaluationInput, EvaluationModel, EvaluationResponse, EvaluationRounding, ScoreQuestion, } from "./evaluation.js";
4
4
  import { Auth } from "../route/auth.js";
5
- import { AIError, HttpContext, HttpOptions, InvalidProviderOutputError, InvalidRequestError, ModelID, Usage, mergeJsonRecords, } from "../schema/index.js";
5
+ import { RequestExecutor } from "../route/executor.js";
6
+ import { JsonObject, ProviderShared } from "../protocols/shared.js";
7
+ import { AIError, HttpOptions, InvalidProviderOutputError, InvalidRequestError, ModelID, ProviderMetadata, Usage, mergeJsonRecords, } from "../schema/index.js";
6
8
  const Noul = Schema.Struct({
7
9
  type: Schema.Literal("noul"),
8
10
  instructions: EvaluationInput,
@@ -24,7 +26,7 @@ const Request = Schema.StructWithRest(Schema.Struct({
24
26
  model: Schema.String,
25
27
  state: EvaluationInput,
26
28
  questions: Schema.Record(Schema.String, Question),
27
- }), [Schema.Record(Schema.String, Schema.Any)]);
29
+ }), [JsonObject]);
28
30
  const Probability = Schema.Number.check(Schema.isBetween({ minimum: 0, maximum: 1 }));
29
31
  const NoulAnswer = Schema.Struct({ type: Schema.Literal("noul"), noul: Probability });
30
32
  const Choice = Schema.Struct({
@@ -44,14 +46,14 @@ const Answer = Schema.Union([NoulAnswer, Choice, Score]).pipe(Schema.toTaggedUni
44
46
  const NativeUsage = Schema.StructWithRest(Schema.Struct({
45
47
  input_tokens: Schema.optional(Schema.Number),
46
48
  output_tokens: Schema.optional(Schema.Number),
47
- }), [Schema.Record(Schema.String, Schema.Unknown)]);
49
+ }), [JsonObject]);
48
50
  const Response = Schema.Struct({
49
51
  model: Schema.String,
50
52
  answers: Schema.Record(Schema.String, Answer),
51
53
  usage: Schema.optional(NativeUsage),
52
54
  id: Schema.optional(Schema.String),
53
55
  provider: Schema.optional(Schema.String),
54
- provider_metadata: Schema.optional(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Unknown))),
56
+ provider_metadata: Schema.optional(ProviderMetadata),
55
57
  });
56
58
  export const model = (cfg) => EvaluationModel.make({
57
59
  id: cfg.id,
@@ -76,7 +78,7 @@ export const model = (cfg) => EvaluationModel.make({
76
78
  headers: Headers.fromInput({ ...cfg.headers, ...req.http?.headers }),
77
79
  });
78
80
  const res = yield* send(HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.bodyText(body, "application/json")));
79
- const http = new HttpContext({ url: res.request.url, status: res.status, headers: res.headers });
81
+ const http = RequestExecutor.responseHttp(res);
80
82
  const fail = (message, cause, body) => new AIError({ reason: new InvalidProviderOutputError({ route: "system-one", message, body, http, cause }) });
81
83
  const text = yield* res.text.pipe(Effect.mapError((cause) => fail("Failed to read the System One response", cause)));
82
84
  const data = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(Response))(text).pipe(Effect.mapError((cause) => fail("System One returned an invalid response", cause, text)));
@@ -120,9 +122,7 @@ export const model = (cfg) => EvaluationModel.make({
120
122
  ? new Usage({
121
123
  inputTokens: data.usage.input_tokens,
122
124
  outputTokens: data.usage.output_tokens,
123
- totalTokens: data.usage.input_tokens === undefined && data.usage.output_tokens === undefined
124
- ? undefined
125
- : (data.usage.input_tokens ?? 0) + (data.usage.output_tokens ?? 0),
125
+ totalTokens: ProviderShared.totalTokens(data.usage.input_tokens, data.usage.output_tokens, undefined),
126
126
  providerMetadata: { [cfg.providerMetadataKey]: data.usage },
127
127
  })
128
128
  : undefined,
@@ -2,4 +2,4 @@ export { EvaluationClient } from "./experimental/evaluation-client.js";
2
2
  export { BooleanAnswer, BooleanQuestion, ChoiceAnswer, ChoiceQuestion, Evaluation, EvaluationAnswer, EvaluationInput, EvaluationModel, EvaluationModelSchema, EvaluationQuestion, EvaluationRequest, EvaluationResponse, EvaluationRounding, ScoreAnswer, ScoreQuestion, } from "./experimental/evaluation.js";
3
3
  export type { AnswerFor, AnswersFor, EvaluationModelOptions, EvaluationOptions, EvaluationQuestions, EvaluationRequestFor, EvaluationRequestInput, EvaluationResponseFor, EvaluationRoute, } from "./experimental/evaluation.js";
4
4
  export { Realtime } from "./experimental/realtime.js";
5
- export type { RealtimeConnectInput, RealtimeSession, RealtimeTransport, RealtimeTransportInput, } from "./experimental/realtime.js";
5
+ export type { RealtimeConnectInput, RealtimeSession, RealtimeTransport, RealtimeTransportInput, RealtimeTransportType, } from "./experimental/realtime.js";
@@ -110,6 +110,6 @@ const within = (effect, id, timeout, deadline) => Clock.currentTimeMillis.pipe(E
110
110
  const timeoutError = (id, timeout) => new AIError({
111
111
  reason: new TimeoutError({
112
112
  message: `Generation ${id} did not finish within ${Duration.format(timeout)}`,
113
- timeoutMs: Duration.toMillis(timeout),
113
+ timeoutMs: Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, Math.ceil(Duration.toMillis(timeout)))),
114
114
  }),
115
115
  });
@@ -57,7 +57,9 @@ export declare const ImageClient: {
57
57
  } | undefined;
58
58
  } | import("./generation.js").Observation, import("./index.js").AIError, ImageClientService>;
59
59
  readonly start: (request: ImageRequestFor) => import("effect/Effect").Effect<import("./generation.js").Generation<ImageResponse>, import("./index.js").AIError, ImageClientService>;
60
- readonly resume: (model: import("./image.js").ImageModel<import("./media-model.js").MediaModel.Options>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<ImageResponse>, import("./index.js").AIError, ImageClientService>;
60
+ readonly resume: (model: import("./image.js").ImageModel<{
61
+ readonly [x: string]: unknown;
62
+ }>, token: unknown) => import("effect/Effect").Effect<import("./generation.js").Generation<ImageResponse>, import("./index.js").AIError, ImageClientService>;
61
63
  readonly Service: typeof ImageClientService;
62
64
  };
63
65
  export {};
package/dist/image.d.ts CHANGED
@@ -14,14 +14,22 @@ export declare class ImageModel<Options extends ImageOptions = ImageOptions> ext
14
14
  static fromRoute<Options extends ImageOptions, Frame, State>(route: MediaModel.StreamRouteInput<ImageRequestFor<Options>, ImageEvent, Frame, State>, input: MediaRoute.ModelInput): ImageModel<Options>;
15
15
  static fromRoute<Options extends ImageOptions, Token>(route: MediaModel.QueuedRouteInput<ImageRequestFor<Options>, ImageResponse, Token>, input: MediaRoute.ModelInput): ImageModel<Options>;
16
16
  }
17
- export declare const ImageModelSchema: Schema.declare<ImageModel<MediaModel.Options>, ImageModel<MediaModel.Options>>;
17
+ export declare const ImageModelSchema: Schema.declare<ImageModel<{
18
+ readonly [x: string]: unknown;
19
+ }>, ImageModel<{
20
+ readonly [x: string]: unknown;
21
+ }>>;
18
22
  export type ImageSize = `${number}x${number}`;
19
23
  export declare const ImageSize: Schema.declare<`${number}x${number}`, `${number}x${number}`>;
20
24
  export type ImageAspectRatio = Media.AspectRatio;
21
25
  export declare const ImageAspectRatio: Schema.declare<`${number}:${number}`, `${number}:${number}`>;
22
26
  export type ImageFormat = OpenString<"png" | "jpeg" | "webp">;
23
27
  declare const ImageRequest_base: Schema.Class<ImageRequest, Schema.Struct<{
24
- readonly model: Schema.declare<ImageModel<MediaModel.Options>, ImageModel<MediaModel.Options>>;
28
+ readonly model: Schema.declare<ImageModel<{
29
+ readonly [x: string]: unknown;
30
+ }>, ImageModel<{
31
+ readonly [x: string]: unknown;
32
+ }>>;
25
33
  readonly prompt: Schema.String;
26
34
  /** Edit sources or style/subject references, in order. */
27
35
  readonly images: Schema.optional<Schema.$Array<Schema.decodeTo<Schema.declare<Media.Asset, Media.Asset>, Schema.Struct<{
package/dist/image.js CHANGED
@@ -3,7 +3,7 @@ import { Generation, ProgressEvent, QueuedEvent } from "./generation.js";
3
3
  import { Media } from "./media.js";
4
4
  import { MediaModel, composeRoute, tryRequest } from "./media-model.js";
5
5
  import { MediaRoute } from "./route/media.js";
6
- import { AIError, HttpOptions, MediaUsage, ProviderMetadata } from "./schema/index.js";
6
+ import { AIError, HttpOptions, MediaUsage, ProviderMetadata, ProviderOptions, } from "./schema/index.js";
7
7
  import { ImageClient, Service } from "./image-client.js";
8
8
  export class ImageModel extends MediaModel {
9
9
  static fromRoute(route, input) {
@@ -32,7 +32,7 @@ export class ImageRequest extends Schema.Class("Image.Request")({
32
32
  aspectRatio: Schema.optional(ImageAspectRatio),
33
33
  seed: Schema.optional(Schema.Number),
34
34
  format: Schema.optional(Schema.String),
35
- providerOptions: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
35
+ providerOptions: Schema.optional(ProviderOptions),
36
36
  http: Schema.optional(HttpOptions),
37
37
  }) {
38
38
  }
package/dist/llm.d.ts CHANGED
@@ -14,10 +14,8 @@ export type RequestInput<SelectedLanguageModel extends LanguageModel = LanguageM
14
14
  readonly providerOptions?: NoInfer<LanguageModelProviderOptions<SelectedLanguageModel>>;
15
15
  readonly http?: HttpOptions.Input;
16
16
  };
17
- export declare function generate<const Model extends LanguageModel>(input: RequestInput<Model>, options?: StreamOptions): Effect.Effect<LLMResponse, AIError, Service>;
18
- export declare function generate(input: LLMRequest, options?: StreamOptions): Effect.Effect<LLMResponse, AIError, Service>;
19
- export declare function stream<const Model extends LanguageModel>(input: RequestInput<Model>, options?: StreamOptions): Stream.Stream<LLMEvent, AIError, Service>;
20
- export declare function stream(input: LLMRequest, options?: StreamOptions): Stream.Stream<LLMEvent, AIError, Service>;
17
+ export declare function generate<const Model extends LanguageModel>(input: RequestInput<Model> | LLMRequest, options?: StreamOptions): Effect.Effect<LLMResponse, AIError, Service>;
18
+ export declare function stream<const Model extends LanguageModel>(input: RequestInput<Model> | LLMRequest, options?: StreamOptions): Stream.Stream<LLMEvent, AIError, Service>;
21
19
  export declare const request: <const SelectedLanguageModel extends LanguageModel>(input: RequestInput<SelectedLanguageModel>) => LLMRequest<SelectedLanguageModel>;
22
20
  type GenerateObjectBase<SelectedLanguageModel extends LanguageModel = LanguageModel> = Omit<RequestInput<SelectedLanguageModel>, "tools" | "toolChoice">;
23
21
  export declare class GenerateObjectResponse<T> {
package/dist/llm.js CHANGED
@@ -46,8 +46,8 @@ const runGenerateObject = Effect.fn("LLM.generateObject")(function* (options, to
46
46
  toolChoice: ToolChoice.named(GENERATE_OBJECT_TOOL_NAME),
47
47
  });
48
48
  const response = yield* LLMClient.generate(generateRequest);
49
- const call = response.toolCalls.find((event) => LLMEvent.is.toolCall(event) && event.name === GENERATE_OBJECT_TOOL_NAME);
50
- if (!call || !LLMEvent.is.toolCall(call))
49
+ const call = response.toolCalls.find((event) => event.name === GENERATE_OBJECT_TOOL_NAME);
50
+ if (!call)
51
51
  return yield* new AIError({
52
52
  reason: new InvalidProviderOutputError({
53
53
  message: `generateObject: model did not call the forced \`${GENERATE_OBJECT_TOOL_NAME}\` tool`,
@@ -2,7 +2,7 @@ import { Effect } from "effect";
2
2
  import { Endpoint } from "./route/endpoint.js";
3
3
  import { MediaRoute } from "./route/media.js";
4
4
  import type { MediaProtocol } from "./route/media-protocol.js";
5
- import { AIError, HttpOptions, ModelID, ProviderID } from "./schema/index.js";
5
+ import { AIError, HttpOptions, ModelID, ProviderID, type ProviderOptions } from "./schema/index.js";
6
6
  /**
7
7
  * What every media model carries: ids, the configured route, and deployment `http` overlays. Modality classes
8
8
  * (`ImageModel`, `VideoModel`, `SpeechModel`, `TranscriptionModel`) extend it with their route type and a nominal
@@ -17,7 +17,7 @@ export declare class MediaModel<Route, Options> {
17
17
  constructor(input: MediaModel.Input<Route>);
18
18
  }
19
19
  export declare namespace MediaModel {
20
- type Options = Record<string, unknown>;
20
+ type Options = ProviderOptions;
21
21
  interface Input<Route> {
22
22
  readonly id: string | ModelID;
23
23
  readonly provider: string | ProviderID;
@@ -1,7 +1,7 @@
1
1
  import { Effect } from "effect";
2
2
  import { Endpoint } from "./route/endpoint.js";
3
3
  import { MediaRoute } from "./route/media.js";
4
- import { AIError, HttpOptions, InvalidRequestError, ModelID, ProviderID } from "./schema/index.js";
4
+ import { AIError, HttpOptions, InvalidRequestError, ModelID, ProviderID, } from "./schema/index.js";
5
5
  /**
6
6
  * What every media model carries: ids, the configured route, and deployment `http` overlays. Modality classes
7
7
  * (`ImageModel`, `VideoModel`, `SpeechModel`, `TranscriptionModel`) extend it with their route type and a nominal
package/dist/media.js CHANGED
@@ -3,9 +3,9 @@ import { Effect, FileSystem, Schema, SchemaGetter } from "effect";
3
3
  import { Base64 } from "effect/encoding";
4
4
  import { HttpClientRequest } from "effect/http";
5
5
  import { ProviderID } from "./schema/ids.js";
6
- import { AIError, HttpContext, InvalidProviderOutputError, InvalidRequestError } from "./schema/errors.js";
6
+ import { AIError, InvalidProviderOutputError, InvalidRequestError } from "./schema/errors.js";
7
7
  import { ProviderMetadata } from "./schema/options.js";
8
- import { Service } from "./route/executor-service.js";
8
+ import { responseHttp, Service } from "./route/executor-service.js";
9
9
  import { detectMediaType, fileMediaType } from "./utils/media-type.js";
10
10
  export { detectMediaType } from "./utils/media-type.js";
11
11
  const OCTET_STREAM = "application/octet-stream";
@@ -183,7 +183,7 @@ const download = Effect.fn("Media.download")(function* (source, headers) {
183
183
  const buffer = yield* response.arrayBuffer.pipe(Effect.mapError((cause) => new AIError({
184
184
  reason: new InvalidProviderOutputError({
185
185
  message: `Failed to read media from ${source.url}`,
186
- http: new HttpContext({ url: response.request.url, status: response.status, headers: response.headers }),
186
+ http: responseHttp(response),
187
187
  cause,
188
188
  }),
189
189
  })));
package/dist/promise.d.ts CHANGED
@@ -5,7 +5,7 @@ import { type ImageModel, type ImageRequest, type ImageRequestInput } from "./im
5
5
  import { LLM } from "./index.js";
6
6
  import { Media } from "./media.js";
7
7
  import { RequestExecutor } from "./route/executor.js";
8
- import { LanguageModel, LLMRequest } from "./schema/index.js";
8
+ import { LanguageModel, type LLMRequest } from "./schema/index.js";
9
9
  import type { RequestInput } from "./llm.js";
10
10
  import { type SpeechModel, type SpeechRequest, type SpeechRequestInput } from "./speech.js";
11
11
  import { type TranscriptionModel, type TranscriptionRequest, type TranscriptionRequestInput } from "./transcription.js";
package/dist/promise.js CHANGED
@@ -3,9 +3,8 @@ import { AIClient } from "./ai-client.js";
3
3
  import { Image } from "./image.js";
4
4
  import { LLM } from "./index.js";
5
5
  import { Media } from "./media.js";
6
- import { tryRequest } from "./media-model.js";
7
6
  import { RequestExecutor } from "./route/executor.js";
8
- import { AIError, InvalidRequestError, LanguageModel, LLMRequest } from "./schema/index.js";
7
+ import { AIError, InvalidRequestError, LanguageModel } from "./schema/index.js";
9
8
  import { Speech } from "./speech.js";
10
9
  import { Transcription, } from "./transcription.js";
11
10
  import { fileMediaType } from "./utils/media-type.js";
@@ -36,7 +35,6 @@ export const make = (options = {}) => {
36
35
  refresh: (options) => run(generation.refresh(), options).then(handle),
37
36
  cancel: (options) => run(generation.cancel(), options),
38
37
  });
39
- const llmRequest = (input) => input instanceof LLMRequest ? Effect.succeed(input) : tryRequest(() => LLM.request(input));
40
38
  return {
41
39
  run,
42
40
  /** Decoded asset bytes, downloading `url` sources through the executor. */
@@ -65,8 +63,8 @@ export const make = (options = {}) => {
65
63
  },
66
64
  llm: {
67
65
  request: LLM.request,
68
- generate: (input, options) => run(Effect.flatMap(llmRequest(input), LLM.generate), options),
69
- stream: (input, options) => iterate(Stream.unwrap(Effect.map(llmRequest(input), LLM.stream)), options),
66
+ generate: (input, options) => run(LLM.generate(input), options),
67
+ stream: (input, options) => iterate(LLM.stream(input), options),
70
68
  },
71
69
  image: {
72
70
  request: Image.request,
@@ -10,19 +10,19 @@ declare const Options: Schema.Struct<{
10
10
  readonly preserveThinking: Schema.optional<Schema.Boolean>;
11
11
  readonly clearThinking: Schema.optional<Schema.Boolean>;
12
12
  readonly thinking: Schema.optional<Schema.Struct<{
13
- readonly type: Schema.declare<"adaptive" | "disabled" | (string & {}), "adaptive" | "disabled" | (string & {})>;
13
+ readonly type: Schema.declare<import("../schema/options.js").OpenString<"adaptive" | "disabled">, import("../schema/options.js").OpenString<"adaptive" | "disabled">>;
14
14
  }>>;
15
15
  readonly toolStream: Schema.optional<Schema.Boolean>;
16
16
  readonly parallelToolCalls: Schema.optional<Schema.Boolean>;
17
17
  readonly repetitionPenalty: Schema.optional<Schema.Number>;
18
18
  readonly responseFormat: Schema.optional<Schema.Struct<{
19
- readonly type: Schema.declare<"json_object" | "json_schema" | "text" | (string & {}), "json_object" | "json_schema" | "text" | (string & {})>;
19
+ readonly type: Schema.declare<import("../schema/options.js").OpenString<"json_object" | "json_schema" | "text">, import("../schema/options.js").OpenString<"json_object" | "json_schema" | "text">>;
20
20
  readonly json_schema: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
21
21
  }>>;
22
22
  readonly enableSearch: Schema.optional<Schema.Boolean>;
23
23
  readonly searchOptions: Schema.optional<Schema.Struct<{
24
24
  readonly forced_search: Schema.optional<Schema.Boolean>;
25
- readonly search_strategy: Schema.optional<Schema.declare<"agent" | "agent_max" | "max" | "turbo" | (string & {}), "agent" | "agent_max" | "max" | "turbo" | (string & {})>>;
25
+ readonly search_strategy: Schema.optional<Schema.declare<import("../schema/options.js").OpenString<"agent" | "agent_max" | "max" | "turbo">, import("../schema/options.js").OpenString<"agent" | "agent_max" | "max" | "turbo">>>;
26
26
  readonly enable_search_extension: Schema.optional<Schema.Boolean>;
27
27
  }>>;
28
28
  }>;
@@ -159,13 +159,13 @@ export declare const protocol: Protocol<{
159
159
  readonly preserve_thinking?: boolean | undefined;
160
160
  readonly clear_thinking?: boolean | undefined;
161
161
  readonly thinking?: {
162
- readonly type: "adaptive" | "disabled" | (string & {});
162
+ readonly type: import("../schema/options.js").OpenString<"adaptive" | "disabled">;
163
163
  } | undefined;
164
164
  readonly parallel_tool_calls?: boolean | undefined;
165
165
  readonly repetition_penalty?: number | undefined;
166
166
  readonly top_k?: number | undefined;
167
167
  readonly response_format?: {
168
- readonly type: "json_object" | "json_schema" | "text" | (string & {});
168
+ readonly type: import("../schema/options.js").OpenString<"json_object" | "json_schema" | "text">;
169
169
  readonly json_schema?: {
170
170
  readonly [x: string]: unknown;
171
171
  } | undefined;
@@ -173,7 +173,7 @@ export declare const protocol: Protocol<{
173
173
  readonly enable_search?: boolean | undefined;
174
174
  readonly search_options?: {
175
175
  readonly forced_search?: boolean | undefined;
176
- readonly search_strategy?: "agent" | "agent_max" | "max" | "turbo" | (string & {}) | undefined;
176
+ readonly search_strategy?: import("../schema/options.js").OpenString<"agent" | "agent_max" | "max" | "turbo"> | undefined;
177
177
  readonly enable_search_extension?: boolean | undefined;
178
178
  } | undefined;
179
179
  }, string, "[DONE]" | {
@@ -11,19 +11,19 @@ const Options = Schema.Struct({
11
11
  preserveThinking: Schema.optional(Schema.Boolean),
12
12
  clearThinking: Schema.optional(Schema.Boolean),
13
13
  thinking: Schema.optional(Schema.Struct({
14
- type: Schema.declare(Schema.is(Schema.String)),
14
+ type: ProviderShared.knownString(),
15
15
  })),
16
16
  toolStream: Schema.optional(Schema.Boolean),
17
17
  parallelToolCalls: Schema.optional(Schema.Boolean),
18
18
  repetitionPenalty: Schema.optional(Schema.Number),
19
19
  responseFormat: Schema.optional(Schema.Struct({
20
- type: Schema.declare(Schema.is(Schema.String)),
20
+ type: ProviderShared.knownString(),
21
21
  json_schema: Schema.optional(JsonObject),
22
22
  })),
23
23
  enableSearch: Schema.optional(Schema.Boolean),
24
24
  searchOptions: Schema.optional(Schema.Struct({
25
25
  forced_search: Schema.optional(Schema.Boolean),
26
- search_strategy: Schema.optional(Schema.declare(Schema.is(Schema.String))),
26
+ search_strategy: Schema.optional(ProviderShared.knownString()),
27
27
  enable_search_extension: Schema.optional(Schema.Boolean),
28
28
  })),
29
29
  });