@effect/ai-openai 4.0.0-beta.50 → 4.0.0-beta.52

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.
@@ -19,7 +19,7 @@ import * as Schema from "effect/Schema"
19
19
  import * as AST from "effect/SchemaAST"
20
20
  import * as Stream from "effect/Stream"
21
21
  import type { Span } from "effect/Tracer"
22
- import type { DeepMutable, Simplify } from "effect/Types"
22
+ import type { DeepMutable, Mutable, Simplify } from "effect/Types"
23
23
  import * as AiError from "effect/unstable/ai/AiError"
24
24
  import * as IdGenerator from "effect/unstable/ai/IdGenerator"
25
25
  import * as LanguageModel from "effect/unstable/ai/LanguageModel"
@@ -33,6 +33,7 @@ import type * as HttpClientResponse from "effect/unstable/http/HttpClientRespons
33
33
  import * as Generated from "./Generated.ts"
34
34
  import * as InternalUtilities from "./internal/utilities.ts"
35
35
  import { OpenAiClient } from "./OpenAiClient.ts"
36
+ import type * as OpenAiSchema from "./OpenAiSchema.ts"
36
37
  import { addGenAIAnnotations } from "./OpenAiTelemetry.ts"
37
38
  import type * as OpenAiTool from "./OpenAiTool.ts"
38
39
 
@@ -65,7 +66,7 @@ export class Config extends Context.Service<
65
66
  Simplify<
66
67
  & Partial<
67
68
  Omit<
68
- typeof Generated.CreateResponse.Encoded,
69
+ typeof OpenAiSchema.CreateResponse.Encoded,
69
70
  "input" | "tools" | "tool_choice" | "stream" | "text"
70
71
  >
71
72
  >
@@ -140,7 +141,7 @@ declare module "effect/unstable/ai/Prompt" {
140
141
  /**
141
142
  * The status of item.
142
143
  */
143
- readonly status?: typeof Generated.Message.Encoded["status"] | null
144
+ readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
144
145
  /**
145
146
  * The ID of the approval request.
146
147
  */
@@ -157,7 +158,7 @@ declare module "effect/unstable/ai/Prompt" {
157
158
  /**
158
159
  * The status of item.
159
160
  */
160
- readonly status?: typeof Generated.Message.Encoded["status"] | null
161
+ readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
161
162
  /**
162
163
  * The ID of the approval request.
163
164
  */
@@ -174,11 +175,11 @@ declare module "effect/unstable/ai/Prompt" {
174
175
  /**
175
176
  * The status of item.
176
177
  */
177
- readonly status?: typeof Generated.Message.Encoded["status"] | null
178
+ readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
178
179
  /**
179
180
  * A list of annotations that apply to the output text.
180
181
  */
181
- readonly annotations?: ReadonlyArray<typeof Generated.Annotation.Encoded> | null
182
+ readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
182
183
  } | null
183
184
  }
184
185
  }
@@ -196,11 +197,11 @@ declare module "effect/unstable/ai/Response" {
196
197
  /**
197
198
  * The status of item.
198
199
  */
199
- readonly status?: typeof Generated.Message.Encoded["status"] | null
200
+ readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
200
201
  /**
201
202
  * The text content part annotations.
202
203
  */
203
- readonly annotations?: ReadonlyArray<typeof Generated.Annotation.Encoded> | null
204
+ readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
204
205
  }
205
206
  }
206
207
 
