@core-ai/openai 0.2.1 → 0.3.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.
- package/dist/index.js +262 -69
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
import OpenAI from "openai";
|
|
3
3
|
|
|
4
4
|
// src/chat-model.ts
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
StructuredOutputNoObjectGeneratedError,
|
|
7
|
+
StructuredOutputParseError,
|
|
8
|
+
StructuredOutputValidationError,
|
|
9
|
+
createObjectStreamResult,
|
|
10
|
+
createStreamResult
|
|
11
|
+
} from "@core-ai/core-ai";
|
|
6
12
|
|
|
7
13
|
// src/chat-adapter.ts
|
|
8
|
-
import { APIError } from "openai";
|
|
9
14
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
10
|
-
|
|
15
|
+
var DEFAULT_STRUCTURED_OUTPUT_TOOL_NAME = "core_ai_generate_object";
|
|
16
|
+
var DEFAULT_STRUCTURED_OUTPUT_TOOL_DESCRIPTION = "Return a JSON object that matches the requested schema.";
|
|
11
17
|
function convertMessages(messages) {
|
|
12
18
|
return messages.map(convertMessage);
|
|
13
19
|
}
|
|
@@ -91,38 +97,66 @@ function convertToolChoice(choice) {
|
|
|
91
97
|
}
|
|
92
98
|
};
|
|
93
99
|
}
|
|
100
|
+
function getStructuredOutputToolName(options) {
|
|
101
|
+
const trimmedName = options.schemaName?.trim();
|
|
102
|
+
if (trimmedName && trimmedName.length > 0) {
|
|
103
|
+
return trimmedName;
|
|
104
|
+
}
|
|
105
|
+
return DEFAULT_STRUCTURED_OUTPUT_TOOL_NAME;
|
|
106
|
+
}
|
|
107
|
+
function createStructuredOutputOptions(options) {
|
|
108
|
+
const toolName = getStructuredOutputToolName(options);
|
|
109
|
+
return {
|
|
110
|
+
messages: options.messages,
|
|
111
|
+
tools: {
|
|
112
|
+
structured_output: {
|
|
113
|
+
name: toolName,
|
|
114
|
+
description: options.schemaDescription ?? DEFAULT_STRUCTURED_OUTPUT_TOOL_DESCRIPTION,
|
|
115
|
+
parameters: options.schema
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
toolChoice: {
|
|
119
|
+
type: "tool",
|
|
120
|
+
toolName
|
|
121
|
+
},
|
|
122
|
+
config: options.config,
|
|
123
|
+
providerOptions: options.providerOptions,
|
|
124
|
+
signal: options.signal
|
|
125
|
+
};
|
|
126
|
+
}
|
|
94
127
|
function createGenerateRequest(modelId, options) {
|
|
95
128
|
return {
|
|
96
|
-
|
|
97
|
-
messages: convertMessages(options.messages),
|
|
98
|
-
...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
|
|
99
|
-
...options.toolChoice ? { tool_choice: convertToolChoice(options.toolChoice) } : {},
|
|
100
|
-
...options.config?.temperature !== void 0 ? { temperature: options.config.temperature } : {},
|
|
101
|
-
...options.config?.maxTokens !== void 0 ? { max_tokens: options.config.maxTokens } : {},
|
|
102
|
-
...options.config?.topP !== void 0 ? { top_p: options.config.topP } : {},
|
|
103
|
-
...options.config?.stopSequences ? { stop: options.config.stopSequences } : {},
|
|
104
|
-
...options.config?.frequencyPenalty !== void 0 ? { frequency_penalty: options.config.frequencyPenalty } : {},
|
|
105
|
-
...options.config?.presencePenalty !== void 0 ? { presence_penalty: options.config.presencePenalty } : {},
|
|
129
|
+
...createRequestBase(modelId, options),
|
|
106
130
|
...options.providerOptions
|
|
107
131
|
};
|
|
108
132
|
}
|
|
109
133
|
function createStreamRequest(modelId, options) {
|
|
110
134
|
return {
|
|
111
|
-
|
|
112
|
-
messages: convertMessages(options.messages),
|
|
135
|
+
...createRequestBase(modelId, options),
|
|
113
136
|
stream: true,
|
|
114
137
|
stream_options: {
|
|
115
138
|
include_usage: true
|
|
116
139
|
},
|
|
140
|
+
...options.providerOptions
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function createRequestBase(modelId, options) {
|
|
144
|
+
return {
|
|
145
|
+
model: modelId,
|
|
146
|
+
messages: convertMessages(options.messages),
|
|
117
147
|
...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
|
|
118
148
|
...options.toolChoice ? { tool_choice: convertToolChoice(options.toolChoice) } : {},
|
|
119
|
-
...options.config
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
...
|
|
125
|
-
...
|
|
149
|
+
...mapConfigToRequestFields(options.config)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function mapConfigToRequestFields(config) {
|
|
153
|
+
return {
|
|
154
|
+
...config?.temperature !== void 0 ? { temperature: config.temperature } : {},
|
|
155
|
+
...config?.maxTokens !== void 0 ? { max_tokens: config.maxTokens } : {},
|
|
156
|
+
...config?.topP !== void 0 ? { top_p: config.topP } : {},
|
|
157
|
+
...config?.stopSequences ? { stop: config.stopSequences } : {},
|
|
158
|
+
...config?.frequencyPenalty !== void 0 ? { frequency_penalty: config.frequencyPenalty } : {},
|
|
159
|
+
...config?.presencePenalty !== void 0 ? { presence_penalty: config.presencePenalty } : {}
|
|
126
160
|
};
|
|
127
161
|
}
|
|
128
162
|
function mapGenerateResponse(response) {
|
|
@@ -217,7 +251,9 @@ async function* transformStream(stream) {
|
|
|
217
251
|
}
|
|
218
252
|
if (choice.delta.tool_calls) {
|
|
219
253
|
for (const partialToolCall of choice.delta.tool_calls) {
|
|
220
|
-
const current = bufferedToolCalls.get(
|
|
254
|
+
const current = bufferedToolCalls.get(
|
|
255
|
+
partialToolCall.index
|
|
256
|
+
) ?? {
|
|
221
257
|
id: partialToolCall.id ?? `tool-${partialToolCall.index}`,
|
|
222
258
|
name: partialToolCall.function?.name ?? "",
|
|
223
259
|
arguments: ""
|
|
@@ -284,7 +320,11 @@ function safeParseJsonObject(json) {
|
|
|
284
320
|
return {};
|
|
285
321
|
}
|
|
286
322
|
}
|
|
287
|
-
|
|
323
|
+
|
|
324
|
+
// src/openai-error.ts
|
|
325
|
+
import { APIError } from "openai";
|
|
326
|
+
import { ProviderError } from "@core-ai/core-ai";
|
|
327
|
+
function wrapOpenAIError(error) {
|
|
288
328
|
if (error instanceof APIError) {
|
|
289
329
|
return new ProviderError(error.message, "openai", error.status, error);
|
|
290
330
|
}
|
|
@@ -298,33 +338,210 @@ function wrapError(error) {
|
|
|
298
338
|
|
|
299
339
|
// src/chat-model.ts
|
|
300
340
|
function createOpenAIChatModel(client, modelId) {
|
|
341
|
+
const provider = "openai";
|
|
342
|
+
async function callOpenAIChatCompletionsApi(request) {
|
|
343
|
+
try {
|
|
344
|
+
return await client.chat.completions.create(
|
|
345
|
+
request
|
|
346
|
+
);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
throw wrapOpenAIError(error);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async function generateChat(options) {
|
|
352
|
+
const request = createGenerateRequest(modelId, options);
|
|
353
|
+
const response = await callOpenAIChatCompletionsApi(request);
|
|
354
|
+
return mapGenerateResponse(response);
|
|
355
|
+
}
|
|
356
|
+
async function streamChat(options) {
|
|
357
|
+
const request = createStreamRequest(modelId, options);
|
|
358
|
+
const stream = await callOpenAIChatCompletionsApi(request);
|
|
359
|
+
return createStreamResult(transformStream(stream));
|
|
360
|
+
}
|
|
301
361
|
return {
|
|
302
|
-
provider
|
|
362
|
+
provider,
|
|
303
363
|
modelId,
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
364
|
+
generate: generateChat,
|
|
365
|
+
stream: streamChat,
|
|
366
|
+
async generateObject(options) {
|
|
367
|
+
const structuredOptions = createStructuredOutputOptions(options);
|
|
368
|
+
const result = await generateChat(structuredOptions);
|
|
369
|
+
const toolName = getStructuredOutputToolName(options);
|
|
370
|
+
const object = extractStructuredObject(
|
|
371
|
+
result,
|
|
372
|
+
options.schema,
|
|
373
|
+
provider,
|
|
374
|
+
toolName
|
|
375
|
+
);
|
|
376
|
+
return {
|
|
377
|
+
object,
|
|
378
|
+
finishReason: result.finishReason,
|
|
379
|
+
usage: result.usage
|
|
380
|
+
};
|
|
312
381
|
},
|
|
313
|
-
async
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
382
|
+
async streamObject(options) {
|
|
383
|
+
const structuredOptions = createStructuredOutputOptions(options);
|
|
384
|
+
const stream = await streamChat(structuredOptions);
|
|
385
|
+
const toolName = getStructuredOutputToolName(options);
|
|
386
|
+
return createObjectStreamResult(
|
|
387
|
+
transformStructuredOutputStream(
|
|
388
|
+
stream,
|
|
389
|
+
options.schema,
|
|
390
|
+
provider,
|
|
391
|
+
toolName
|
|
392
|
+
)
|
|
393
|
+
);
|
|
321
394
|
}
|
|
322
395
|
};
|
|
323
396
|
}
|
|
397
|
+
function extractStructuredObject(result, schema, provider, toolName) {
|
|
398
|
+
const structuredToolCall = result.toolCalls.find(
|
|
399
|
+
(toolCall) => toolCall.name === toolName
|
|
400
|
+
);
|
|
401
|
+
if (structuredToolCall) {
|
|
402
|
+
return validateStructuredToolArguments(
|
|
403
|
+
schema,
|
|
404
|
+
structuredToolCall.arguments,
|
|
405
|
+
provider
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
const rawOutput = result.content?.trim();
|
|
409
|
+
if (rawOutput && rawOutput.length > 0) {
|
|
410
|
+
return parseAndValidateStructuredPayload(schema, rawOutput, provider);
|
|
411
|
+
}
|
|
412
|
+
throw new StructuredOutputNoObjectGeneratedError(
|
|
413
|
+
"model did not emit a structured object payload",
|
|
414
|
+
provider
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
async function* transformStructuredOutputStream(stream, schema, provider, toolName) {
|
|
418
|
+
let validatedObject;
|
|
419
|
+
let contentBuffer = "";
|
|
420
|
+
const toolArgumentDeltas = /* @__PURE__ */ new Map();
|
|
421
|
+
for await (const event of stream) {
|
|
422
|
+
if (event.type === "content-delta") {
|
|
423
|
+
contentBuffer += event.text;
|
|
424
|
+
yield {
|
|
425
|
+
type: "object-delta",
|
|
426
|
+
text: event.text
|
|
427
|
+
};
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
if (event.type === "tool-call-delta") {
|
|
431
|
+
const previous = toolArgumentDeltas.get(event.toolCallId) ?? "";
|
|
432
|
+
toolArgumentDeltas.set(
|
|
433
|
+
event.toolCallId,
|
|
434
|
+
`${previous}${event.argumentsDelta}`
|
|
435
|
+
);
|
|
436
|
+
yield {
|
|
437
|
+
type: "object-delta",
|
|
438
|
+
text: event.argumentsDelta
|
|
439
|
+
};
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (event.type === "tool-call-end" && event.toolCall.name === toolName) {
|
|
443
|
+
validatedObject = validateStructuredToolArguments(
|
|
444
|
+
schema,
|
|
445
|
+
event.toolCall.arguments,
|
|
446
|
+
provider
|
|
447
|
+
);
|
|
448
|
+
yield {
|
|
449
|
+
type: "object",
|
|
450
|
+
object: validatedObject
|
|
451
|
+
};
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (event.type === "finish") {
|
|
455
|
+
if (validatedObject === void 0) {
|
|
456
|
+
const fallbackPayload = getFallbackStructuredPayload(
|
|
457
|
+
contentBuffer,
|
|
458
|
+
toolArgumentDeltas
|
|
459
|
+
);
|
|
460
|
+
if (!fallbackPayload) {
|
|
461
|
+
throw new StructuredOutputNoObjectGeneratedError(
|
|
462
|
+
"structured output stream ended without an object payload",
|
|
463
|
+
provider
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
validatedObject = parseAndValidateStructuredPayload(
|
|
467
|
+
schema,
|
|
468
|
+
fallbackPayload,
|
|
469
|
+
provider
|
|
470
|
+
);
|
|
471
|
+
yield {
|
|
472
|
+
type: "object",
|
|
473
|
+
object: validatedObject
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
yield {
|
|
477
|
+
type: "finish",
|
|
478
|
+
finishReason: event.finishReason,
|
|
479
|
+
usage: event.usage
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
function getFallbackStructuredPayload(contentBuffer, toolArgumentDeltas) {
|
|
485
|
+
for (const delta of toolArgumentDeltas.values()) {
|
|
486
|
+
const trimmed = delta.trim();
|
|
487
|
+
if (trimmed.length > 0) {
|
|
488
|
+
return trimmed;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
const trimmedContent = contentBuffer.trim();
|
|
492
|
+
if (trimmedContent.length > 0) {
|
|
493
|
+
return trimmedContent;
|
|
494
|
+
}
|
|
495
|
+
return void 0;
|
|
496
|
+
}
|
|
497
|
+
function validateStructuredToolArguments(schema, toolArguments, provider) {
|
|
498
|
+
return validateStructuredObject(
|
|
499
|
+
schema,
|
|
500
|
+
toolArguments,
|
|
501
|
+
provider,
|
|
502
|
+
JSON.stringify(toolArguments)
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
function parseAndValidateStructuredPayload(schema, rawPayload, provider) {
|
|
506
|
+
const parsedPayload = parseJson(rawPayload, provider);
|
|
507
|
+
return validateStructuredObject(schema, parsedPayload, provider, rawPayload);
|
|
508
|
+
}
|
|
509
|
+
function parseJson(rawOutput, provider) {
|
|
510
|
+
try {
|
|
511
|
+
return JSON.parse(rawOutput);
|
|
512
|
+
} catch (error) {
|
|
513
|
+
throw new StructuredOutputParseError(
|
|
514
|
+
"failed to parse structured output as JSON",
|
|
515
|
+
provider,
|
|
516
|
+
{
|
|
517
|
+
rawOutput,
|
|
518
|
+
cause: error
|
|
519
|
+
}
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function validateStructuredObject(schema, value, provider, rawOutput) {
|
|
524
|
+
const parsed = schema.safeParse(value);
|
|
525
|
+
if (parsed.success) {
|
|
526
|
+
return parsed.data;
|
|
527
|
+
}
|
|
528
|
+
throw new StructuredOutputValidationError(
|
|
529
|
+
"structured output does not match schema",
|
|
530
|
+
provider,
|
|
531
|
+
formatZodIssues(parsed.error.issues),
|
|
532
|
+
{
|
|
533
|
+
rawOutput
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
function formatZodIssues(issues) {
|
|
538
|
+
return issues.map((issue) => {
|
|
539
|
+
const path = issue.path.length > 0 ? issue.path.map((segment) => String(segment)).join(".") : "<root>";
|
|
540
|
+
return `${path}: ${issue.message}`;
|
|
541
|
+
});
|
|
542
|
+
}
|
|
324
543
|
|
|
325
544
|
// src/embedding-model.ts
|
|
326
|
-
import { APIError as APIError2 } from "openai";
|
|
327
|
-
import { ProviderError as ProviderError2 } from "@core-ai/core-ai";
|
|
328
545
|
function createOpenAIEmbeddingModel(client, modelId) {
|
|
329
546
|
return {
|
|
330
547
|
provider: "openai",
|
|
@@ -344,26 +561,13 @@ function createOpenAIEmbeddingModel(client, modelId) {
|
|
|
344
561
|
}
|
|
345
562
|
};
|
|
346
563
|
} catch (error) {
|
|
347
|
-
throw
|
|
564
|
+
throw wrapOpenAIError(error);
|
|
348
565
|
}
|
|
349
566
|
}
|
|
350
567
|
};
|
|
351
568
|
}
|
|
352
|
-
function wrapError2(error) {
|
|
353
|
-
if (error instanceof APIError2) {
|
|
354
|
-
return new ProviderError2(error.message, "openai", error.status, error);
|
|
355
|
-
}
|
|
356
|
-
return new ProviderError2(
|
|
357
|
-
error instanceof Error ? error.message : String(error),
|
|
358
|
-
"openai",
|
|
359
|
-
void 0,
|
|
360
|
-
error
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
569
|
|
|
364
570
|
// src/image-model.ts
|
|
365
|
-
import { APIError as APIError3 } from "openai";
|
|
366
|
-
import { ProviderError as ProviderError3 } from "@core-ai/core-ai";
|
|
367
571
|
function createOpenAIImageModel(client, modelId) {
|
|
368
572
|
return {
|
|
369
573
|
provider: "openai",
|
|
@@ -388,22 +592,11 @@ function createOpenAIImageModel(client, modelId) {
|
|
|
388
592
|
}))
|
|
389
593
|
};
|
|
390
594
|
} catch (error) {
|
|
391
|
-
throw
|
|
595
|
+
throw wrapOpenAIError(error);
|
|
392
596
|
}
|
|
393
597
|
}
|
|
394
598
|
};
|
|
395
599
|
}
|
|
396
|
-
function wrapError3(error) {
|
|
397
|
-
if (error instanceof APIError3) {
|
|
398
|
-
return new ProviderError3(error.message, "openai", error.status, error);
|
|
399
|
-
}
|
|
400
|
-
return new ProviderError3(
|
|
401
|
-
error instanceof Error ? error.message : String(error),
|
|
402
|
-
"openai",
|
|
403
|
-
void 0,
|
|
404
|
-
error
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
600
|
|
|
408
601
|
// src/provider.ts
|
|
409
602
|
function createOpenAI(options = {}) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@core-ai/openai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "OpenAI provider package for @core-ai/core-ai",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Omnifact (https://omnifact.ai)",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"test:watch": "vitest"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@core-ai/core-ai": "^0.
|
|
46
|
+
"@core-ai/core-ai": "^0.3.0",
|
|
47
47
|
"openai": "^6.1.0",
|
|
48
48
|
"zod-to-json-schema": "^3.25.1"
|
|
49
49
|
},
|