@effect/ai-openai 4.0.0-beta.3 → 4.0.0-beta.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Generated.d.ts +4060 -4038
- package/dist/Generated.d.ts.map +1 -1
- package/dist/Generated.js +1 -1
- package/dist/Generated.js.map +1 -1
- package/dist/OpenAiClient.d.ts +3 -3
- package/dist/OpenAiClient.d.ts.map +1 -1
- package/dist/OpenAiError.d.ts +22 -32
- package/dist/OpenAiError.d.ts.map +1 -1
- package/dist/OpenAiLanguageModel.d.ts.map +1 -1
- package/dist/OpenAiLanguageModel.js +70 -19
- package/dist/OpenAiLanguageModel.js.map +1 -1
- package/dist/OpenAiTool.d.ts +24 -24
- package/package.json +3 -3
- package/src/Generated.ts +125 -122
- package/src/OpenAiClient.ts +3 -3
- package/src/OpenAiError.ts +24 -32
- package/src/OpenAiLanguageModel.ts +94 -19
package/src/OpenAiClient.ts
CHANGED
|
@@ -281,7 +281,7 @@ export const layerConfig = (options?: {
|
|
|
281
281
|
/**
|
|
282
282
|
* The config value to load for the API key.
|
|
283
283
|
*/
|
|
284
|
-
readonly apiKey?: Config.Config<Redacted.Redacted<string
|
|
284
|
+
readonly apiKey?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
|
|
285
285
|
|
|
286
286
|
/**
|
|
287
287
|
* The config value to load for the API URL.
|
|
@@ -291,12 +291,12 @@ export const layerConfig = (options?: {
|
|
|
291
291
|
/**
|
|
292
292
|
* The config value to load for the organization ID.
|
|
293
293
|
*/
|
|
294
|
-
readonly organizationId?: Config.Config<Redacted.Redacted<string
|
|
294
|
+
readonly organizationId?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
|
|
295
295
|
|
|
296
296
|
/**
|
|
297
297
|
* The config value to load for the project ID.
|
|
298
298
|
*/
|
|
299
|
-
readonly projectId?: Config.Config<Redacted.Redacted<string
|
|
299
|
+
readonly projectId?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
|
|
300
300
|
|
|
301
301
|
/**
|
|
302
302
|
* Optional transformer for the HTTP client.
|
package/src/OpenAiError.ts
CHANGED
|
@@ -57,51 +57,43 @@ export type OpenAiRateLimitMetadata = OpenAiErrorMetadata & {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
declare module "effect/unstable/ai/AiError" {
|
|
60
|
-
export interface
|
|
61
|
-
readonly
|
|
62
|
-
readonly openai?: OpenAiRateLimitMetadata | null
|
|
63
|
-
}
|
|
60
|
+
export interface RateLimitErrorMetadata {
|
|
61
|
+
readonly openai?: OpenAiRateLimitMetadata | null
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
export interface
|
|
67
|
-
readonly
|
|
68
|
-
readonly openai?: OpenAiErrorMetadata | null
|
|
69
|
-
}
|
|
64
|
+
export interface QuotaExhaustedErrorMetadata {
|
|
65
|
+
readonly openai?: OpenAiErrorMetadata | null
|
|
70
66
|
}
|
|
71
67
|
|
|
72
|
-
export interface
|
|
73
|
-
readonly
|
|
74
|
-
readonly openai?: OpenAiErrorMetadata | null
|
|
75
|
-
}
|
|
68
|
+
export interface AuthenticationErrorMetadata {
|
|
69
|
+
readonly openai?: OpenAiErrorMetadata | null
|
|
76
70
|
}
|
|
77
71
|
|
|
78
|
-
export interface
|
|
79
|
-
readonly
|
|
80
|
-
readonly openai?: OpenAiErrorMetadata | null
|
|
81
|
-
}
|
|
72
|
+
export interface ContentPolicyErrorMetadata {
|
|
73
|
+
readonly openai?: OpenAiErrorMetadata | null
|
|
82
74
|
}
|
|
83
75
|
|
|
84
|
-
export interface
|
|
85
|
-
readonly
|
|
86
|
-
readonly openai?: OpenAiErrorMetadata | null
|
|
87
|
-
}
|
|
76
|
+
export interface InvalidRequestErrorMetadata {
|
|
77
|
+
readonly openai?: OpenAiErrorMetadata | null
|
|
88
78
|
}
|
|
89
79
|
|
|
90
|
-
export interface
|
|
91
|
-
readonly
|
|
92
|
-
readonly openai?: OpenAiErrorMetadata | null
|
|
93
|
-
}
|
|
80
|
+
export interface InternalProviderErrorMetadata {
|
|
81
|
+
readonly openai?: OpenAiErrorMetadata | null
|
|
94
82
|
}
|
|
95
83
|
|
|
96
|
-
export interface
|
|
97
|
-
readonly
|
|
98
|
-
readonly openai?: OpenAiErrorMetadata | null
|
|
99
|
-
}
|
|
84
|
+
export interface InvalidOutputErrorMetadata {
|
|
85
|
+
readonly openai?: OpenAiErrorMetadata | null
|
|
100
86
|
}
|
|
101
87
|
|
|
102
|
-
export interface
|
|
103
|
-
readonly
|
|
104
|
-
|
|
105
|
-
|
|
88
|
+
export interface StructuredOutputErrorMetadata {
|
|
89
|
+
readonly openai?: OpenAiErrorMetadata | null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface UnsupportedSchemaErrorMetadata {
|
|
93
|
+
readonly openai?: OpenAiErrorMetadata | null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface UnknownErrorMetadata {
|
|
97
|
+
readonly openai?: OpenAiErrorMetadata | null
|
|
106
98
|
}
|
|
107
99
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as DateTime from "effect/DateTime"
|
|
10
10
|
import * as Effect from "effect/Effect"
|
|
11
|
-
import * as
|
|
11
|
+
import * as Encoding from "effect/Encoding"
|
|
12
12
|
import { dual } from "effect/Function"
|
|
13
13
|
import * as Layer from "effect/Layer"
|
|
14
14
|
import * as Predicate from "effect/Predicate"
|
|
@@ -23,6 +23,7 @@ import * as AiError from "effect/unstable/ai/AiError"
|
|
|
23
23
|
import * as IdGenerator from "effect/unstable/ai/IdGenerator"
|
|
24
24
|
import * as LanguageModel from "effect/unstable/ai/LanguageModel"
|
|
25
25
|
import * as AiModel from "effect/unstable/ai/Model"
|
|
26
|
+
import { toCodecOpenAI } from "effect/unstable/ai/OpenAiStructuredOutput"
|
|
26
27
|
import type * as Prompt from "effect/unstable/ai/Prompt"
|
|
27
28
|
import type * as Response from "effect/unstable/ai/Response"
|
|
28
29
|
import * as Tool from "effect/unstable/ai/Tool"
|
|
@@ -319,7 +320,7 @@ export const model = (
|
|
|
319
320
|
model: (string & {}) | Model,
|
|
320
321
|
config?: Omit<typeof Config.Service, "model">
|
|
321
322
|
): AiModel.Model<"openai", LanguageModel.LanguageModel, OpenAiClient> =>
|
|
322
|
-
AiModel.make("openai", layer({ model, config }))
|
|
323
|
+
AiModel.make("openai", model, layer({ model, config }))
|
|
323
324
|
|
|
324
325
|
// TODO
|
|
325
326
|
// /**
|
|
@@ -330,7 +331,7 @@ export const model = (
|
|
|
330
331
|
// model: (string & {}) | Model,
|
|
331
332
|
// config?: Omit<typeof Config.Service, "model">
|
|
332
333
|
// ): AiModel.Model<"openai", LanguageModel.LanguageModel | Tokenizer.Tokenizer, OpenAiClient> =>
|
|
333
|
-
// AiModel.make("openai", layerWithTokenizer({ model, config }))
|
|
334
|
+
// AiModel.make("openai", model, layerWithTokenizer({ model, config }))
|
|
334
335
|
|
|
335
336
|
/**
|
|
336
337
|
* Creates an OpenAI language model service.
|
|
@@ -369,7 +370,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
369
370
|
options,
|
|
370
371
|
toolNameMapper
|
|
371
372
|
})
|
|
372
|
-
const responseFormat = prepareResponseFormat({
|
|
373
|
+
const responseFormat = yield* prepareResponseFormat({
|
|
373
374
|
config,
|
|
374
375
|
options
|
|
375
376
|
})
|
|
@@ -389,6 +390,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
389
390
|
)
|
|
390
391
|
|
|
391
392
|
return yield* LanguageModel.make({
|
|
393
|
+
codecTransformer: toCodecOpenAI,
|
|
392
394
|
generateText: Effect.fnUntraced(
|
|
393
395
|
function*(options) {
|
|
394
396
|
const config = yield* makeConfig
|
|
@@ -592,7 +594,7 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
592
594
|
}
|
|
593
595
|
|
|
594
596
|
if (part.data instanceof Uint8Array) {
|
|
595
|
-
const base64 =
|
|
597
|
+
const base64 = Encoding.encodeBase64(part.data)
|
|
596
598
|
const imageUrl = `data:${mediaType};base64,${base64}`
|
|
597
599
|
content.push({ type: "input_image", image_url: imageUrl, detail })
|
|
598
600
|
}
|
|
@@ -606,7 +608,7 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
606
608
|
}
|
|
607
609
|
|
|
608
610
|
if (part.data instanceof Uint8Array) {
|
|
609
|
-
const base64 =
|
|
611
|
+
const base64 = Encoding.encodeBase64(part.data)
|
|
610
612
|
const fileName = part.fileName ?? `part-${index}.pdf`
|
|
611
613
|
const fileData = `data:application/pdf;base64,${base64}`
|
|
612
614
|
content.push({ type: "input_file", filename: fileName, file_data: fileData })
|
|
@@ -1036,10 +1038,11 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1036
1038
|
|
|
1037
1039
|
case "function_call": {
|
|
1038
1040
|
hasToolCalls = true
|
|
1041
|
+
|
|
1039
1042
|
const toolName = part.name
|
|
1040
|
-
|
|
1041
|
-
const
|
|
1042
|
-
try: () => Tool.unsafeSecureJsonParse(
|
|
1043
|
+
|
|
1044
|
+
const toolParams = yield* Effect.try({
|
|
1045
|
+
try: () => Tool.unsafeSecureJsonParse(part.arguments),
|
|
1043
1046
|
catch: (cause) =>
|
|
1044
1047
|
AiError.make({
|
|
1045
1048
|
module: "OpenAiLanguageModel",
|
|
@@ -1051,6 +1054,9 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1051
1054
|
})
|
|
1052
1055
|
})
|
|
1053
1056
|
})
|
|
1057
|
+
|
|
1058
|
+
const params = yield* transformToolCallParams(options.tools, part.name, toolParams)
|
|
1059
|
+
|
|
1054
1060
|
parts.push({
|
|
1055
1061
|
type: "tool-call",
|
|
1056
1062
|
id: part.call_id,
|
|
@@ -1730,11 +1736,14 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1730
1736
|
|
|
1731
1737
|
case "function_call": {
|
|
1732
1738
|
delete activeToolCalls[event.output_index]
|
|
1739
|
+
|
|
1733
1740
|
hasToolCalls = true
|
|
1741
|
+
|
|
1734
1742
|
const toolName = event.item.name
|
|
1735
|
-
const
|
|
1736
|
-
|
|
1737
|
-
|
|
1743
|
+
const toolArgs = event.item.arguments
|
|
1744
|
+
|
|
1745
|
+
const toolParams = yield* Effect.try({
|
|
1746
|
+
try: () => Tool.unsafeSecureJsonParse(toolArgs),
|
|
1738
1747
|
catch: (cause) =>
|
|
1739
1748
|
AiError.make({
|
|
1740
1749
|
module: "OpenAiLanguageModel",
|
|
@@ -1746,10 +1755,14 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1746
1755
|
})
|
|
1747
1756
|
})
|
|
1748
1757
|
})
|
|
1758
|
+
|
|
1759
|
+
const params = yield* transformToolCallParams(options.tools, toolName, toolParams)
|
|
1760
|
+
|
|
1749
1761
|
parts.push({
|
|
1750
1762
|
type: "tool-params-end",
|
|
1751
1763
|
id: event.item.call_id
|
|
1752
1764
|
})
|
|
1765
|
+
|
|
1753
1766
|
parts.push({
|
|
1754
1767
|
type: "tool-call",
|
|
1755
1768
|
id: event.item.call_id,
|
|
@@ -1757,6 +1770,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1757
1770
|
params,
|
|
1758
1771
|
metadata: { openai: { ...makeItemIdMetadata(event.item.id) } }
|
|
1759
1772
|
})
|
|
1773
|
+
|
|
1760
1774
|
break
|
|
1761
1775
|
}
|
|
1762
1776
|
|
|
@@ -2281,12 +2295,14 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2281
2295
|
for (const tool of allowedTools) {
|
|
2282
2296
|
if (Tool.isUserDefined(tool)) {
|
|
2283
2297
|
const strict = Tool.getStrictMode(tool) ?? config.strictJsonSchema ?? true
|
|
2298
|
+
const description = Tool.getDescription(tool)
|
|
2299
|
+
const parameters = yield* tryJsonSchema(tool.parametersSchema, "prepareTools")
|
|
2284
2300
|
tools.push({
|
|
2285
2301
|
type: "function",
|
|
2286
2302
|
name: tool.name,
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2303
|
+
parameters,
|
|
2304
|
+
strict,
|
|
2305
|
+
...(Predicate.isNotUndefined(description) ? { description } : undefined)
|
|
2290
2306
|
})
|
|
2291
2307
|
}
|
|
2292
2308
|
|
|
@@ -2480,23 +2496,45 @@ const makeItemIdMetadata = (itemId: string | undefined) => Predicate.isNotUndefi
|
|
|
2480
2496
|
const makeEncryptedContentMetadata = (encryptedContent: string | null | undefined) =>
|
|
2481
2497
|
Predicate.isNotNullish(encryptedContent) ? { encryptedContent } : undefined
|
|
2482
2498
|
|
|
2483
|
-
const
|
|
2499
|
+
const unsupportedSchemaError = (error: unknown, method: string): AiError.AiError =>
|
|
2500
|
+
AiError.make({
|
|
2501
|
+
module: "OpenAiLanguageModel",
|
|
2502
|
+
method,
|
|
2503
|
+
reason: new AiError.UnsupportedSchemaError({
|
|
2504
|
+
description: error instanceof Error ? error.message : String(error)
|
|
2505
|
+
})
|
|
2506
|
+
})
|
|
2507
|
+
|
|
2508
|
+
const tryCodecTransform = <S extends Schema.Top>(schema: S, method: string) =>
|
|
2509
|
+
Effect.try({
|
|
2510
|
+
try: () => toCodecOpenAI(schema),
|
|
2511
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
2512
|
+
})
|
|
2513
|
+
|
|
2514
|
+
const tryJsonSchema = <S extends Schema.Top>(schema: S, method: string) =>
|
|
2515
|
+
Effect.try({
|
|
2516
|
+
try: () => Tool.getJsonSchemaFromSchema(schema, { transformer: toCodecOpenAI }),
|
|
2517
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
2518
|
+
})
|
|
2519
|
+
|
|
2520
|
+
const prepareResponseFormat = Effect.fnUntraced(function*({ config, options }: {
|
|
2484
2521
|
readonly config: typeof Config.Service
|
|
2485
2522
|
readonly options: LanguageModel.ProviderOptions
|
|
2486
|
-
}): typeof Generated.TextResponseFormatConfiguration.Encoded
|
|
2523
|
+
}): Effect.fn.Return<typeof Generated.TextResponseFormatConfiguration.Encoded, AiError.AiError> {
|
|
2487
2524
|
if (options.responseFormat.type === "json") {
|
|
2488
2525
|
const name = options.responseFormat.objectName
|
|
2489
2526
|
const schema = options.responseFormat.schema
|
|
2527
|
+
const jsonSchema = yield* tryJsonSchema(schema, "prepareResponseFormat")
|
|
2490
2528
|
return {
|
|
2491
2529
|
type: "json_schema",
|
|
2492
2530
|
name,
|
|
2493
2531
|
description: AST.resolveDescription(schema.ast) ?? "Response with a JSON object",
|
|
2494
|
-
schema:
|
|
2532
|
+
schema: jsonSchema,
|
|
2495
2533
|
strict: config.strictJsonSchema ?? true
|
|
2496
2534
|
}
|
|
2497
2535
|
}
|
|
2498
2536
|
return { type: "text" }
|
|
2499
|
-
}
|
|
2537
|
+
})
|
|
2500
2538
|
|
|
2501
2539
|
interface ModelCapabilities {
|
|
2502
2540
|
readonly isReasoningModel: boolean
|
|
@@ -2606,3 +2644,40 @@ const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.U
|
|
|
2606
2644
|
}
|
|
2607
2645
|
}
|
|
2608
2646
|
}
|
|
2647
|
+
|
|
2648
|
+
const transformToolCallParams = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>(
|
|
2649
|
+
tools: Tools,
|
|
2650
|
+
toolName: string,
|
|
2651
|
+
toolParams: unknown
|
|
2652
|
+
): Effect.fn.Return<unknown, AiError.AiError> {
|
|
2653
|
+
const tool = tools.find((tool) => tool.name === toolName)
|
|
2654
|
+
|
|
2655
|
+
if (Predicate.isUndefined(tool)) {
|
|
2656
|
+
return yield* AiError.make({
|
|
2657
|
+
module: "OpenAiLanguageModel",
|
|
2658
|
+
method: "makeResponse",
|
|
2659
|
+
reason: new AiError.ToolNotFoundError({
|
|
2660
|
+
toolName,
|
|
2661
|
+
availableTools: tools.map((tool) => tool.name)
|
|
2662
|
+
})
|
|
2663
|
+
})
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
const { codec } = yield* tryCodecTransform(tool.parametersSchema, "makeResponse")
|
|
2667
|
+
|
|
2668
|
+
const transform = Schema.decodeEffect(codec)
|
|
2669
|
+
|
|
2670
|
+
return yield* (
|
|
2671
|
+
transform(toolParams) as Effect.Effect<unknown, Schema.SchemaError>
|
|
2672
|
+
).pipe(Effect.mapError((error) =>
|
|
2673
|
+
AiError.make({
|
|
2674
|
+
module: "OpenAiLanguageModel",
|
|
2675
|
+
method: "makeResponse",
|
|
2676
|
+
reason: new AiError.ToolParameterValidationError({
|
|
2677
|
+
toolName,
|
|
2678
|
+
toolParams,
|
|
2679
|
+
description: error.issue.toString()
|
|
2680
|
+
})
|
|
2681
|
+
})
|
|
2682
|
+
))
|
|
2683
|
+
})
|