@@ -213,7 +214,7 @@ declare module "effect/unstable/ai/Response" {
213
214
  export interface TextEndPartMetadata extends ProviderMetadata {
214
215
  readonly openai?: {
215
216
  readonly itemId?: string | null
216
- readonly annotations?: ReadonlyArray<typeof Generated.Annotation.Encoded> | null
217
+ readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
217
218
  } | null
218
219
  }
219
220
 
@@ -356,9 +357,9 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
356
357
  readonly config: typeof Config.Service
357
358
  readonly options: LanguageModel.ProviderOptions
358
359
  readonly toolNameMapper: Tool.NameMapper<Tools>
359
- }): Effect.fn.Return<typeof Generated.CreateResponse.Encoded, AiError.AiError> {
360
- const include = new Set<typeof Generated.IncludeEnum.Encoded>()
361
- const capabilities = getModelCapabilities(config.model!)
360
+ }): Effect.fn.Return<typeof OpenAiSchema.CreateResponse.Encoded, AiError.AiError> {
361
+ const include = new Set<typeof OpenAiSchema.IncludeEnum.Encoded>()
362
+ const capabilities = getModelCapabilities(config.model as string)
362
363
  const messages = yield* prepareMessages({
363
364
  config,
364
365
  options,
@@ -375,18 +376,18 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
375
376
  config,
376
377
  options
377
378
  })
378
- const request: typeof Generated.CreateResponse.Encoded = {
379
+ const request: Mutable<typeof OpenAiSchema.CreateResponse.Encoded> = {
379
380
  ...config,
380
381
  input: messages,
381
382
  include: include.size > 0 ? Array.from(include) : null,
382
383
  text: {
383
384
  verbosity: config.text?.verbosity ?? null,
384
385
  format: responseFormat
385
- },
386
- ...(tools ? { tools } : undefined),
387
- ...(toolChoice ? { tool_choice: toolChoice } : undefined),
388
- ...(options.previousResponseId ? { previous_response_id: options.previousResponseId } : undefined)
386
+ }
389
387
  }
388
+ if (tools) request.tools = tools
389
+ if (toolChoice) request.tool_choice = toolChoice
390
+ if (options.previousResponseId) request.previous_response_id = options.previousResponseId
390
391
  return request
391
392
  }
392
393
  )
