@aigne/openai 0.11.3 → 0.12.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/CHANGELOG.md +38 -0
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/openai-chat-model.d.ts +4 -4
- package/lib/cjs/openai-chat-model.js +8 -22
- package/lib/cjs/openai-image-model.d.ts +55 -0
- package/lib/cjs/openai-image-model.js +97 -0
- package/lib/cjs/openai.d.ts +4 -0
- package/lib/cjs/openai.js +17 -0
- package/lib/dts/index.d.ts +1 -0
- package/lib/dts/openai-chat-model.d.ts +4 -4
- package/lib/dts/openai-image-model.d.ts +55 -0
- package/lib/dts/openai.d.ts +4 -0
- package/lib/esm/index.d.ts +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/openai-chat-model.d.ts +4 -4
- package/lib/esm/openai-chat-model.js +7 -18
- package/lib/esm/openai-image-model.d.ts +55 -0
- package/lib/esm/openai-image-model.js +93 -0
- package/lib/esm/openai.d.ts +4 -0
- package/lib/esm/openai.js +10 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.12.0](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.11.5...openai-v0.12.0) (2025-08-20)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add ImageModel/ImageAgent support ([#383](https://github.com/AIGNE-io/aigne-framework/issues/383)) ([96a2093](https://github.com/AIGNE-io/aigne-framework/commit/96a209368d91d98f47db6de1e404640368a86fa8))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* The following workspace dependencies were updated
|
|
14
|
+
* dependencies
|
|
15
|
+
* @aigne/core bumped to 1.53.0
|
|
16
|
+
* devDependencies
|
|
17
|
+
* @aigne/test-utils bumped to 0.5.32
|
|
18
|
+
|
|
19
|
+
## [0.11.5](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.11.4...openai-v0.11.5) (2025-08-20)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Dependencies
|
|
23
|
+
|
|
24
|
+
* The following workspace dependencies were updated
|
|
25
|
+
* dependencies
|
|
26
|
+
* @aigne/core bumped to 1.52.0
|
|
27
|
+
* devDependencies
|
|
28
|
+
* @aigne/test-utils bumped to 0.5.31
|
|
29
|
+
|
|
30
|
+
## [0.11.4](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.11.3...openai-v0.11.4) (2025-08-18)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Dependencies
|
|
34
|
+
|
|
35
|
+
* The following workspace dependencies were updated
|
|
36
|
+
* dependencies
|
|
37
|
+
* @aigne/core bumped to 1.51.0
|
|
38
|
+
* devDependencies
|
|
39
|
+
* @aigne/test-utils bumped to 0.5.30
|
|
40
|
+
|
|
3
41
|
## [0.11.3](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.11.2...openai-v0.11.3) (2025-08-16)
|
|
4
42
|
|
|
5
43
|
|
package/lib/cjs/index.d.ts
CHANGED
package/lib/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
|
|
2
2
|
import { type PromiseOrValue } from "@aigne/core/utils/type-utils.js";
|
|
3
|
-
import
|
|
3
|
+
import type { ClientOptions, OpenAI } from "openai";
|
|
4
4
|
import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
export interface OpenAIChatModelCapabilities {
|
|
@@ -131,12 +131,12 @@ export declare class OpenAIChatModel extends ChatModel {
|
|
|
131
131
|
protected supportsToolsEmptyParameters: boolean;
|
|
132
132
|
protected supportsToolStreaming: boolean;
|
|
133
133
|
protected supportsTemperature: boolean;
|
|
134
|
-
client():
|
|
135
|
-
|
|
134
|
+
get client(): OpenAI;
|
|
135
|
+
get credential(): {
|
|
136
136
|
url: string | undefined;
|
|
137
137
|
apiKey: string | undefined;
|
|
138
138
|
model: string;
|
|
139
|
-
}
|
|
139
|
+
};
|
|
140
140
|
get modelOptions(): ChatModelOptions | undefined;
|
|
141
141
|
/**
|
|
142
142
|
* Process the input and generate a response
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.OpenAIChatModel = exports.openAIChatModelOptionsSchema = void 0;
|
|
7
4
|
exports.contentsFromInputMessages = contentsFromInputMessages;
|
|
@@ -14,9 +11,9 @@ const prompts_js_1 = require("@aigne/core/utils/prompts.js");
|
|
|
14
11
|
const stream_utils_js_1 = require("@aigne/core/utils/stream-utils.js");
|
|
15
12
|
const type_utils_js_1 = require("@aigne/core/utils/type-utils.js");
|
|
16
13
|
const ajv_1 = require("ajv");
|
|
17
|
-
const openai_1 = __importDefault(require("openai"));
|
|
18
14
|
const uuid_1 = require("uuid");
|
|
19
15
|
const zod_1 = require("zod");
|
|
16
|
+
const openai_js_1 = require("./openai.js");
|
|
20
17
|
const CHAT_MODEL_OPENAI_DEFAULT_MODEL = "gpt-4o-mini";
|
|
21
18
|
const OPENAI_CHAT_MODEL_CAPABILITIES = {
|
|
22
19
|
"o4-mini": { supportsParallelToolCalls: false, supportsTemperature: false },
|
|
@@ -81,18 +78,18 @@ class OpenAIChatModel extends core_1.ChatModel {
|
|
|
81
78
|
supportsToolsEmptyParameters = true;
|
|
82
79
|
supportsToolStreaming = true;
|
|
83
80
|
supportsTemperature = true;
|
|
84
|
-
|
|
85
|
-
const { apiKey, url } =
|
|
81
|
+
get client() {
|
|
82
|
+
const { apiKey, url } = this.credential;
|
|
86
83
|
if (!apiKey)
|
|
87
84
|
throw new Error(`${this.name} requires an API key. Please provide it via \`options.apiKey\`, or set the \`${this.apiKeyEnvName}\` environment variable`);
|
|
88
|
-
this._client ??= new CustomOpenAI({
|
|
85
|
+
this._client ??= new openai_js_1.CustomOpenAI({
|
|
89
86
|
baseURL: url,
|
|
90
87
|
apiKey,
|
|
91
88
|
...this.options?.clientOptions,
|
|
92
89
|
});
|
|
93
90
|
return this._client;
|
|
94
91
|
}
|
|
95
|
-
|
|
92
|
+
get credential() {
|
|
96
93
|
return {
|
|
97
94
|
url: this.options?.baseURL || process.env.OPENAI_BASE_URL,
|
|
98
95
|
apiKey: this.options?.apiKey || process.env[this.apiKeyEnvName] || this.apiKeyDefault,
|
|
@@ -113,7 +110,7 @@ class OpenAIChatModel extends core_1.ChatModel {
|
|
|
113
110
|
ajv = new ajv_1.Ajv();
|
|
114
111
|
async _process(input) {
|
|
115
112
|
const messages = await this.getRunMessages(input);
|
|
116
|
-
const
|
|
113
|
+
const model = input.modelOptions?.model || this.credential.model;
|
|
117
114
|
const body = {
|
|
118
115
|
model,
|
|
119
116
|
temperature: this.supportsTemperature
|
|
@@ -134,8 +131,7 @@ class OpenAIChatModel extends core_1.ChatModel {
|
|
|
134
131
|
return await this.requestStructuredOutput(body, input.responseFormat);
|
|
135
132
|
}
|
|
136
133
|
const { jsonMode, responseFormat } = await this.getRunResponseFormat(input);
|
|
137
|
-
const
|
|
138
|
-
const stream = (await client.chat.completions.create({
|
|
134
|
+
const stream = (await this.client.chat.completions.create({
|
|
139
135
|
...body,
|
|
140
136
|
tools: toolsFromInputTools(input.tools, {
|
|
141
137
|
addTypeToEmptyParameters: !this.supportsToolsEmptyParameters,
|
|
@@ -214,8 +210,7 @@ class OpenAIChatModel extends core_1.ChatModel {
|
|
|
214
210
|
const { jsonMode, responseFormat: resolvedResponseFormat } = await this.getRunResponseFormat({
|
|
215
211
|
responseFormat,
|
|
216
212
|
});
|
|
217
|
-
const
|
|
218
|
-
const res = (await client.chat.completions.create({
|
|
213
|
+
const res = (await this.client.chat.completions.create({
|
|
219
214
|
...body,
|
|
220
215
|
response_format: resolvedResponseFormat,
|
|
221
216
|
}));
|
|
@@ -428,13 +423,4 @@ function handleCompleteToolCall(toolCalls, call) {
|
|
|
428
423
|
args: call.function?.arguments || "",
|
|
429
424
|
});
|
|
430
425
|
}
|
|
431
|
-
// Use a custom OpenAI client to handle API errors for better error messages
|
|
432
|
-
class CustomOpenAI extends openai_1.default {
|
|
433
|
-
makeStatusError(status, error, message, headers) {
|
|
434
|
-
if (!("error" in error) || typeof error.error !== "string") {
|
|
435
|
-
message = JSON.stringify(error);
|
|
436
|
-
}
|
|
437
|
-
return super.makeStatusError(status, error, message, headers);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
426
|
// safeParseJSON is now imported from @aigne/core
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ImageModel, type ImageModelInput, type ImageModelOptions, type ImageModelOutput } from "@aigne/core";
|
|
2
|
+
import { type Camelize } from "@aigne/core/utils/camelize.js";
|
|
3
|
+
import type OpenAI from "openai";
|
|
4
|
+
import type { ClientOptions } from "openai";
|
|
5
|
+
export interface OpenAIImageModelInput extends ImageModelInput, Camelize<Omit<OpenAI.ImageGenerateParams, "prompt" | "model" | "n" | "response_format">> {
|
|
6
|
+
}
|
|
7
|
+
export interface OpenAIImageModelOutput extends ImageModelOutput {
|
|
8
|
+
}
|
|
9
|
+
export interface OpenAIImageModelOptions extends ImageModelOptions<OpenAIImageModelInput, OpenAIImageModelOutput> {
|
|
10
|
+
/**
|
|
11
|
+
* API key for OpenAI API
|
|
12
|
+
*
|
|
13
|
+
* If not provided, will look for OPENAI_API_KEY in environment variables
|
|
14
|
+
*/
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Base URL for OpenAI API
|
|
18
|
+
*
|
|
19
|
+
* Useful for proxies or alternate endpoints
|
|
20
|
+
*/
|
|
21
|
+
baseURL?: string;
|
|
22
|
+
/**
|
|
23
|
+
* OpenAI model to use
|
|
24
|
+
*
|
|
25
|
+
* Defaults to 'dall-e-2'
|
|
26
|
+
*/
|
|
27
|
+
model?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Additional model options to control behavior
|
|
30
|
+
*/
|
|
31
|
+
modelOptions?: Omit<Partial<OpenAIImageModelInput>, "model">;
|
|
32
|
+
/**
|
|
33
|
+
* Client options for OpenAI API
|
|
34
|
+
*/
|
|
35
|
+
clientOptions?: Partial<ClientOptions>;
|
|
36
|
+
}
|
|
37
|
+
export declare class OpenAIImageModel extends ImageModel<OpenAIImageModelInput, OpenAIImageModelOutput> {
|
|
38
|
+
options?: OpenAIImageModelOptions | undefined;
|
|
39
|
+
constructor(options?: OpenAIImageModelOptions | undefined);
|
|
40
|
+
protected _client?: OpenAI;
|
|
41
|
+
protected apiKeyEnvName: string;
|
|
42
|
+
get client(): OpenAI;
|
|
43
|
+
get credential(): {
|
|
44
|
+
url: string | undefined;
|
|
45
|
+
apiKey: string | undefined;
|
|
46
|
+
model: string;
|
|
47
|
+
};
|
|
48
|
+
get modelOptions(): Omit<Partial<OpenAIImageModelInput>, "model"> | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Process the input and generate a response
|
|
51
|
+
* @param input The input to process
|
|
52
|
+
* @returns The generated response
|
|
53
|
+
*/
|
|
54
|
+
process(input: ImageModelInput): Promise<ImageModelOutput>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenAIImageModel = void 0;
|
|
4
|
+
const core_1 = require("@aigne/core");
|
|
5
|
+
const camelize_js_1 = require("@aigne/core/utils/camelize.js");
|
|
6
|
+
const type_utils_js_1 = require("@aigne/core/utils/type-utils.js");
|
|
7
|
+
const zod_1 = require("zod");
|
|
8
|
+
const openai_js_1 = require("./openai.js");
|
|
9
|
+
const DEFAULT_MODEL = "dall-e-2";
|
|
10
|
+
const openAIImageModelInputSchema = core_1.imageModelInputSchema.extend({});
|
|
11
|
+
const openAIImageModelOptionsSchema = zod_1.z.object({
|
|
12
|
+
apiKey: zod_1.z.string().optional(),
|
|
13
|
+
baseURL: zod_1.z.string().optional(),
|
|
14
|
+
model: zod_1.z.string().optional(),
|
|
15
|
+
});
|
|
16
|
+
class OpenAIImageModel extends core_1.ImageModel {
|
|
17
|
+
options;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
super({
|
|
20
|
+
...options,
|
|
21
|
+
inputSchema: openAIImageModelInputSchema,
|
|
22
|
+
description: options?.description ?? "Draw or edit image by OpenAI image models",
|
|
23
|
+
});
|
|
24
|
+
this.options = options;
|
|
25
|
+
if (options)
|
|
26
|
+
(0, type_utils_js_1.checkArguments)(this.name, openAIImageModelOptionsSchema, options);
|
|
27
|
+
}
|
|
28
|
+
_client;
|
|
29
|
+
apiKeyEnvName = "OPENAI_API_KEY";
|
|
30
|
+
get client() {
|
|
31
|
+
if (this._client)
|
|
32
|
+
return this._client;
|
|
33
|
+
const { apiKey, url } = this.credential;
|
|
34
|
+
if (!apiKey)
|
|
35
|
+
throw new Error(`${this.name} requires an API key. Please provide it via \`options.apiKey\`, or set the \`${this.apiKeyEnvName}\` environment variable`);
|
|
36
|
+
this._client ??= new openai_js_1.CustomOpenAI({
|
|
37
|
+
baseURL: url,
|
|
38
|
+
apiKey,
|
|
39
|
+
...this.options?.clientOptions,
|
|
40
|
+
});
|
|
41
|
+
return this._client;
|
|
42
|
+
}
|
|
43
|
+
get credential() {
|
|
44
|
+
return {
|
|
45
|
+
url: this.options?.baseURL || process.env.OPENAI_BASE_URL,
|
|
46
|
+
apiKey: this.options?.apiKey || process.env[this.apiKeyEnvName],
|
|
47
|
+
model: this.options?.model || DEFAULT_MODEL,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
get modelOptions() {
|
|
51
|
+
return this.options?.modelOptions;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Process the input and generate a response
|
|
55
|
+
* @param input The input to process
|
|
56
|
+
* @returns The generated response
|
|
57
|
+
*/
|
|
58
|
+
async process(input) {
|
|
59
|
+
const model = input.model || this.credential.model;
|
|
60
|
+
const inputKeys = [
|
|
61
|
+
"background",
|
|
62
|
+
"moderation",
|
|
63
|
+
"output_compression",
|
|
64
|
+
"output_format",
|
|
65
|
+
"prompt",
|
|
66
|
+
"quality",
|
|
67
|
+
"size",
|
|
68
|
+
"style",
|
|
69
|
+
"user",
|
|
70
|
+
];
|
|
71
|
+
let responseFormat;
|
|
72
|
+
if (model !== "gpt-image-1") {
|
|
73
|
+
responseFormat = input.responseFormat === "base64" ? "b64_json" : "url";
|
|
74
|
+
}
|
|
75
|
+
const body = {
|
|
76
|
+
...(0, camelize_js_1.snakelize)((0, type_utils_js_1.pick)({ ...this.modelOptions, ...input }, inputKeys)),
|
|
77
|
+
response_format: responseFormat,
|
|
78
|
+
model,
|
|
79
|
+
};
|
|
80
|
+
const response = await this.client.images.generate({ ...body });
|
|
81
|
+
return {
|
|
82
|
+
images: (response.data ?? []).map((image) => {
|
|
83
|
+
if (image.url)
|
|
84
|
+
return { url: image.url };
|
|
85
|
+
if (image.b64_json)
|
|
86
|
+
return { base64: image.b64_json };
|
|
87
|
+
throw new Error("Image response does not contain a valid URL or base64 data");
|
|
88
|
+
}),
|
|
89
|
+
usage: {
|
|
90
|
+
inputTokens: response.usage?.input_tokens || 0,
|
|
91
|
+
outputTokens: response.usage?.output_tokens || 0,
|
|
92
|
+
},
|
|
93
|
+
model,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.OpenAIImageModel = OpenAIImageModel;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CustomOpenAI = void 0;
|
|
7
|
+
const openai_1 = __importDefault(require("openai"));
|
|
8
|
+
// Use a custom OpenAI client to handle API errors for better error messages
|
|
9
|
+
class CustomOpenAI extends openai_1.default {
|
|
10
|
+
makeStatusError(status, error, message, headers) {
|
|
11
|
+
if (!("error" in error) || typeof error.error !== "string") {
|
|
12
|
+
message = JSON.stringify(error);
|
|
13
|
+
}
|
|
14
|
+
return super.makeStatusError(status, error, message, headers);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.CustomOpenAI = CustomOpenAI;
|
package/lib/dts/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
|
|
2
2
|
import { type PromiseOrValue } from "@aigne/core/utils/type-utils.js";
|
|
3
|
-
import
|
|
3
|
+
import type { ClientOptions, OpenAI } from "openai";
|
|
4
4
|
import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
export interface OpenAIChatModelCapabilities {
|
|
@@ -131,12 +131,12 @@ export declare class OpenAIChatModel extends ChatModel {
|
|
|
131
131
|
protected supportsToolsEmptyParameters: boolean;
|
|
132
132
|
protected supportsToolStreaming: boolean;
|
|
133
133
|
protected supportsTemperature: boolean;
|
|
134
|
-
client():
|
|
135
|
-
|
|
134
|
+
get client(): OpenAI;
|
|
135
|
+
get credential(): {
|
|
136
136
|
url: string | undefined;
|
|
137
137
|
apiKey: string | undefined;
|
|
138
138
|
model: string;
|
|
139
|
-
}
|
|
139
|
+
};
|
|
140
140
|
get modelOptions(): ChatModelOptions | undefined;
|
|
141
141
|
/**
|
|
142
142
|
* Process the input and generate a response
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ImageModel, type ImageModelInput, type ImageModelOptions, type ImageModelOutput } from "@aigne/core";
|
|
2
|
+
import { type Camelize } from "@aigne/core/utils/camelize.js";
|
|
3
|
+
import type OpenAI from "openai";
|
|
4
|
+
import type { ClientOptions } from "openai";
|
|
5
|
+
export interface OpenAIImageModelInput extends ImageModelInput, Camelize<Omit<OpenAI.ImageGenerateParams, "prompt" | "model" | "n" | "response_format">> {
|
|
6
|
+
}
|
|
7
|
+
export interface OpenAIImageModelOutput extends ImageModelOutput {
|
|
8
|
+
}
|
|
9
|
+
export interface OpenAIImageModelOptions extends ImageModelOptions<OpenAIImageModelInput, OpenAIImageModelOutput> {
|
|
10
|
+
/**
|
|
11
|
+
* API key for OpenAI API
|
|
12
|
+
*
|
|
13
|
+
* If not provided, will look for OPENAI_API_KEY in environment variables
|
|
14
|
+
*/
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Base URL for OpenAI API
|
|
18
|
+
*
|
|
19
|
+
* Useful for proxies or alternate endpoints
|
|
20
|
+
*/
|
|
21
|
+
baseURL?: string;
|
|
22
|
+
/**
|
|
23
|
+
* OpenAI model to use
|
|
24
|
+
*
|
|
25
|
+
* Defaults to 'dall-e-2'
|
|
26
|
+
*/
|
|
27
|
+
model?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Additional model options to control behavior
|
|
30
|
+
*/
|
|
31
|
+
modelOptions?: Omit<Partial<OpenAIImageModelInput>, "model">;
|
|
32
|
+
/**
|
|
33
|
+
* Client options for OpenAI API
|
|
34
|
+
*/
|
|
35
|
+
clientOptions?: Partial<ClientOptions>;
|
|
36
|
+
}
|
|
37
|
+
export declare class OpenAIImageModel extends ImageModel<OpenAIImageModelInput, OpenAIImageModelOutput> {
|
|
38
|
+
options?: OpenAIImageModelOptions | undefined;
|
|
39
|
+
constructor(options?: OpenAIImageModelOptions | undefined);
|
|
40
|
+
protected _client?: OpenAI;
|
|
41
|
+
protected apiKeyEnvName: string;
|
|
42
|
+
get client(): OpenAI;
|
|
43
|
+
get credential(): {
|
|
44
|
+
url: string | undefined;
|
|
45
|
+
apiKey: string | undefined;
|
|
46
|
+
model: string;
|
|
47
|
+
};
|
|
48
|
+
get modelOptions(): Omit<Partial<OpenAIImageModelInput>, "model"> | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Process the input and generate a response
|
|
51
|
+
* @param input The input to process
|
|
52
|
+
* @returns The generated response
|
|
53
|
+
*/
|
|
54
|
+
process(input: ImageModelInput): Promise<ImageModelOutput>;
|
|
55
|
+
}
|
package/lib/esm/index.d.ts
CHANGED
package/lib/esm/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
|
|
2
2
|
import { type PromiseOrValue } from "@aigne/core/utils/type-utils.js";
|
|
3
|
-
import
|
|
3
|
+
import type { ClientOptions, OpenAI } from "openai";
|
|
4
4
|
import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
export interface OpenAIChatModelCapabilities {
|
|
@@ -131,12 +131,12 @@ export declare class OpenAIChatModel extends ChatModel {
|
|
|
131
131
|
protected supportsToolsEmptyParameters: boolean;
|
|
132
132
|
protected supportsToolStreaming: boolean;
|
|
133
133
|
protected supportsTemperature: boolean;
|
|
134
|
-
client():
|
|
135
|
-
|
|
134
|
+
get client(): OpenAI;
|
|
135
|
+
get credential(): {
|
|
136
136
|
url: string | undefined;
|
|
137
137
|
apiKey: string | undefined;
|
|
138
138
|
model: string;
|
|
139
|
-
}
|
|
139
|
+
};
|
|
140
140
|
get modelOptions(): ChatModelOptions | undefined;
|
|
141
141
|
/**
|
|
142
142
|
* Process the input and generate a response
|
|
@@ -5,9 +5,9 @@ import { getJsonOutputPrompt } from "@aigne/core/utils/prompts.js";
|
|
|
5
5
|
import { agentResponseStreamToObject } from "@aigne/core/utils/stream-utils.js";
|
|
6
6
|
import { checkArguments, isNonNullable, } from "@aigne/core/utils/type-utils.js";
|
|
7
7
|
import { Ajv } from "ajv";
|
|
8
|
-
import OpenAI from "openai";
|
|
9
8
|
import { v7 } from "uuid";
|
|
10
9
|
import { z } from "zod";
|
|
10
|
+
import { CustomOpenAI } from "./openai.js";
|
|
11
11
|
const CHAT_MODEL_OPENAI_DEFAULT_MODEL = "gpt-4o-mini";
|
|
12
12
|
const OPENAI_CHAT_MODEL_CAPABILITIES = {
|
|
13
13
|
"o4-mini": { supportsParallelToolCalls: false, supportsTemperature: false },
|
|
@@ -72,8 +72,8 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
72
72
|
supportsToolsEmptyParameters = true;
|
|
73
73
|
supportsToolStreaming = true;
|
|
74
74
|
supportsTemperature = true;
|
|
75
|
-
|
|
76
|
-
const { apiKey, url } =
|
|
75
|
+
get client() {
|
|
76
|
+
const { apiKey, url } = this.credential;
|
|
77
77
|
if (!apiKey)
|
|
78
78
|
throw new Error(`${this.name} requires an API key. Please provide it via \`options.apiKey\`, or set the \`${this.apiKeyEnvName}\` environment variable`);
|
|
79
79
|
this._client ??= new CustomOpenAI({
|
|
@@ -83,7 +83,7 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
83
83
|
});
|
|
84
84
|
return this._client;
|
|
85
85
|
}
|
|
86
|
-
|
|
86
|
+
get credential() {
|
|
87
87
|
return {
|
|
88
88
|
url: this.options?.baseURL || process.env.OPENAI_BASE_URL,
|
|
89
89
|
apiKey: this.options?.apiKey || process.env[this.apiKeyEnvName] || this.apiKeyDefault,
|
|
@@ -104,7 +104,7 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
104
104
|
ajv = new Ajv();
|
|
105
105
|
async _process(input) {
|
|
106
106
|
const messages = await this.getRunMessages(input);
|
|
107
|
-
const
|
|
107
|
+
const model = input.modelOptions?.model || this.credential.model;
|
|
108
108
|
const body = {
|
|
109
109
|
model,
|
|
110
110
|
temperature: this.supportsTemperature
|
|
@@ -125,8 +125,7 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
125
125
|
return await this.requestStructuredOutput(body, input.responseFormat);
|
|
126
126
|
}
|
|
127
127
|
const { jsonMode, responseFormat } = await this.getRunResponseFormat(input);
|
|
128
|
-
const
|
|
129
|
-
const stream = (await client.chat.completions.create({
|
|
128
|
+
const stream = (await this.client.chat.completions.create({
|
|
130
129
|
...body,
|
|
131
130
|
tools: toolsFromInputTools(input.tools, {
|
|
132
131
|
addTypeToEmptyParameters: !this.supportsToolsEmptyParameters,
|
|
@@ -205,8 +204,7 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
205
204
|
const { jsonMode, responseFormat: resolvedResponseFormat } = await this.getRunResponseFormat({
|
|
206
205
|
responseFormat,
|
|
207
206
|
});
|
|
208
|
-
const
|
|
209
|
-
const res = (await client.chat.completions.create({
|
|
207
|
+
const res = (await this.client.chat.completions.create({
|
|
210
208
|
...body,
|
|
211
209
|
response_format: resolvedResponseFormat,
|
|
212
210
|
}));
|
|
@@ -418,13 +416,4 @@ function handleCompleteToolCall(toolCalls, call) {
|
|
|
418
416
|
args: call.function?.arguments || "",
|
|
419
417
|
});
|
|
420
418
|
}
|
|
421
|
-
// Use a custom OpenAI client to handle API errors for better error messages
|
|
422
|
-
class CustomOpenAI extends OpenAI {
|
|
423
|
-
makeStatusError(status, error, message, headers) {
|
|
424
|
-
if (!("error" in error) || typeof error.error !== "string") {
|
|
425
|
-
message = JSON.stringify(error);
|
|
426
|
-
}
|
|
427
|
-
return super.makeStatusError(status, error, message, headers);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
419
|
// safeParseJSON is now imported from @aigne/core
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ImageModel, type ImageModelInput, type ImageModelOptions, type ImageModelOutput } from "@aigne/core";
|
|
2
|
+
import { type Camelize } from "@aigne/core/utils/camelize.js";
|
|
3
|
+
import type OpenAI from "openai";
|
|
4
|
+
import type { ClientOptions } from "openai";
|
|
5
|
+
export interface OpenAIImageModelInput extends ImageModelInput, Camelize<Omit<OpenAI.ImageGenerateParams, "prompt" | "model" | "n" | "response_format">> {
|
|
6
|
+
}
|
|
7
|
+
export interface OpenAIImageModelOutput extends ImageModelOutput {
|
|
8
|
+
}
|
|
9
|
+
export interface OpenAIImageModelOptions extends ImageModelOptions<OpenAIImageModelInput, OpenAIImageModelOutput> {
|
|
10
|
+
/**
|
|
11
|
+
* API key for OpenAI API
|
|
12
|
+
*
|
|
13
|
+
* If not provided, will look for OPENAI_API_KEY in environment variables
|
|
14
|
+
*/
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Base URL for OpenAI API
|
|
18
|
+
*
|
|
19
|
+
* Useful for proxies or alternate endpoints
|
|
20
|
+
*/
|
|
21
|
+
baseURL?: string;
|
|
22
|
+
/**
|
|
23
|
+
* OpenAI model to use
|
|
24
|
+
*
|
|
25
|
+
* Defaults to 'dall-e-2'
|
|
26
|
+
*/
|
|
27
|
+
model?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Additional model options to control behavior
|
|
30
|
+
*/
|
|
31
|
+
modelOptions?: Omit<Partial<OpenAIImageModelInput>, "model">;
|
|
32
|
+
/**
|
|
33
|
+
* Client options for OpenAI API
|
|
34
|
+
*/
|
|
35
|
+
clientOptions?: Partial<ClientOptions>;
|
|
36
|
+
}
|
|
37
|
+
export declare class OpenAIImageModel extends ImageModel<OpenAIImageModelInput, OpenAIImageModelOutput> {
|
|
38
|
+
options?: OpenAIImageModelOptions | undefined;
|
|
39
|
+
constructor(options?: OpenAIImageModelOptions | undefined);
|
|
40
|
+
protected _client?: OpenAI;
|
|
41
|
+
protected apiKeyEnvName: string;
|
|
42
|
+
get client(): OpenAI;
|
|
43
|
+
get credential(): {
|
|
44
|
+
url: string | undefined;
|
|
45
|
+
apiKey: string | undefined;
|
|
46
|
+
model: string;
|
|
47
|
+
};
|
|
48
|
+
get modelOptions(): Omit<Partial<OpenAIImageModelInput>, "model"> | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Process the input and generate a response
|
|
51
|
+
* @param input The input to process
|
|
52
|
+
* @returns The generated response
|
|
53
|
+
*/
|
|
54
|
+
process(input: ImageModelInput): Promise<ImageModelOutput>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ImageModel, imageModelInputSchema, } from "@aigne/core";
|
|
2
|
+
import { snakelize } from "@aigne/core/utils/camelize.js";
|
|
3
|
+
import { checkArguments, pick } from "@aigne/core/utils/type-utils.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { CustomOpenAI } from "./openai.js";
|
|
6
|
+
const DEFAULT_MODEL = "dall-e-2";
|
|
7
|
+
const openAIImageModelInputSchema = imageModelInputSchema.extend({});
|
|
8
|
+
const openAIImageModelOptionsSchema = z.object({
|
|
9
|
+
apiKey: z.string().optional(),
|
|
10
|
+
baseURL: z.string().optional(),
|
|
11
|
+
model: z.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
export class OpenAIImageModel extends ImageModel {
|
|
14
|
+
options;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
super({
|
|
17
|
+
...options,
|
|
18
|
+
inputSchema: openAIImageModelInputSchema,
|
|
19
|
+
description: options?.description ?? "Draw or edit image by OpenAI image models",
|
|
20
|
+
});
|
|
21
|
+
this.options = options;
|
|
22
|
+
if (options)
|
|
23
|
+
checkArguments(this.name, openAIImageModelOptionsSchema, options);
|
|
24
|
+
}
|
|
25
|
+
_client;
|
|
26
|
+
apiKeyEnvName = "OPENAI_API_KEY";
|
|
27
|
+
get client() {
|
|
28
|
+
if (this._client)
|
|
29
|
+
return this._client;
|
|
30
|
+
const { apiKey, url } = this.credential;
|
|
31
|
+
if (!apiKey)
|
|
32
|
+
throw new Error(`${this.name} requires an API key. Please provide it via \`options.apiKey\`, or set the \`${this.apiKeyEnvName}\` environment variable`);
|
|
33
|
+
this._client ??= new CustomOpenAI({
|
|
34
|
+
baseURL: url,
|
|
35
|
+
apiKey,
|
|
36
|
+
...this.options?.clientOptions,
|
|
37
|
+
});
|
|
38
|
+
return this._client;
|
|
39
|
+
}
|
|
40
|
+
get credential() {
|
|
41
|
+
return {
|
|
42
|
+
url: this.options?.baseURL || process.env.OPENAI_BASE_URL,
|
|
43
|
+
apiKey: this.options?.apiKey || process.env[this.apiKeyEnvName],
|
|
44
|
+
model: this.options?.model || DEFAULT_MODEL,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
get modelOptions() {
|
|
48
|
+
return this.options?.modelOptions;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Process the input and generate a response
|
|
52
|
+
* @param input The input to process
|
|
53
|
+
* @returns The generated response
|
|
54
|
+
*/
|
|
55
|
+
async process(input) {
|
|
56
|
+
const model = input.model || this.credential.model;
|
|
57
|
+
const inputKeys = [
|
|
58
|
+
"background",
|
|
59
|
+
"moderation",
|
|
60
|
+
"output_compression",
|
|
61
|
+
"output_format",
|
|
62
|
+
"prompt",
|
|
63
|
+
"quality",
|
|
64
|
+
"size",
|
|
65
|
+
"style",
|
|
66
|
+
"user",
|
|
67
|
+
];
|
|
68
|
+
let responseFormat;
|
|
69
|
+
if (model !== "gpt-image-1") {
|
|
70
|
+
responseFormat = input.responseFormat === "base64" ? "b64_json" : "url";
|
|
71
|
+
}
|
|
72
|
+
const body = {
|
|
73
|
+
...snakelize(pick({ ...this.modelOptions, ...input }, inputKeys)),
|
|
74
|
+
response_format: responseFormat,
|
|
75
|
+
model,
|
|
76
|
+
};
|
|
77
|
+
const response = await this.client.images.generate({ ...body });
|
|
78
|
+
return {
|
|
79
|
+
images: (response.data ?? []).map((image) => {
|
|
80
|
+
if (image.url)
|
|
81
|
+
return { url: image.url };
|
|
82
|
+
if (image.b64_json)
|
|
83
|
+
return { base64: image.b64_json };
|
|
84
|
+
throw new Error("Image response does not contain a valid URL or base64 data");
|
|
85
|
+
}),
|
|
86
|
+
usage: {
|
|
87
|
+
inputTokens: response.usage?.input_tokens || 0,
|
|
88
|
+
outputTokens: response.usage?.output_tokens || 0,
|
|
89
|
+
},
|
|
90
|
+
model,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
// Use a custom OpenAI client to handle API errors for better error messages
|
|
3
|
+
export class CustomOpenAI extends OpenAI {
|
|
4
|
+
makeStatusError(status, error, message, headers) {
|
|
5
|
+
if (!("error" in error) || typeof error.error !== "string") {
|
|
6
|
+
message = JSON.stringify(error);
|
|
7
|
+
}
|
|
8
|
+
return super.makeStatusError(status, error, message, headers);
|
|
9
|
+
}
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/openai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "AIGNE OpenAI SDK for integrating with OpenAI's GPT models and API services",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"openai": "^5.8.3",
|
|
40
40
|
"uuid": "^11.1.0",
|
|
41
41
|
"zod": "^3.25.67",
|
|
42
|
-
"@aigne/core": "^1.
|
|
42
|
+
"@aigne/core": "^1.53.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/bun": "^1.2.18",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"npm-run-all": "^4.1.5",
|
|
48
48
|
"rimraf": "^6.0.1",
|
|
49
49
|
"typescript": "^5.8.3",
|
|
50
|
-
"@aigne/test-utils": "^0.5.
|
|
50
|
+
"@aigne/test-utils": "^0.5.32"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"lint": "tsc --noEmit",
|