@effect/ai-openai-compat 4.0.0-beta.0

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/dist/OpenAiClient.d.ts +739 -0
  3. package/dist/OpenAiClient.d.ts.map +1 -0
  4. package/dist/OpenAiClient.js +170 -0
  5. package/dist/OpenAiClient.js.map +1 -0
  6. package/dist/OpenAiConfig.d.ts +47 -0
  7. package/dist/OpenAiConfig.d.ts.map +1 -0
  8. package/dist/OpenAiConfig.js +25 -0
  9. package/dist/OpenAiConfig.js.map +1 -0
  10. package/dist/OpenAiError.d.ts +93 -0
  11. package/dist/OpenAiError.d.ts.map +1 -0
  12. package/dist/OpenAiError.js +5 -0
  13. package/dist/OpenAiError.js.map +1 -0
  14. package/dist/OpenAiLanguageModel.d.ts +285 -0
  15. package/dist/OpenAiLanguageModel.d.ts.map +1 -0
  16. package/dist/OpenAiLanguageModel.js +1223 -0
  17. package/dist/OpenAiLanguageModel.js.map +1 -0
  18. package/dist/OpenAiTelemetry.d.ts +120 -0
  19. package/dist/OpenAiTelemetry.d.ts.map +1 -0
  20. package/dist/OpenAiTelemetry.js +35 -0
  21. package/dist/OpenAiTelemetry.js.map +1 -0
  22. package/dist/index.d.ts +35 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +36 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/internal/errors.d.ts +2 -0
  27. package/dist/internal/errors.d.ts.map +1 -0
  28. package/dist/internal/errors.js +286 -0
  29. package/dist/internal/errors.js.map +1 -0
  30. package/dist/internal/utilities.d.ts +2 -0
  31. package/dist/internal/utilities.d.ts.map +1 -0
  32. package/dist/internal/utilities.js +25 -0
  33. package/dist/internal/utilities.js.map +1 -0
  34. package/package.json +62 -0
  35. package/src/OpenAiClient.ts +998 -0
  36. package/src/OpenAiConfig.ts +64 -0
  37. package/src/OpenAiError.ts +102 -0
  38. package/src/OpenAiLanguageModel.ts +1638 -0
  39. package/src/OpenAiTelemetry.ts +159 -0
  40. package/src/index.ts +41 -0
  41. package/src/internal/errors.ts +327 -0
  42. package/src/internal/utilities.ts +33 -0
