@effect/ai-openai 0.30.0 → 0.30.2

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.
@@ -145,54 +145,70 @@ declare module "@effect/ai/Prompt" {
145
145
  }
146
146
  }
147
147
 
148
- /**
149
- * @since 1.0.0
150
- * @category Context
151
- */
152
- export class ProviderMetadata extends Context.Tag(InternalUtilities.ProviderMetadataKey)<
153
- ProviderMetadata,
154
- ProviderMetadata.Service
155
- >() {}
148
+ declare module "@effect/ai/Response" {
149
+ export interface TextPartMetadata extends ProviderMetadata {
150
+ readonly openai?: {
151
+ readonly itemId?: string | undefined
152
+ /**
153
+ * If the model emits a refusal content part, the refusal explanation
154
+ * from the model will be contained in the metadata of an empty text
155
+ * part.
156
+ */
157
+ readonly refusal?: string | undefined
158
+ } | undefined
159
+ }
156
160
 
157
- /**
158
- * @since 1.0.0
159
- */
160
- export declare namespace ProviderMetadata {
161
- /**
162
- * @since 1.0.0
163
- * @category Provider Metadata
164
- */
165
- export interface Service {
166
- "finish": {
167
- readonly serviceTier?: "default" | "auto" | "flex" | "scale" | "priority" | undefined
168
- }
161
+ export interface TextStartPartMetadata extends ProviderMetadata {
162
+ readonly openai?: {
163
+ readonly itemId?: string | undefined
164
+ } | undefined
165
+ }
169
166
 
170
- "reasoning": {
167
+ export interface ReasoningPartMetadata extends ProviderMetadata {
168
+ readonly openai?: {
171
169
  readonly itemId?: string | undefined
172
170
  readonly encryptedContent?: string | undefined
173
- }
171
+ } | undefined
172
+ }
174
173
 
175
- "reasoning-start": {
174
+ export interface ReasoningStartPartMetadata extends ProviderMetadata {
175
+ readonly openai?: {
176
176
  readonly itemId?: string | undefined
177
177
  readonly encryptedContent?: string | undefined
178
- }
178
+ } | undefined
179
+ }
179
180
 
180
- "reasoning-delta": {
181
+ export interface ReasoningDeltaPartMetadata extends ProviderMetadata {
182
+ readonly openai?: {
181
183
  readonly itemId?: string | undefined
182
- }
184
+ } | undefined
185
+ }
183
186
 
184
- "reasoning-end": {
187
+ export interface ReasoningEndPartMetadata extends ProviderMetadata {
188
+ readonly openai?: {
185
189
  readonly itemId?: string | undefined
186
190
  readonly encryptedContent?: string | undefined
187
- }
191
+ } | undefined
192
+ }
193
+
194
+ export interface ToolCallPartMetadata extends ProviderMetadata {
195
+ readonly openai?: {
196
+ readonly itemId?: string | undefined
197
+ } | undefined
198
+ }
188
199
 
189
- "source": {
200
+ export interface DocumentSourcePartMetadata extends ProviderMetadata {
201
+ readonly openai?: {
190
202
  readonly type: "file_citation"
191
203
  /**
192
204
  * The index of the file in the list of files.
193
205
  */
194
206
  readonly index: number
195
- } | {
207
+ } | undefined
208
+ }
209
+
210
+ export interface UrlSourcePartMetadata extends ProviderMetadata {
211
+ readonly openai?: {
196
212
  readonly type: "url_citation"
197
213
  /**
198
214
  * The index of the first character of the URL citation in the message.
@@ -202,25 +218,26 @@ export declare namespace ProviderMetadata {
202
218
  * The index of the last character of the URL citation in the message.
203
219
  */
204
220
  readonly endIndex: number
205
- }
206
-
207
- "text": {
208
- readonly itemId?: string | undefined
209
- /**
210
- * If the model emits a refusal content part, the refusal explanation
211
- * from the model will be contained in the metadata of an empty text
212
- * part.
213
- */
214
- readonly refusal?: string | undefined
215
- }
221
+ } | undefined
222
+ }
216
223
 
217
- "text-start": {
218
- readonly itemId?: string | undefined
219
- }
224
+ export interface FinishPartMetadata extends ProviderMetadata {
225
+ readonly openai?: {
226
+ readonly serviceTier?: "default" | "auto" | "flex" | "scale" | "priority" | undefined
227
+ } | undefined
228
+ }
229
+ }
220
230
 
221
- "tool-call": {
222
- readonly itemId?: string | undefined
223
- }
231
+ /**
232
+ * @since 1.0.0
233
+ */
234
+ export declare namespace ProviderMetadata {
235
+ /**
236
+ * @since 1.0.0
237
+ * @category Provider Metadata
238
+ */
239
+ export interface Service {
240
+ "source": {} | {}
224
241
  }
225
242
  }
226
243
 
@@ -524,7 +541,7 @@ const prepareMessages: (
524
541
  messages.push({
525
542
  type: "function_call_output",
526
543
  call_id: part.id,
527
- result: JSON.stringify(part.result)
544
+ output: JSON.stringify(part.result)
528
545
  })
529
546
  }
530
547
 
@@ -545,7 +562,7 @@ const makeResponse: (
545
562
  options: LanguageModel.ProviderOptions
546
563
  ) => Effect.Effect<
547
564
  Array<Response.PartEncoded>,
548
- never,
565
+ AiError.AiError,
549
566
  IdGenerator.IdGenerator
550
567
  > = Effect.fnUntraced(
551
568
  function*(response, options) {
@@ -577,7 +594,7 @@ const makeResponse: (
577
594
  parts.push({
578
595
  type: "text",
579
596
  text: contentPart.text,
580
- metadata: { [ProviderMetadata.key]: { itemId: part.id } }
597
+ metadata: { openai: { itemId: part.id } }
581
598
  })
582
599
 
583
600
  for (const annotation of contentPart.annotations) {
@@ -593,7 +610,7 @@ const makeResponse: (
593
610
  id: yield* idGenerator.generateId(),
594
611
  mediaType: "text/plain",
595
612
  title: annotation.filename ?? "Untitled Document",
596
- metadata: { [ProviderMetadata.key]: metadata }
613
+ metadata: { openai: metadata }
597
614
  })
598
615
  }
599
616
 
@@ -610,7 +627,7 @@ const makeResponse: (
610
627
  id: yield* idGenerator.generateId(),
611
628
  url: annotation.url,
612
629
  title: annotation.title,
613
- metadata: { [ProviderMetadata.key]: metadata }
630
+ metadata: { openai: metadata }
614
631
  })
615
632
  }
616
633
  }
@@ -621,7 +638,7 @@ const makeResponse: (
621
638
  parts.push({
622
639
  type: "text",
623
640
  text: "",
624
- metadata: { [ProviderMetadata.key]: { refusal: contentPart.refusal } }
641
+ metadata: { openai: { refusal: contentPart.refusal } }
625
642
  })
626
643
 
627
644
  break
@@ -635,12 +652,27 @@ const makeResponse: (
635
652
  case "function_call": {
636
653
  hasToolCalls = true
637
654
 
655
+ const toolName = part.name
656
+ const toolParams = part.arguments
657
+
658
+ const params = yield* Effect.try({
659
+ try: () => Tool.unsafeSecureJsonParse(toolParams),
660
+ catch: (cause) =>
661
+ new AiError.MalformedOutput({
662
+ module: "OpenAiLanguageModel",
663
+ method: "makeResponse",
664
+ description: "Failed to securely parse tool call parameters " +
665
+ `for tool '${toolName}':\nParameters: ${toolParams}`,
666
+ cause
667
+ })
668
+ })
669
+
638
670
  parts.push({
639
671
  type: "tool-call",
640
672
  id: part.call_id,
641
- name: part.name,
642
- params: JSON.parse(part.arguments),
643
- metadata: { [ProviderMetadata.key]: { itemId: part.id } }
673
+ name: toolName,
674
+ params,
675
+ metadata: { openai: { itemId: part.id } }
644
676
  })
645
677
 
646
678
  break
@@ -745,7 +777,7 @@ const makeResponse: (
745
777
  parts.push({
746
778
  type: "reasoning",
747
779
  text: "",
748
- metadata: { [ProviderMetadata.key]: { itemId: part.id } }
780
+ metadata: { openai: { itemId: part.id } }
749
781
  })
750
782
  } else {
751
783
  for (const summary of part.summary) {
@@ -756,7 +788,7 @@ const makeResponse: (
756
788
  parts.push({
757
789
  type: "reasoning",
758
790
  text: summary.text,
759
- metadata: { [ProviderMetadata.key]: metadata }
791
+ metadata: { openai: metadata }
760
792
  })
761
793
  }
762
794
  }
@@ -785,7 +817,7 @@ const makeResponse: (
785
817
  reasoningTokens: response.usage?.output_tokens_details?.reasoning_tokens,
786
818
  cachedInputTokens: response.usage?.input_tokens_details?.cached_tokens
787
819
  },
788
- metadata: { [ProviderMetadata.key]: metadata }
820
+ metadata: { openai: metadata }
789
821
  })
790
822
 
791
823
  return parts
@@ -858,7 +890,7 @@ const makeStreamResponse: (
858
890
  reasoningTokens: event.response.usage?.output_tokens_details?.reasoning_tokens,
859
891
  cachedInputTokens: event.response.usage?.input_tokens_details?.cached_tokens
860
892
  },
861
- metadata: { [ProviderMetadata.key]: { serviceTier: event.response.service_tier } }
893
+ metadata: { openai: { serviceTier: event.response.service_tier } }
862
894
  })
863
895
  break
864
896
  }