@@ -516,10 +517,10 @@ const prepareMessages = Effect.fnUntraced(
516
517
  }: {
517
518
  readonly config: typeof Config.Service
518
519
  readonly options: LanguageModel.ProviderOptions
519
- readonly include: Set<typeof Generated.IncludeEnum.Encoded>
520
+ readonly include: Set<typeof OpenAiSchema.IncludeEnum.Encoded>
520
521
  readonly capabilities: ModelCapabilities
521
522
  readonly toolNameMapper: Tool.NameMapper<Tools>
522
- }): Effect.fn.Return<ReadonlyArray<typeof Generated.InputItem.Encoded>, AiError.AiError> {
523
+ }): Effect.fn.Return<ReadonlyArray<typeof OpenAiSchema.InputItem.Encoded>, AiError.AiError> {
523
524
  const processedApprovalIds = new Set<string>()
524
525
 
525
526
  const hasConversation = Predicate.isNotNullish(config.conversation)
@@ -558,21 +559,21 @@ const prepareMessages = Effect.fnUntraced(
558
559
  include.add("web_search_call.action.sources")
559
560
  }
560
561
 
561
- const messages: Array<typeof Generated.InputItem.Encoded> = []
562
+ const messages: Array<typeof OpenAiSchema.InputItem.Encoded> = []
562
563
  const prompt = options.incrementalPrompt ?? options.prompt
563
564
 
564
565
  for (const message of prompt.content) {
565
566
  switch (message.role) {
566
567
  case "system": {
567
568
  messages.push({
568
- role: getSystemMessageMode(config.model!),
569
+ role: getSystemMessageMode(config.model as string),
569
570
  content: message.content
570
571
  })
571
572
  break
572
573
  }
573
574
 
574
575
  case "user": {
575
- const content: Array<typeof Generated.InputContent.Encoded> = []
576
+ const content: Array<typeof OpenAiSchema.InputContent.Encoded> = []
576
577
 
577
578
  for (let index = 0; index < message.content.length; index++) {
578
579
  const part = message.content[index]
@@ -635,7 +636,7 @@ const prepareMessages = Effect.fnUntraced(
635
636
  }
636
637
 
637
638
  case "assistant": {
638
- const reasoningMessages: Record<string, DeepMutable<typeof Generated.ReasoningItem.Encoded>> = {}
639
+ const reasoningMessages: Record<string, DeepMutable<typeof OpenAiSchema.ReasoningItem.Encoded>> = {}
639
640
 
640
641
  for (const part of message.content) {
641
642
  switch (part.type) {
@@ -694,7 +695,7 @@ const prepareMessages = Effect.fnUntraced(
694
695
  }
695
696
  }
696
697
  } else {
697
- const summaryParts: Array<typeof Generated.SummaryTextContent.Encoded> = []
698
+ const summaryParts: Array<typeof OpenAiSchema.SummaryTextContent.Encoded> = []
698
699
 
699
700
  if (part.text.length > 0) {
700
701
  summaryParts.push({ type: "summary_text", text: part.text })
@@ -705,7 +706,9 @@ const prepareMessages = Effect.fnUntraced(
705
706
  type: "reasoning",
706
707
  id,
707
708
  summary: summaryParts,
708
- encrypted_content: encryptedContent ?? null
709
+ ...(Predicate.isNotNull(encryptedContent)
710
+ ? { encrypted_content: encryptedContent }
711
+ : undefined)
709
712
  }
710
713
 
711
714
  messages.push(reasoningMessages[id])
@@ -941,7 +944,56 @@ const buildHttpResponseDetails = (
941
944
  // Response Conversion
942
945
  // =============================================================================
943
946
 
944
- type ResponseStreamEvent = typeof Generated.ResponseStreamEvent.Type
947
+ type ResponseStreamEvent = typeof OpenAiSchema.ResponseStreamEvent.Type
948
+
949
+ type KnownResponseStreamEventType =
950
+ | "response.created"
951
+ | "response.completed"
952
+ | "response.incomplete"
953
+ | "response.failed"
954
+ | "response.output_item.added"
955
+ | "response.output_item.done"
956
+ | "response.output_text.delta"
957
+ | "response.output_text.annotation.added"
958
+ | "response.reasoning_summary_part.added"
959
+ | "response.reasoning_summary_part.done"
960
+ | "response.reasoning_summary_text.delta"
961
+ | "response.function_call_arguments.delta"
962
+ | "response.function_call_arguments.done"
963
+ | "response.code_interpreter_call_code.delta"
964
+ | "response.code_interpreter_call_code.done"
965
+ | "response.apply_patch_call_operation_diff.delta"
966
+ | "response.apply_patch_call_operation_diff.done"
967
+ | "response.image_generation_call.partial_image"
968
+ | "error"
969
+
970
+ type KnownResponseStreamEvent = Extract<ResponseStreamEvent, { readonly type: KnownResponseStreamEventType }>
971
+
972
+ const knownResponseStreamEventTypes = new Set<KnownResponseStreamEventType>([
973
+ "response.created",
974
+ "response.completed",
975
+ "response.incomplete",
976
+ "response.failed",
977
+ "response.output_item.added",
978
+ "response.output_item.done",
979
+ "response.output_text.delta",
980
+ "response.output_text.annotation.added",
981
+ "response.reasoning_summary_part.added",
982
+ "response.reasoning_summary_part.done",
983
+ "response.reasoning_summary_text.delta",
984
+ "response.function_call_arguments.delta",
985
+ "response.function_call_arguments.done",
986
+ "response.code_interpreter_call_code.delta",
987
+ "response.code_interpreter_call_code.done",
988
+ "response.apply_patch_call_operation_diff.delta",
989
+ "response.apply_patch_call_operation_diff.done",
990
+ "response.image_generation_call.partial_image",
991
+ "error"
992
+ ])
993
+
994
+ const isKnownResponseStreamEvent = (
995
+ event: ResponseStreamEvent
996
+ ): event is KnownResponseStreamEvent => knownResponseStreamEventTypes.has(event.type as KnownResponseStreamEventType)
945
997
 
946
998
  const makeResponse = Effect.fnUntraced(
947
999
  function*<Tools extends ReadonlyArray<Tool.Any>>({
@@ -951,7 +1003,7 @@ const makeResponse = Effect.fnUntraced(
951
1003
  toolNameMapper
952
1004
  }: {
953
1005
  readonly options: LanguageModel.ProviderOptions
954
- readonly rawResponse: Generated.Response
1006
+ readonly rawResponse: OpenAiSchema.Response
955
1007
  readonly response: HttpClientResponse.HttpClientResponse
956
1008
  readonly toolNameMapper: Tool.NameMapper<Tools>
957
1009
  }): Effect.fn.Return<
@@ -990,7 +1042,7 @@ const makeResponse = Effect.fnUntraced(
990
1042
  id: part.call_id,
991
1043
  name: toolName,
992
1044
  params: { call_id: part.call_id, operation: part.operation },
993
- metadata: { openai: { ...makeItemIdMetadata(part.id) } }
1045
+ metadata: { openai: makeItemIdMetadata(part.id) }
994
1046
  })
995
1047
  break
996
1048
  }
@@ -1065,7 +1117,7 @@ const makeResponse = Effect.fnUntraced(
1065
1117
  id: part.call_id,
1066
1118
  name: toolName,
1067
1119
  params,
1068
- metadata: { openai: { ...makeItemIdMetadata(part.id) } }
1120
+ metadata: { openai: makeItemIdMetadata(part.id) }
1069
1121
  })
1070
1122
  break
1071
1123
  }
@@ -1096,7 +1148,7 @@ const makeResponse = Effect.fnUntraced(
1096
1148
  id: part.call_id,
1097
1149
  name: toolName,
1098
1150
  params: { action: part.action },
1099
- metadata: { openai: { ...makeItemIdMetadata(part.id) } }
1151
+ metadata: { openai: makeItemIdMetadata(part.id) }
1100
1152
  })
1101
1153
  break
1102
1154
  }
@@ -1130,7 +1182,7 @@ const makeResponse = Effect.fnUntraced(
1130
1182
  ...(Predicate.isNotNullish(part.output) ? { output: part.output } : undefined),
1131
1183
  ...(Predicate.isNotNullish(part.error) ? { error: part.error } : undefined)
1132
1184
  },
1133
- metadata: { openai: { ...makeItemIdMetadata(part.id) } }
1185
+ metadata: { openai: makeItemIdMetadata(part.id) }
1134
1186
  })
1135
1187
 
1136
1188
  break
@@ -1147,7 +1199,12 @@ const makeResponse = Effect.fnUntraced(
1147
1199
  const toolName = `mcp.${part.name}`
1148
1200
 
1149
1201
  const params = yield* Effect.try({
1150
- try: () => Tool.unsafeSecureJsonParse(part.arguments),
1202
+ try: () =>
1203
+ Tool.unsafeSecureJsonParse(
1204
+ typeof part.arguments === "string"
1205
+ ? part.arguments
1206
+ : JSON.stringify(part.arguments)
1207
+ ),
1151
1208
  catch: (cause) =>
1152
1209
  AiError.make({
1153
1210
  module: "OpenAiLanguageModel",
@@ -1305,7 +1362,7 @@ const makeResponse = Effect.fnUntraced(
1305
1362
  id: part.call_id,
1306
1363
  name: toolName,
1307
1364
  params: { action: part.action },
1308
- metadata: { openai: { ...makeItemIdMetadata(part.id) } }
1365
+ metadata: { openai: makeItemIdMetadata(part.id) }
1309
1366
  })
1310
1367
  break
1311
1368
  }
@@ -1344,7 +1401,7 @@ const makeResponse = Effect.fnUntraced(
1344
1401
  reason: finishReason,
1345
1402
  usage: getUsage(rawResponse.usage),
1346
1403
  response: buildHttpResponseDetails(response),
1347
- ...(rawResponse.service_tier && { metadata: { openai: { serviceTier: rawResponse.service_tier } } })
1404
+ ...toServiceTier(rawResponse.service_tier)
1348
1405
  })
1349
1406
 
1350
1407
  return parts
@@ -1377,7 +1434,7 @@ const makeStreamResponse = Effect.fnUntraced(
1377
1434
  let hasToolCalls = false
1378
1435
 
1379
1436
  // Track annotations for current message to include in text-end metadata
1380
- const activeAnnotations: Array<typeof Generated.Annotation.Encoded> = []
1437
+ const activeAnnotations: Array<typeof OpenAiSchema.Annotation.Encoded> = []
1381
1438
 
1382
1439
  type ReasoningSummaryPartStatus = "active" | "can-conclude" | "concluded"
1383
1440
  type ReasoningPart = {
@@ -1434,6 +1491,10 @@ const makeStreamResponse = Effect.fnUntraced(
1434
1491
  Stream.mapEffect(Effect.fnUntraced(function*(event) {
1435
1492
  const parts: Array<Response.StreamPartEncoded> = []
1436
1493
 
1494
+ if (!isKnownResponseStreamEvent(event)) {
1495
+ return parts
1496
+ }
1497
+
1437
1498
  switch (event.type) {
1438
1499
  case "response.created": {
1439
1500
  const createdAt = new Date(event.response.created_at * 1000)
@@ -1463,7 +1524,7 @@ const makeStreamResponse = Effect.fnUntraced(
1463
1524
  ),
1464
1525
  usage: getUsage(event.response.usage),
1465
1526
  response: buildHttpResponseDetails(response),
1466
- ...(event.response.service_tier && { metadata: { openai: { serviceTier: event.response.service_tier } } })
1527
+ ...toServiceTier(event.response.service_tier)
1467
1528
  })
1468
1529
  break
1469
1530
  }
@@ -1602,7 +1663,7 @@ const makeStreamResponse = Effect.fnUntraced(
1602
1663
  parts.push({
1603
1664
  type: "text-start",
1604
1665
  id: event.item.id,
1605
- metadata: { openai: { ...makeItemIdMetadata(event.item.id) } }
1666
+ metadata: { openai: makeItemIdMetadata(event.item.id) }
1606
1667
  })
1607
1668
  break
1608
1669
  }
@@ -1628,7 +1689,7 @@ const makeStreamResponse = Effect.fnUntraced(
1628
1689
  case "shell_call": {
1629
1690
  const toolName = toolNameMapper.getCustomName("shell")
1630
1691
  activeToolCalls[event.output_index] = {
1631
- id: event.item.id,
1692
+ id: event.item.id ?? event.item.call_id,
1632
1693
  name: toolName
1633
1694
  }
1634
1695
  break
@@ -1679,7 +1740,7 @@ const makeStreamResponse = Effect.fnUntraced(
1679
1740
  parts.push({
1680
1741
  type: "tool-params-delta",
1681
1742
  id: toolCall.id,
1682
- delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff)
1743
+ delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff ?? "")
1683
1744
  })
1684
1745
  }
1685
1746
  parts.push({
@@ -1701,7 +1762,7 @@ const makeStreamResponse = Effect.fnUntraced(
1701
1762
  id: toolCall.id,
1702
1763
  name: toolName,
1703
1764
  params: { call_id: event.item.call_id, operation: event.item.operation },
1704
- metadata: { openai: { ...makeItemIdMetadata(event.item.id) } }
1765
+ metadata: { openai: makeItemIdMetadata(event.item.id) }
1705
1766
  })
1706
1767
  }
1707
1768
  delete activeToolCalls[event.output_index]
@@ -1802,7 +1863,7 @@ const makeStreamResponse = Effect.fnUntraced(
1802
1863
  id: event.item.call_id,
1803
1864
  name: toolName,
1804
1865
  params,
1805
- metadata: { openai: { ...makeItemIdMetadata(event.item.id) } }
1866
+ metadata: { openai: makeItemIdMetadata(event.item.id) }
1806
1867
  })
1807
1868
 
1808
1869
  break
@@ -1828,7 +1889,7 @@ const makeStreamResponse = Effect.fnUntraced(
1828
1889
  id: event.item.call_id,
1829
1890
  name: toolName,
1830
1891
  params: { action: event.item.action },
1831
- metadata: { openai: { ...makeItemIdMetadata(event.item.id) } }
1892
+ metadata: { openai: makeItemIdMetadata(event.item.id) }
1832
1893
  })
1833
1894
  break
1834
1895
  }
@@ -1866,7 +1927,7 @@ const makeStreamResponse = Effect.fnUntraced(
1866
1927
  ...(Predicate.isNotNullish(event.item.output) ? { output: event.item.output } : undefined),
1867
1928
  ...(Predicate.isNotNullish(event.item.error) ? { error: event.item.error } : undefined)
1868
1929
  },
1869
- metadata: { openai: { ...makeItemIdMetadata(event.item.id) } }
1930
+ metadata: { openai: makeItemIdMetadata(event.item.id) }
1870
1931
  })
1871
1932
 
1872
1933
  break
@@ -1934,10 +1995,10 @@ const makeStreamResponse = Effect.fnUntraced(
1934
1995
  const toolName = toolNameMapper.getCustomName("shell")
1935
1996
  parts.push({
1936
1997
  type: "tool-call",
1937
- id: event.item.id,
1998
+ id: event.item.id ?? event.item.call_id,
1938
1999
  name: toolName,
1939
2000
  params: { action: event.item.action },
1940
- metadata: { openai: { ...makeItemIdMetadata(event.item.id) } }
2001
+ metadata: { openai: makeItemIdMetadata(event.item.id) }
1941
2002
  })
1942
2003
  break
1943
2004
  }
@@ -1972,7 +2033,7 @@ const makeStreamResponse = Effect.fnUntraced(
1972
2033
  }
1973
2034
 
1974
2035
  case "response.output_text.annotation.added": {
1975
- const annotation = event.annotation as typeof Generated.Annotation.Encoded
2036
+ const annotation = event.annotation as typeof OpenAiSchema.Annotation.Encoded
1976
2037
  // Track annotation for text-end metadata
1977
2038
  activeAnnotations.push(annotation)
1978
2039
  if (annotation.type === "container_file_citation") {
@@ -2088,7 +2149,7 @@ const makeStreamResponse = Effect.fnUntraced(
2088
2149
  id: toolCall.id,
2089
2150
  name: toolCall.name,
2090
2151
  params,
2091
- metadata: { openai: { ...makeItemIdMetadata(event.item_id) } }
2152
+ metadata: { openai: makeItemIdMetadata(event.item_id) }
2092
2153
  })
2093
2154
 
2094
2155
  toolCall.functionCall.emitted = true
@@ -2226,7 +2287,7 @@ const makeStreamResponse = Effect.fnUntraced(
2226
2287
  type: "reasoning-delta",
2227
2288
  id: `${event.item_id}:${event.summary_index}`,
2228
2289
  delta: event.delta,
2229
- metadata: { openai: { ...makeItemIdMetadata(event.item_id) } }
2290
+ metadata: { openai: makeItemIdMetadata(event.item_id) }
2230
2291
  })
2231
2292
  break
2232
2293
  }
@@ -2239,7 +2300,7 @@ const makeStreamResponse = Effect.fnUntraced(
2239
2300
  parts.push({
2240
2301
  type: "reasoning-end",
2241
2302
  id: `${event.item_id}:${event.summary_index}`,
2242
- metadata: { openai: { ...makeItemIdMetadata(event.item_id) } }
2303
+ metadata: { openai: makeItemIdMetadata(event.item_id) }
2243
2304
  })
2244
2305
  // Mark the summary part concluded
2245
2306
  reasoningPart.summaryParts[event.summary_index] = "concluded"
@@ -2265,7 +2326,7 @@ const makeStreamResponse = Effect.fnUntraced(
2265
2326
 
2266
2327
  const annotateRequest = (
2267
2328
  span: Span,
2268
- request: typeof Generated.CreateResponse.Encoded
2329
+ request: typeof OpenAiSchema.CreateResponse.Encoded
2269
2330
  ): void => {
2270
2331
  addGenAIAnnotations(span, {
2271
2332
  system: "openai",
@@ -2285,7 +2346,7 @@ const annotateRequest = (
2285
2346
  })
2286
2347
  }
2287
2348
 
2288
- const annotateResponse = (span: Span, response: Generated.Response): void => {
2349
+ const annotateResponse = (span: Span, response: OpenAiSchema.Response): void => {
2289
2350
  const finishReason = response.incomplete_details?.reason as string | undefined
2290
2351
  addGenAIAnnotations(span, {
2291
2352
  response: {
@@ -2335,7 +2396,7 @@ const annotateStreamResponse = (span: Span, part: Response.StreamPartEncoded) =>
2335
2396
  // Tool Conversion
2336
2397
  // =============================================================================
2337
2398
 
2338
- type OpenAiToolChoice = typeof Generated.CreateResponse.Encoded["tool_choice"]
2399
+ type OpenAiToolChoice = typeof OpenAiSchema.CreateResponse.Encoded["tool_choice"]
2339
2400
 
2340
2401
  const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>({
2341
2402
  config,
@@ -2346,7 +2407,7 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
2346
2407
  readonly options: LanguageModel.ProviderOptions
2347
2408
  readonly toolNameMapper: Tool.NameMapper<Tools>
2348
2409
  }): Effect.fn.Return<{
2349
- readonly tools: ReadonlyArray<typeof Generated.Tool.Encoded> | undefined
2410
+ readonly tools: ReadonlyArray<typeof OpenAiSchema.Tool.Encoded> | undefined
2350
2411
  readonly toolChoice: OpenAiToolChoice | undefined
2351
2412
  }, AiError.AiError> {
2352
2413
  // Return immediately if no tools are in the toolkit
@@ -2354,7 +2415,7 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
2354
2415
  return { tools: undefined, toolChoice: undefined }
2355
2416
  }
2356
2417
 
2357
- const tools: Array<typeof Generated.Tool.Encoded> = []
2418
+ const tools: Array<typeof OpenAiSchema.Tool.Encoded> = []
2358
2419
  let toolChoice: OpenAiToolChoice | undefined = undefined
2359
2420
 
2360
2421
  // Filter the incoming tools down to the set of allowed tools as indicated by
@@ -2561,14 +2622,14 @@ const getStatus = (
2561
2622
  | Prompt.TextPart
2562
2623
  | Prompt.ToolCallPart
2563
2624
  | Prompt.ToolResultPart
2564
- ): typeof Generated.Message.Encoded["status"] | null => part.options.openai?.status ?? null
2625
+ ): typeof OpenAiSchema.MessageStatus.Encoded | null => part.options.openai?.status ?? null
2565
2626
  const getEncryptedContent = (
2566
2627
  part: Prompt.ReasoningPart
2567
2628
  ): string | null => part.options.openai?.encryptedContent ?? null
2568
2629
 
2569
2630
  const getImageDetail = (part: Prompt.FilePart): ImageDetail => part.options.openai?.imageDetail ?? "auto"
2570
2631
 
2571
- const makeItemIdMetadata = (itemId: string | undefined) => Predicate.isNotUndefined(itemId) ? { itemId } : undefined
2632
+ const makeItemIdMetadata = (itemId: string | undefined) => Predicate.isNotUndefined(itemId) ? { itemId } : {}
2572
2633
 
2573
2634
  const makeEncryptedContentMetadata = (encryptedContent: string | null | undefined) =>
2574
2635
  Predicate.isNotNullish(encryptedContent) ? { encryptedContent } : undefined
@@ -2603,7 +2664,7 @@ const tryToolJsonSchema = <T extends Tool.Any>(tool: T, method: string) =>
2603
2664
  const prepareResponseFormat = Effect.fnUntraced(function*({ config, options }: {
2604
2665
  readonly config: typeof Config.Service
2605
2666
  readonly options: LanguageModel.ProviderOptions
2606
- }): Effect.fn.Return<typeof Generated.TextResponseFormatConfiguration.Encoded, AiError.AiError> {
2667
+ }): Effect.fn.Return<typeof OpenAiSchema.TextResponseFormatConfiguration.Encoded, AiError.AiError> {
2607
2668
  if (options.responseFormat.type === "json") {
2608
2669
  const name = options.responseFormat.objectName
2609
2670
  const schema = options.responseFormat.schema
@@ -2691,7 +2752,7 @@ const getApprovalRequestIdMapping = (prompt: Prompt.Prompt): ReadonlyMap<string,
2691
2752
  return mapping
2692
2753
  }
2693
2754
 
2694
- const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.Usage => {
2755
+ const getUsage = (usage: OpenAiSchema.ResponseUsage | null | undefined): Response.Usage => {
2695
2756
  if (Predicate.isNullish(usage)) {
2696
2757
  return {
2697
2758
  inputTokens: {
@@ -2710,8 +2771,8 @@ const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.U
2710
2771
 
2711
2772
  const inputTokens = usage.input_tokens
2712
2773
  const outputTokens = usage.output_tokens
2713
- const cachedTokens = usage.input_tokens_details.cached_tokens
2714
- const reasoningTokens = usage.output_tokens_details.reasoning_tokens
2774
+ const cachedTokens = getUsageTokenDetail(usage.input_tokens_details, "cached_tokens")
2775
+ const reasoningTokens = getUsageTokenDetail(usage.output_tokens_details, "reasoning_tokens")
2715
2776
 
2716
2777
  return {
2717
2778
  inputTokens: {
@@ -2728,6 +2789,30 @@ const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.U
2728
2789
  }
2729
2790
  }
2730
2791
 
2792
+ type ServiceTier = "default" | "auto" | "flex" | "scale" | "priority" | null
2793
+
2794
+ const toServiceTier = (value: string | undefined): {
2795
+ readonly metadata: {
2796
+ readonly openai: {
2797
+ readonly serviceTier: ServiceTier
2798
+ }
2799
+ }
2800
+ } | undefined => {
2801
+ switch (value) {
2802
+ case "default":
2803
+ case "auto":
2804
+ case "flex":
2805
+ case "scale":
2806
+ case "priority":
2807
+ return { metadata: { openai: { serviceTier: value } } }
2808
+ default:
2809
+ return undefined
2810
+ }
2811
+ }
2812
+
2813
+ const getUsageTokenDetail = (details: unknown, key: string): number =>
2814
+ Predicate.hasProperty(details, key) && typeof details[key] === "number" ? details[key] : 0
2815
+
2731
2816
  const transformToolCallParams = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>(
2732
2817
  tools: Tools,
2733
2818
  toolName: string,