@@ -0,0 +1,998 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import * as Array from "effect/Array"
5
+ import type * as Config from "effect/Config"
6
+ import * as Effect from "effect/Effect"
7
+ import { identity, pipe } from "effect/Function"
8
+ import * as Layer from "effect/Layer"
9
+ import * as Redacted from "effect/Redacted"
10
+ import * as Schema from "effect/Schema"
11
+ import * as ServiceMap from "effect/ServiceMap"
12
+ import * as Stream from "effect/Stream"
13
+ import type * as AiError from "effect/unstable/ai/AiError"
14
+ import * as Sse from "effect/unstable/encoding/Sse"
15
+ import * as Headers from "effect/unstable/http/Headers"
16
+ import * as HttpClient from "effect/unstable/http/HttpClient"
17
+ import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
18
+ import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
19
+ import * as Errors from "./internal/errors.ts"
20
+ import { OpenAiConfig } from "./OpenAiConfig.ts"
21
+
22
+ /**
23
+ * @since 1.0.0
24
+ * @category models
25
+ */
26
+ export interface Service {
27
+ readonly client: HttpClient.HttpClient
28
+ readonly createResponse: (
29
+ options: CreateResponseRequestJson
30
+ ) => Effect.Effect<
31
+ [body: CreateResponse200, response: HttpClientResponse.HttpClientResponse],
32
+ AiError.AiError
33
+ >
34
+ readonly createResponseStream: (
35
+ options: Omit<CreateResponseRequestJson, "stream" | "stream_options">
36
+ ) => Effect.Effect<
37
+ [
38
+ response: HttpClientResponse.HttpClientResponse,
39
+ stream: Stream.Stream<CreateResponse200Sse, AiError.AiError>
40
+ ],
41
+ AiError.AiError
42
+ >
43
+ readonly createEmbedding: (
44
+ options: CreateEmbeddingRequestJson
45
+ ) => Effect.Effect<CreateEmbedding200, AiError.AiError>
46
+ }
47
+
48
+ /**
49
+ * @since 1.0.0
50
+ * @category service
51
+ */
52
+ export class OpenAiClient extends ServiceMap.Service<OpenAiClient, Service>()(
53
+ "@effect/ai-openai-compat/OpenAiClient"
54
+ ) {}
55
+
56
+ /**
57
+ * @since 1.0.0
58
+ * @category models
59
+ */
60
+ export type Options = {
61
+ readonly apiKey?: Redacted.Redacted<string> | undefined
62
+ readonly apiUrl?: string | undefined
63
+ readonly organizationId?: Redacted.Redacted<string> | undefined
64
+ readonly projectId?: Redacted.Redacted<string> | undefined
65
+ readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
66
+ }
67
+
68
+ const RedactedOpenAiHeaders = {
69
+ OpenAiOrganization: "OpenAI-Organization",
70
+ OpenAiProject: "OpenAI-Project"
71
+ }
72
+
73
+ /**
74
+ * @since 1.0.0
75
+ * @category constructors
76
+ */
77
+ export const make = Effect.fnUntraced(
78
+ function*(options: Options): Effect.fn.Return<Service, never, HttpClient.HttpClient> {
79
+ const baseClient = yield* HttpClient.HttpClient
80
+
81
+ const httpClient = baseClient.pipe(
82
+ HttpClient.mapRequest((request) =>
83
+ request.pipe(
84
+ HttpClientRequest.prependUrl(options.apiUrl ?? "https://api.openai.com/v1"),
85
+ options.apiKey !== undefined
86
+ ? HttpClientRequest.bearerToken(Redacted.value(options.apiKey))
87
+ : identity,
88
+ options.organizationId !== undefined
89
+ ? HttpClientRequest.setHeader(
90
+ RedactedOpenAiHeaders.OpenAiOrganization,
91
+ Redacted.value(options.organizationId)
92
+ )
93
+ : identity,
94
+ options.projectId !== undefined
95
+ ? HttpClientRequest.setHeader(
96
+ RedactedOpenAiHeaders.OpenAiProject,
97
+ Redacted.value(options.projectId)
98
+ )
99
+ : identity,
100
+ HttpClientRequest.acceptJson
101
+ )
102
+ ),
103
+ options.transformClient !== undefined
104
+ ? options.transformClient
105
+ : identity
106
+ )
107
+
108
+ const resolveHttpClient = Effect.map(
109
+ OpenAiConfig.getOrUndefined,
110
+ (config) =>
111
+ config?.transformClient !== undefined
112
+ ? config.transformClient(httpClient)
113
+ : httpClient
114
+ )
115
+
116
+ const decodeResponse = HttpClientResponse.schemaBodyJson(ChatCompletionResponse)
117
+
118
+ const createResponse = (
119
+ payload: CreateResponseRequestJson
120
+ ): Effect.Effect<
121
+ [body: CreateResponse200, response: HttpClientResponse.HttpClientResponse],
122
+ AiError.AiError
123
+ > =>
124
+ Effect.flatMap(resolveHttpClient, (client) =>
125
+ pipe(
126
+ HttpClientRequest.post("/chat/completions"),
127
+ HttpClientRequest.bodyJsonUnsafe(payload),
128
+ HttpClient.filterStatusOk(client).execute,
129
+ Effect.flatMap((response) =>
130
+ Effect.map(decodeResponse(response), (
131
+ body
132
+ ): [CreateResponse200, HttpClientResponse.HttpClientResponse] => [
133
+ body,
134
+ response
135
+ ])
136
+ ),
137
+ Effect.catchTags({
138
+ HttpClientError: (error) => Errors.mapHttpClientError(error, "createResponse"),
139
+ SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, "createResponse"))
140
+ })
141
+ ))
142
+
143
+ const buildResponseStream = (
144
+ response: HttpClientResponse.HttpClientResponse
145
+ ): [
146
+ HttpClientResponse.HttpClientResponse,
147
+ Stream.Stream<CreateResponse200Sse, AiError.AiError>
148
+ ] => {
149
+ const stream = response.stream.pipe(
150
+ Stream.decodeText(),
151
+ Stream.pipeThroughChannel(Sse.decode()),
152
+ Stream.flatMap((event) => {
153
+ const data = decodeChatCompletionSseData(event.data)
154
+ return Stream.fromIterable(data !== undefined ? [data] : [])
155
+ }),
156
+ Stream.takeUntil((event) => event === "[DONE]"),
157
+ Stream.catchTags({
158
+ Retry: (error) => Stream.die(error),
159
+ HttpClientError: (error) => Stream.fromEffect(Errors.mapHttpClientError(error, "createResponseStream"))
160
+ })
161
+ ) as any
162
+ return [response, stream]
163
+ }
164
+
165
+ const createResponseStream: Service["createResponseStream"] = (payload) =>
166
+ Effect.flatMap(resolveHttpClient, (client) =>
167
+ pipe(
168
+ HttpClientRequest.post("/chat/completions"),
169
+ HttpClientRequest.bodyJsonUnsafe({
170
+ ...payload,
171
+ stream: true,
172
+ stream_options: {
173
+ include_usage: true
174
+ }
175
+ }),
176
+ HttpClient.filterStatusOk(client).execute,
177
+ Effect.map(buildResponseStream),
178
+ Effect.catchTag(
179
+ "HttpClientError",
180
+ (error) => Errors.mapHttpClientError(error, "createResponseStream")
181
+ )
182
+ ))
183
+
184
+ const decodeEmbedding = HttpClientResponse.schemaBodyJson(CreateEmbeddingResponseSchema)
185
+
186
+ const createEmbedding = (
187
+ payload: CreateEmbeddingRequestJson
188
+ ): Effect.Effect<CreateEmbedding200, AiError.AiError> =>
189
+ Effect.flatMap(resolveHttpClient, (client) =>
190
+ pipe(
191
+ HttpClientRequest.post("/embeddings"),
192
+ HttpClientRequest.bodyJsonUnsafe(payload),
193
+ HttpClient.filterStatusOk(client).execute,
194
+ Effect.flatMap(decodeEmbedding),
195
+ Effect.catchTags({
196
+ HttpClientError: (error) => Errors.mapHttpClientError(error, "createEmbedding"),
197
+ SchemaError: (error) => Effect.fail(Errors.mapSchemaError(error, "createEmbedding"))
198
+ })
199
+ ))
200
+
201
+ return OpenAiClient.of({
202
+ client: httpClient,
203
+ createResponse,
204
+ createResponseStream,
205
+ createEmbedding
206
+ })
207
+ },
208
+ Effect.updateService(
209
+ Headers.CurrentRedactedNames,
210
+ Array.appendAll(Object.values(RedactedOpenAiHeaders))
211
+ )
212
+ )
213
+
214
+ /**
215
+ * @since 1.0.0
216
+ * @category layers
217
+ */
218
+ export const layer = (options: Options): Layer.Layer<OpenAiClient, never, HttpClient.HttpClient> =>
219
+ Layer.effect(OpenAiClient, make(options))
220
+
221
+ /**
222
+ * @since 1.0.0
223
+ * @category layers
224
+ */
225
+ export const layerConfig = (options?: {
226
+ readonly apiKey?: Config.Config<Redacted.Redacted<string>> | undefined
227
+ readonly apiUrl?: Config.Config<string> | undefined
228
+ readonly organizationId?: Config.Config<Redacted.Redacted<string>> | undefined
229
+ readonly projectId?: Config.Config<Redacted.Redacted<string>> | undefined
230
+ readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
231
+ }): Layer.Layer<OpenAiClient, Config.ConfigError, HttpClient.HttpClient> =>
232
+ Layer.effect(
233
+ OpenAiClient,
234
+ Effect.gen(function*() {
235
+ const apiKey = options?.apiKey !== undefined
236
+ ? yield* options.apiKey :
237
+ undefined
238
+ const apiUrl = options?.apiUrl !== undefined
239
+ ? yield* options.apiUrl :
240
+ undefined
241
+ const organizationId = options?.organizationId !== undefined
242
+ ? yield* options.organizationId
243
+ : undefined
244
+ const projectId = options?.projectId !== undefined
245
+ ? yield* options.projectId :
246
+ undefined
247
+ return yield* make({
248
+ apiKey,
249
+ apiUrl,
250
+ organizationId,
251
+ projectId,
252
+ transformClient: options?.transformClient
253
+ })
254
+ })
255
+ )
256
+
257
+ type JsonObject = { readonly [x: string]: Schema.Json }
258
+
259
+ /**
260
+ * @since 1.0.0
261
+ */
262
+ export type IncludeEnum =
263
+ | "message.input_image.image_url"
264
+ | "reasoning.encrypted_content"
265
+ | "message.output_text.logprobs"
266
+
267
+ /**
268
+ * @since 1.0.0
269
+ */
270
+ export type MessageStatus = "in_progress" | "completed" | "incomplete"
271
+
272
+ type InputTextContent = {
273
+ readonly type: "input_text"
274
+ readonly text: string
275
+ }
276
+
277
+ type InputImageContent = {
278
+ readonly type: "input_image"
279
+ readonly image_url?: string | null | undefined
280
+ readonly file_id?: string | null | undefined
281
+ readonly detail?: "low" | "high" | "auto" | null | undefined
282
+ }
283
+
284
+ type InputFileContent = {
285
+ readonly type: "input_file"
286
+ readonly file_id?: string | null | undefined
287
+ readonly filename?: string | undefined
288
+ readonly file_url?: string | undefined
289
+ readonly file_data?: string | undefined
290
+ }
291
+
292
+ /**
293
+ * @since 1.0.0
294
+ */
295
+ export type InputContent = InputTextContent | InputImageContent | InputFileContent
296
+
297
+ /**
298
+ * @since 1.0.0
299
+ */
300
+ export type SummaryTextContent = {
301
+ readonly type: "summary_text"
302
+ readonly text: string
303
+ }
304
+
305
+ type ReasoningTextContent = {
306
+ readonly type: "reasoning_text"
307
+ readonly text: string
308
+ }
309
+
310
+ type RefusalContent = {
311
+ readonly type: "refusal"
312
+ readonly refusal: string
313
+ }
314
+
315
+ type TextContent = {
316
+ readonly type: "text"
317
+ readonly text: string
318
+ }
319
+
320
+ type ComputerScreenshotContent = {
321
+ readonly type: "computer_screenshot"
322
+ readonly image_url: string | null
323
+ readonly file_id: string | null
324
+ }
325
+
326
+ type FileCitationAnnotation = {
327
+ readonly type: "file_citation"
328
+ readonly file_id: string
329
+ readonly index: number
330
+ readonly filename: string
331
+ }
332
+
333
+ type UrlCitationAnnotation = {
334
+ readonly type: "url_citation"
335
+ readonly url: string
336
+ readonly start_index: number
337
+ readonly end_index: number
338
+ readonly title: string
339
+ }
340
+
341
+ type ContainerFileCitationAnnotation = {
342
+ readonly type: "container_file_citation"
343
+ readonly container_id: string
344
+ readonly file_id: string
345
+ readonly start_index: number
346
+ readonly end_index: number
347
+ readonly filename: string
348
+ }
349
+
350
+ type FilePathAnnotation = {
351
+ readonly type: "file_path"
352
+ readonly file_id: string
353
+ readonly index: number
354
+ }
355
+
356
+ /**
357
+ * @since 1.0.0
358
+ */
359
+ export type Annotation =
360
+ | FileCitationAnnotation
361
+ | UrlCitationAnnotation
362
+ | ContainerFileCitationAnnotation
363
+ | FilePathAnnotation
364
+
365
+ type OutputTextContent = {
366
+ readonly type: "output_text"
367
+ readonly text: string
368
+ readonly annotations?: ReadonlyArray<Annotation> | undefined
369
+ readonly logprobs?: ReadonlyArray<unknown> | undefined
370
+ }
371
+
372
+ type OutputMessageContent =
373
+ | InputTextContent
374
+ | OutputTextContent
375
+ | TextContent
376
+ | SummaryTextContent
377
+ | ReasoningTextContent
378
+ | RefusalContent
379
+ | InputImageContent
380
+ | ComputerScreenshotContent
381
+ | InputFileContent
382
+
383
+ type OutputMessage = {
384
+ readonly id: string
385
+ readonly type: "message"
386
+ readonly role: "assistant"
387
+ readonly content: ReadonlyArray<OutputMessageContent>
388
+ readonly status: MessageStatus
389
+ }
390
+
391
+ /**
392
+ * @since 1.0.0
393
+ */
394
+ export type ReasoningItem = {
395
+ readonly type: "reasoning"
396
+ readonly id: string
397
+ readonly encrypted_content?: string | null | undefined
398
+ readonly summary: ReadonlyArray<SummaryTextContent>
399
+ readonly content?: ReadonlyArray<ReasoningTextContent> | undefined
400
+ readonly status?: MessageStatus | undefined
401
+ }
402
+
403
+ type FunctionCall = {
404
+ readonly id?: string | undefined
405
+ readonly type: "function_call"
406
+ readonly call_id: string
407
+ readonly name: string
408
+ readonly arguments: string
409
+ readonly status?: MessageStatus | undefined
410
+ }
411
+
412
+ type FunctionCallOutput = {
413
+ readonly id?: string | null | undefined
414
+ readonly call_id: string
415
+ readonly type: "function_call_output"
416
+ readonly output: string | ReadonlyArray<InputTextContent | InputImageContent | InputFileContent>
417
+ readonly status?: MessageStatus | null | undefined
418
+ }
419
+
420
+ type CustomToolCall = {
421
+ readonly type: "custom_tool_call"
422
+ readonly id?: string | undefined
423
+ readonly call_id: string
424
+ readonly name: string
425
+ readonly input: string
426
+ }
427
+
428
+ type CustomToolCallOutput = {
429
+ readonly type: "custom_tool_call_output"
430
+ readonly id?: string | undefined
431
+ readonly call_id: string
432
+ readonly output: string | ReadonlyArray<InputTextContent | InputImageContent | InputFileContent>
433
+ }
434
+
435
+ type ItemReference = {
436
+ readonly type?: "item_reference" | null | undefined
437
+ readonly id: string
438
+ }
439
+
440
+ /**
441
+ * @since 1.0.0
442
+ */
443
+ export type InputItem =
444
+ | {
445
+ readonly role: "user" | "assistant" | "system" | "developer"
446
+ readonly content: string | ReadonlyArray<InputContent>
447
+ readonly type?: "message" | undefined
448
+ }
449
+ | {
450
+ readonly type?: "message" | undefined
451
+ readonly role: "user" | "system" | "developer"
452
+ readonly status?: MessageStatus | undefined
453
+ readonly content: ReadonlyArray<InputContent>
454
+ }
455
+ | OutputMessage
456
+ | FunctionCall
457
+ | FunctionCallOutput
458
+ | ReasoningItem
459
+ | CustomToolCallOutput
460
+ | CustomToolCall
461
+ | ItemReference
462
+
463
+ type FunctionTool = {
464
+ readonly type: "function"
465
+ readonly name: string
466
+ readonly description?: string | null | undefined
467
+ readonly parameters?: JsonObject | null | undefined
468
+ readonly strict?: boolean | null | undefined
469
+ }
470
+
471
+ type CustomToolParam = {
472
+ readonly type: "custom"
473
+ readonly name: string
474
+ readonly description?: string | undefined
475
+ readonly format?: unknown
476
+ }
477
+
478
+ /**
479
+ * @since 1.0.0
480
+ */
481
+ export type Tool =
482
+ | FunctionTool
483
+ | CustomToolParam
484
+
485
+ type ToolChoice =
486
+ | "none"
487
+ | "auto"
488
+ | "required"
489
+ | {
490
+ readonly type: "allowed_tools"
491
+ readonly mode: "auto" | "required"
492
+ readonly tools: ReadonlyArray<JsonObject>
493
+ }
494
+ | {
495
+ readonly type: "function"
496
+ readonly name: string
497
+ }
498
+ | {
499
+ readonly type: "custom"
500
+ readonly name: string
501
+ }
502
+
503
+ /**
504
+ * @since 1.0.0
505
+ */
506
+ export type TextResponseFormatConfiguration =
507
+ | {
508
+ readonly type: "text"
509
+ }
510
+ | {
511
+ readonly type: "json_schema"
512
+ readonly description?: string | undefined
513
+ readonly name: string
514
+ readonly schema: JsonObject
515
+ readonly strict?: boolean | null | undefined
516
+ }
517
+ | {
518
+ readonly type: "json_object"
519
+ }
520
+
521
+ /**
522
+ * @since 1.0.0
523
+ */
524
+ export type CreateResponse = {
525
+ readonly metadata?: Readonly<Record<string, string>> | null | undefined
526
+ readonly top_logprobs?: number | undefined
527
+ readonly temperature?: number | null | undefined
528
+ readonly top_p?: number | null | undefined
529
+ readonly user?: string | null | undefined
530
+ readonly safety_identifier?: string | null | undefined
531
+ readonly prompt_cache_key?: string | null | undefined
532
+ readonly service_tier?: string | undefined
533
+ readonly prompt_cache_retention?: "in-memory" | "24h" | null | undefined
534
+ readonly previous_response_id?: string | null | undefined
535
+ readonly model?: string | undefined
536
+ readonly reasoning?: unknown
537
+ readonly background?: boolean | null | undefined
538
+ readonly max_output_tokens?: number | null | undefined
539
+ readonly max_tool_calls?: number | null | undefined
540
+ readonly text?: {
541
+ readonly format?: TextResponseFormatConfiguration | undefined
542
+ readonly verbosity?: "low" | "medium" | "high" | null | undefined
543
+ } | undefined
544
+ readonly tools?: ReadonlyArray<Tool> | undefined
545
+ readonly tool_choice?: ToolChoice | undefined
546
+ readonly truncation?: "auto" | "disabled" | null | undefined
547
+ readonly input?: string | ReadonlyArray<InputItem> | undefined
548
+ readonly include?: ReadonlyArray<IncludeEnum> | null | undefined
549
+ readonly parallel_tool_calls?: boolean | null | undefined
550
+ readonly store?: boolean | null | undefined
551
+ readonly instructions?: string | null | undefined
552
+ readonly stream?: boolean | null | undefined
553
+ readonly conversation?: string | null | undefined
554
+ readonly modalities?: ReadonlyArray<"text" | "audio"> | undefined
555
+ readonly seed?: number | undefined
556
+ }
557
+
558
+ /**
559
+ * @since 1.0.0
560
+ */
561
+ export type ResponseUsage = {
562
+ readonly input_tokens: number
563
+ readonly output_tokens: number
564
+ readonly total_tokens: number
565
+ readonly input_tokens_details?: unknown
566
+ readonly output_tokens_details?: unknown
567
+ }
568
+
569
+ type OutputItem =
570
+ | OutputMessage
571
+ | FunctionCall
572
+ | ReasoningItem
573
+ | CustomToolCall
574
+
575
+ /**
576
+ * @since 1.0.0
577
+ */
578
+ export type Response = {
579
+ readonly id: string
580
+ readonly object?: "response" | undefined
581
+ readonly model: string
582
+ readonly status?: "completed" | "failed" | "in_progress" | "cancelled" | "queued" | "incomplete" | undefined
583
+ readonly created_at: number
584
+ readonly output: ReadonlyArray<OutputItem>
585
+ readonly usage?: ResponseUsage | null | undefined
586
+ readonly incomplete_details?:
587
+ | {
588
+ readonly reason?: "max_output_tokens" | "content_filter" | undefined
589
+ }
590
+ | null
591
+ | undefined
592
+ readonly service_tier?: string | undefined
593
+ }
594
+
595
+ type ResponseCreatedEvent = {
596
+ readonly type: "response.created"
597
+ readonly response: Response
598
+ readonly sequence_number: number
599
+ }
600
+
601
+ type ResponseCompletedEvent = {
602
+ readonly type: "response.completed"
603
+ readonly response: Response
604
+ readonly sequence_number: number
605
+ }
606
+
607
+ type ResponseIncompleteEvent = {
608
+ readonly type: "response.incomplete"
609
+ readonly response: Response
610
+ readonly sequence_number: number
611
+ }
612
+
613
+ type ResponseFailedEvent = {
614
+ readonly type: "response.failed"
615
+ readonly response: Response
616
+ readonly sequence_number: number
617
+ }
618
+
619
+ type ResponseOutputItemAddedEvent = {
620
+ readonly type: "response.output_item.added"
621
+ readonly output_index: number
622
+ readonly sequence_number: number
623
+ readonly item: OutputItem
624
+ }
625
+
626
+ type ResponseOutputItemDoneEvent = {
627
+ readonly type: "response.output_item.done"
628
+ readonly output_index: number
629
+ readonly sequence_number: number
630
+ readonly item: OutputItem
631
+ }
632
+
633
+ type ResponseTextDeltaEvent = {
634
+ readonly type: "response.output_text.delta"
635
+ readonly item_id: string
636
+ readonly output_index: number
637
+ readonly content_index: number
638
+ readonly delta: string
639
+ readonly sequence_number: number
640
+ readonly logprobs?: ReadonlyArray<unknown> | undefined
641
+ }
642
+
643
+ type ResponseOutputTextAnnotationAddedEvent = {
644
+ readonly type: "response.output_text.annotation.added"
645
+ readonly item_id: string
646
+ readonly output_index: number
647
+ readonly content_index: number
648
+ readonly annotation_index: number
649
+ readonly sequence_number: number
650
+ readonly annotation: Annotation
651
+ }
652
+
653
+ type ResponseFunctionCallArgumentsDeltaEvent = {
654
+ readonly type: "response.function_call_arguments.delta"
655
+ readonly item_id: string
656
+ readonly output_index: number
657
+ readonly sequence_number: number
658
+ readonly delta: string
659
+ }
660
+
661
+ type ResponseReasoningSummaryPartAddedEvent = {
662
+ readonly type: "response.reasoning_summary_part.added"
663
+ readonly item_id: string
664
+ readonly output_index: number
665
+ readonly summary_index: number
666
+ readonly sequence_number: number
667
+ readonly part: SummaryTextContent
668
+ }
669
+
670
+ type ResponseReasoningSummaryPartDoneEvent = {
671
+ readonly type: "response.reasoning_summary_part.done"
672
+ readonly item_id: string
673
+ readonly output_index: number
674
+ readonly summary_index: number
675
+ readonly sequence_number: number
676
+ readonly part: SummaryTextContent
677
+ }
678
+
679
+ type ResponseReasoningSummaryTextDeltaEvent = {
680
+ readonly type: "response.reasoning_summary_text.delta"
681
+ readonly item_id: string
682
+ readonly output_index: number
683
+ readonly summary_index: number
684
+ readonly delta: string
685
+ readonly sequence_number: number
686
+ }
687
+
688
+ type ResponseErrorEvent = {
689
+ readonly type: "error"
690
+ readonly code: string | null
691
+ readonly message: string
692
+ readonly param: string | null
693
+ readonly sequence_number: number
694
+ }
695
+
696
+ type UnknownResponseStreamEvent = {
697
+ readonly type: string
698
+ readonly [key: string]: unknown
699
+ }
700
+
701
+ /**
702
+ * @since 1.0.0
703
+ */
704
+ export type ResponseStreamEvent =
705
+ | ResponseCreatedEvent
706
+ | ResponseCompletedEvent
707
+ | ResponseIncompleteEvent
708
+ | ResponseFailedEvent
709
+ | ResponseOutputItemAddedEvent
710
+ | ResponseOutputItemDoneEvent
711
+ | ResponseTextDeltaEvent
712
+ | ResponseOutputTextAnnotationAddedEvent
713
+ | ResponseFunctionCallArgumentsDeltaEvent
714
+ | ResponseReasoningSummaryPartAddedEvent
715
+ | ResponseReasoningSummaryPartDoneEvent
716
+ | ResponseReasoningSummaryTextDeltaEvent
717
+ | ResponseErrorEvent
718
+ | UnknownResponseStreamEvent
719
+
720
+ /**
721
+ * @since 1.0.0
722
+ */
723
+ export type Embedding = {
724
+ readonly embedding: ReadonlyArray<number> | string
725
+ readonly index: number
726
+ readonly object?: string | undefined
727
+ }
728
+
729
+ /**
730
+ * @since 1.0.0
731
+ */
732
+ export type CreateEmbeddingRequest = {
733
+ readonly input: string | ReadonlyArray<string> | ReadonlyArray<number> | ReadonlyArray<ReadonlyArray<number>>
734
+ readonly model: string
735
+ readonly encoding_format?: "float" | "base64" | undefined
736
+ readonly dimensions?: number | undefined
737
+ readonly user?: string | undefined
738
+ }
739
+
740
+ /**
741
+ * @since 1.0.0
742
+ */
743
+ export type CreateEmbeddingResponse = {
744
+ readonly data: ReadonlyArray<Embedding>
745
+ readonly model: string
746
+ readonly object?: "list" | undefined
747
+ readonly usage?: {
748
+ readonly prompt_tokens: number
749
+ readonly total_tokens: number
750
+ } | undefined
751
+ }
752
+
753
+ /**
754
+ * @since 1.0.0
755
+ */
756
+ export type CreateEmbeddingRequestJson = CreateEmbeddingRequest
757
+ /**
758
+ * @since 1.0.0
759
+ */
760
+ export type CreateEmbedding200 = CreateEmbeddingResponse
761
+ /**
762
+ * @since 1.0.0
763
+ */
764
+ export type ChatCompletionContentPart =
765
+ | {
766
+ readonly type: "text"
767
+ readonly text: string
768
+ }
769
+ | {
770
+ readonly type: "image_url"
771
+ readonly image_url: {
772
+ readonly url: string
773
+ readonly detail?: "low" | "high" | "auto" | undefined
774
+ }
775
+ }
776
+ /**
777
+ * @since 1.0.0
778
+ */
779
+ export type ChatCompletionRequestToolCall = {
780
+ readonly id: string
781
+ readonly type: "function"
782
+ readonly function: {
783
+ readonly name: string
784
+ readonly arguments: string
785
+ }
786
+ }
787
+ /**
788
+ * @since 1.0.0
789
+ */
790
+ export type ChatCompletionRequestMessage =
791
+ | {
792
+ readonly role: "system" | "developer" | "user" | "assistant"
793
+ readonly content: string | ReadonlyArray<ChatCompletionContentPart> | null
794
+ readonly tool_calls?: ReadonlyArray<ChatCompletionRequestToolCall> | undefined
795
+ }
796
+ | {
797
+ readonly role: "tool"
798
+ readonly tool_call_id: string
799
+ readonly content: string
800
+ }
801
+ /**
802
+ * @since 1.0.0
803
+ */
804
+ export type ChatCompletionTool = {
805
+ readonly type: "function"
806
+ readonly function: {
807
+ readonly name: string
808
+ readonly description?: string | null | undefined
809
+ readonly parameters?: JsonObject | undefined
810
+ readonly strict?: boolean | undefined
811
+ }
812
+ }
813
+ /**
814
+ * @since 1.0.0
815
+ */
816
+ export type ChatCompletionToolChoice =
817
+ | "none"
818
+ | "auto"
819
+ | "required"
820
+ | {
821
+ readonly type: "function"
822
+ readonly function: {
823
+ readonly name: string
824
+ }
825
+ }
826
+ /**
827
+ * @since 1.0.0
828
+ */
829
+ export type ChatCompletionResponseFormat =
830
+ | {
831
+ readonly type: "json_object"
832
+ }
833
+ | {
834
+ readonly type: "json_schema"
835
+ readonly json_schema: {
836
+ readonly name: string
837
+ readonly schema: JsonObject
838
+ readonly description?: string | undefined
839
+ readonly strict?: boolean | undefined
840
+ }
841
+ }
842
+ /**
843
+ * @since 1.0.0
844
+ */
845
+ export type ChatCompletionRequest = {
846
+ readonly model: string
847
+ readonly messages: ReadonlyArray<ChatCompletionRequestMessage>
848
+ readonly temperature?: number | null | undefined
849
+ readonly top_p?: number | null | undefined
850
+ readonly max_tokens?: number | null | undefined
851
+ readonly user?: string | null | undefined
852
+ readonly seed?: number | undefined
853
+ readonly parallel_tool_calls?: boolean | null | undefined
854
+ readonly response_format?: ChatCompletionResponseFormat | undefined
855
+ readonly tools?: ReadonlyArray<ChatCompletionTool> | undefined
856
+ readonly tool_choice?: ChatCompletionToolChoice | undefined
857
+ readonly service_tier?: string | undefined
858
+ readonly stream?: boolean | undefined
859
+ readonly stream_options?: {
860
+ readonly include_usage?: boolean | undefined
861
+ } | undefined
862
+ }
863
+ /**
864
+ * @since 1.0.0
865
+ */
866
+ export type CreateResponseRequestJson = ChatCompletionRequest
867
+ /**
868
+ * @since 1.0.0
869
+ */
870
+ export type CreateResponse200 = ChatCompletionResponse
871
+ /**
872
+ * @since 1.0.0
873
+ */
874
+ export type CreateResponse200Sse = ChatCompletionStreamEvent
875
+
876
+ const EmbeddingSchema = Schema.Struct({
877
+ embedding: Schema.Union([Schema.Array(Schema.Number), Schema.String]),
878
+ index: Schema.Number,
879
+ object: Schema.optionalKey(Schema.String)
880
+ })
881
+
882
+ const CreateEmbeddingResponseSchema = Schema.Struct({
883
+ data: Schema.Array(EmbeddingSchema),
884
+ model: Schema.String,
885
+ object: Schema.optionalKey(Schema.Literal("list")),
886
+ usage: Schema.optionalKey(Schema.Struct({
887
+ prompt_tokens: Schema.Number,
888
+ total_tokens: Schema.Number
889
+ }))
890
+ })
891
+
892
+ const ChatCompletionToolFunction = Schema.Struct({
893
+ name: Schema.String,
894
+ arguments: Schema.optionalKey(Schema.String)
895
+ })
896
+
897
+ const ChatCompletionToolCall = Schema.Struct({
898
+ id: Schema.optionalKey(Schema.String),
899
+ index: Schema.optionalKey(Schema.Number),
900
+ type: Schema.optionalKey(Schema.String),
901
+ function: Schema.optionalKey(ChatCompletionToolFunction)
902
+ })
903
+
904
+ const ChatCompletionMessage = Schema.Struct({
905
+ role: Schema.optionalKey(Schema.String),
906
+ content: Schema.optionalKey(Schema.NullOr(Schema.String)),
907
+ tool_calls: Schema.optionalKey(Schema.Array(ChatCompletionToolCall))
908
+ })
909
+
910
+ const ChatCompletionDelta = Schema.Struct({
911
+ role: Schema.optionalKey(Schema.String),
912
+ content: Schema.optionalKey(Schema.NullOr(Schema.String)),
913
+ tool_calls: Schema.optionalKey(Schema.Array(ChatCompletionToolCall))
914
+ })
915
+
916
+ const ChatCompletionChoice = Schema.Struct({
917
+ index: Schema.Number,
918
+ finish_reason: Schema.optionalKey(Schema.NullOr(Schema.String)),
919
+ message: Schema.optionalKey(ChatCompletionMessage),
920
+ delta: Schema.optionalKey(ChatCompletionDelta)
921
+ })
922
+
923
+ const ChatCompletionUsage = Schema.Struct({
924
+ prompt_tokens: Schema.Number,
925
+ completion_tokens: Schema.Number,
926
+ total_tokens: Schema.Number,
927
+ prompt_tokens_details: Schema.optionalKey(Schema.Any),
928
+ completion_tokens_details: Schema.optionalKey(Schema.Any)
929
+ })
930
+
931
+ const ChatCompletionResponse = Schema.Struct({
932
+ id: Schema.String,
933
+ model: Schema.String,
934
+ created: Schema.Number,
935
+ choices: Schema.Array(ChatCompletionChoice),
936
+ usage: Schema.optionalKey(Schema.NullOr(ChatCompletionUsage)),
937
+ service_tier: Schema.optionalKey(Schema.String)
938
+ })
939
+
940
+ const ChatCompletionChunk = Schema.Struct({
941
+ id: Schema.String,
942
+ model: Schema.String,
943
+ created: Schema.Number,
944
+ choices: Schema.Array(ChatCompletionChoice),
945
+ usage: Schema.optionalKey(Schema.NullOr(ChatCompletionUsage)),
946
+ service_tier: Schema.optionalKey(Schema.String)
947
+ })
948
+
949
+ /**
950
+ * @since 1.0.0
951
+ */
952
+ export type ChatCompletionToolCall = typeof ChatCompletionToolCall.Type
953
+ /**
954
+ * @since 1.0.0
955
+ */
956
+ export type ChatCompletionMessage = typeof ChatCompletionMessage.Type
957
+ /**
958
+ * @since 1.0.0
959
+ */
960
+ export type ChatCompletionChoice = typeof ChatCompletionChoice.Type
961
+ /**
962
+ * @since 1.0.0
963
+ */
964
+ export type ChatCompletionUsage = typeof ChatCompletionUsage.Type
965
+ /**
966
+ * @since 1.0.0
967
+ */
968
+ export type ChatCompletionResponse = typeof ChatCompletionResponse.Type
969
+ /**
970
+ * @since 1.0.0
971
+ */
972
+ export type ChatCompletionChunk = typeof ChatCompletionChunk.Type
973
+ /**
974
+ * @since 1.0.0
975
+ */
976
+ export type ChatCompletionStreamEvent = ChatCompletionChunk | "[DONE]"
977
+
978
+ const parseJson = (value: string): unknown => {
979
+ try {
980
+ return JSON.parse(value)
981
+ } catch {
982
+ return undefined
983
+ }
984
+ }
985
+
986
+ const isChatCompletionChunk = Schema.is(ChatCompletionChunk)
987
+
988
+ const decodeChatCompletionSseData = (
989
+ data: string
990
+ ): ChatCompletionStreamEvent | undefined => {
991
+ if (data === "[DONE]") {
992
+ return data
993
+ }
994
+ const parsed = parseJson(data)
995
+ return isChatCompletionChunk(parsed)
996
+ ? parsed
997
+ : undefined
998
+ }