@@ -902,7 +934,7 @@ const makeStreamResponse: (
902
934
  parts.push({
903
935
  type: "text-start",
904
936
  id: event.item.id,
905
- metadata: { [ProviderMetadata.key]: { itemId: event.item.id } }
937
+ metadata: { openai: { itemId: event.item.id } }
906
938
  })
907
939
  break
908
940
  }
@@ -916,7 +948,7 @@ const makeStreamResponse: (
916
948
  type: "reasoning-start",
917
949
  id: `${event.item.id}:0`,
918
950
  metadata: {
919
- [ProviderMetadata.key]: {
951
+ openai: {
920
952
  itemId: event.item.id,
921
953
  encryptedContent: event.item.encrypted_content
922
954
  }
@@ -1002,18 +1034,37 @@ const makeStreamResponse: (
1002
1034
 
1003
1035
  case "function_call": {
1004
1036
  hasToolCalls = true
1005
- delete activeToolCalls[event.output_index]
1037
+
1038
+ const toolName = event.item.name
1039
+ const toolParams = event.item.arguments
1040
+
1041
+ const params = yield* Effect.try({
1042
+ try: () => Tool.unsafeSecureJsonParse(toolParams),
1043
+ catch: (cause) =>
1044
+ new AiError.MalformedOutput({
1045
+ module: "OpenAiLanguageModel",
1046
+ method: "makeStreamResponse",
1047
+ description: "Failed to securely parse tool call parameters " +
1048
+ `for tool '${toolName}':\nParameters: ${toolParams}`,
1049
+ cause
1050
+ })
1051
+ })
1052
+
1006
1053
  parts.push({
1007
1054
  type: "tool-params-end",
1008
1055
  id: event.item.call_id
1009
1056
  })
1057
+
1010
1058
  parts.push({
1011
1059
  type: "tool-call",
1012
1060
  id: event.item.call_id,
1013
- name: event.item.name,
1014
- params: JSON.parse(event.item.arguments),
1015
- metadata: { [ProviderMetadata.key]: { itemId: event.item.id } }
1061
+ name: toolName,
1062
+ params,
1063
+ metadata: { openai: { itemId: event.item.id } }
1016
1064
  })
1065
+
1066
+ delete activeToolCalls[event.output_index]
1067
+
1017
1068
  break
1018
1069
  }
1019
1070
 
@@ -1032,7 +1083,7 @@ const makeStreamResponse: (
1032
1083
  type: "reasoning-end",
1033
1084
  id: `${event.item.id}:${summaryIndex}`,
1034
1085
  metadata: {
1035
- [ProviderMetadata.key]: {
1086
+ openai: {
1036
1087
  itemId: event.item.id,
1037
1088
  encryptedContent: event.item.encrypted_content
1038
1089
  }
@@ -1127,7 +1178,7 @@ const makeStreamResponse: (
1127
1178
  type: "reasoning-start",
1128
1179
  id: `${event.item_id}:${event.summary_index}`,
1129
1180
  metadata: {
1130
- [ProviderMetadata.key]: {
1181
+ openai: {
1131
1182
  itemId: event.item_id,
1132
1183
  encryptedContent: reasoningPart?.encryptedContent
1133
1184
  }
@@ -1142,7 +1193,7 @@ const makeStreamResponse: (
1142
1193
  type: "reasoning-delta",
1143
1194
  id: `${event.item_id}:${event.summary_index}`,
1144
1195
  delta: event.delta,
1145
- metadata: { [ProviderMetadata.key]: { itemId: event.item_id } }
1196
+ metadata: { openai: { itemId: event.item_id } }
1146
1197
  })
1147
1198
  break
1148
1199
  }
@@ -1211,7 +1262,7 @@ const annotateStreamResponse = (span: Span, part: Response.StreamPartEncoded) =>
1211
1262
  })
1212
1263
  }
1213
1264
  if (part.type === "finish") {
1214
- const serviceTier = part.metadata?.[ProviderMetadata.key]?.serviceTier as string | undefined
1265
+ const serviceTier = part.metadata?.openai?.serviceTier as string | undefined
1215
1266
  addGenAIAnnotations(span, {
1216
1267
  response: {
1217
1268
  finishReasons: [part.reason]