@aigne/doubao 1.0.38 → 1.0.40

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 CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.40](https://github.com/AIGNE-io/aigne-framework/compare/doubao-v1.0.39...doubao-v1.0.40) (2025-09-18)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * support the correct 3.0 model ([#514](https://github.com/AIGNE-io/aigne-framework/issues/514)) ([98e0d44](https://github.com/AIGNE-io/aigne-framework/commit/98e0d44ce2c4c7b043d5fc934c6e312ffa821521))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @aigne/openai bumped to 0.15.4
16
+ * devDependencies
17
+ * @aigne/core bumped to 1.60.3
18
+ * @aigne/test-utils bumped to 0.5.52
19
+
20
+ ## [1.0.39](https://github.com/AIGNE-io/aigne-framework/compare/doubao-v1.0.38...doubao-v1.0.39) (2025-09-16)
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * release new version ([#509](https://github.com/AIGNE-io/aigne-framework/issues/509)) ([822ccd2](https://github.com/AIGNE-io/aigne-framework/commit/822ccd2c374cdcef187066e102dc79230e2eebff))
26
+
3
27
  ## [1.0.38](https://github.com/AIGNE-io/aigne-framework/compare/doubao-v1.0.37...doubao-v1.0.38) (2025-09-11)
4
28
 
5
29
 
@@ -0,0 +1,34 @@
1
+ import { ImageModel, type ImageModelInput, type ImageModelOptions, type ImageModelOutput } from "@aigne/core";
2
+ export interface DoubaoImageModelInput extends ImageModelInput {
3
+ size?: string;
4
+ seed?: number;
5
+ sequentialImageGeneration?: boolean;
6
+ sequentialImageGenerationOptions?: {
7
+ maxImages: number;
8
+ };
9
+ stream?: boolean;
10
+ guidanceScale?: number;
11
+ watermark?: boolean;
12
+ }
13
+ export interface DoubaoImageModelOutput extends ImageModelOutput {
14
+ }
15
+ export interface DoubaoImageModelOptions extends ImageModelOptions<DoubaoImageModelInput, DoubaoImageModelOutput> {
16
+ apiKey?: string;
17
+ baseURL?: string;
18
+ model?: string;
19
+ modelOptions?: Omit<Partial<DoubaoImageModelInput>, "model">;
20
+ clientOptions?: Record<string, any>;
21
+ }
22
+ export declare class DoubaoImageModel extends ImageModel<DoubaoImageModelInput, DoubaoImageModelOutput> {
23
+ options?: DoubaoImageModelOptions | undefined;
24
+ constructor(options?: DoubaoImageModelOptions | undefined);
25
+ protected apiKeyEnvName: string;
26
+ get credential(): {
27
+ url: string;
28
+ apiKey: string | undefined;
29
+ model: string;
30
+ };
31
+ get modelOptions(): Omit<Partial<DoubaoImageModelInput>, "model"> | undefined;
32
+ private extractDataObjects;
33
+ process(input: DoubaoImageModelInput): Promise<ImageModelOutput>;
34
+ }
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DoubaoImageModel = 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 ufo_1 = require("ufo");
8
+ const zod_1 = require("zod");
9
+ const DOUBAO_DEFAULT_IMAGE_MODEL = "doubao-seedream-4-0-250828";
10
+ const DOUBAO_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3";
11
+ const doubaoImageModelInputSchema = core_1.imageModelInputSchema.extend({});
12
+ const doubaoImageModelOptionsSchema = zod_1.z.object({
13
+ apiKey: zod_1.z.string().optional(),
14
+ baseURL: zod_1.z.string().optional(),
15
+ model: zod_1.z.string().optional(),
16
+ modelOptions: zod_1.z.object({}).optional(),
17
+ clientOptions: zod_1.z.object({}).optional(),
18
+ });
19
+ class DoubaoImageModel extends core_1.ImageModel {
20
+ options;
21
+ constructor(options) {
22
+ super({
23
+ ...options,
24
+ inputSchema: doubaoImageModelInputSchema,
25
+ description: options?.description ?? "Draw or edit image by Doubao image models",
26
+ });
27
+ this.options = options;
28
+ if (options)
29
+ (0, type_utils_js_1.checkArguments)(this.name, doubaoImageModelOptionsSchema, options);
30
+ }
31
+ apiKeyEnvName = "DOUBAO_API_KEY";
32
+ get credential() {
33
+ return {
34
+ url: this.options?.baseURL || process.env.DOUBAO_BASE_URL || DOUBAO_BASE_URL,
35
+ apiKey: this.options?.apiKey || process.env[this.apiKeyEnvName],
36
+ model: this.options?.model || DOUBAO_DEFAULT_IMAGE_MODEL,
37
+ };
38
+ }
39
+ get modelOptions() {
40
+ return this.options?.modelOptions;
41
+ }
42
+ extractDataObjects(text) {
43
+ const dataObjects = [];
44
+ const lines = text.split("\n");
45
+ for (const line of lines) {
46
+ const trimmed = line.trim();
47
+ if (trimmed.startsWith("data:")) {
48
+ const jsonPart = trimmed.slice(5).trim();
49
+ if (jsonPart === "[DONE]")
50
+ continue;
51
+ try {
52
+ const obj = JSON.parse(jsonPart);
53
+ dataObjects.push(obj);
54
+ }
55
+ catch (e) {
56
+ console.warn("Failed to parse JSON data object:", jsonPart, e);
57
+ }
58
+ }
59
+ }
60
+ return dataObjects;
61
+ }
62
+ async process(input) {
63
+ const model = input.model || this.credential.model;
64
+ const { url, apiKey } = this.credential;
65
+ if (!apiKey) {
66
+ throw new Error(`${this.name} requires an API key. Please provide it via \`options.apiKey\`, or set the \`${this.apiKeyEnvName}\` environment variable`);
67
+ }
68
+ const map = {
69
+ "doubao-seedream-4": [
70
+ "model",
71
+ "prompt",
72
+ "image",
73
+ "size",
74
+ "sequentialImageGeneration",
75
+ "sequentialImageGenerationOptions",
76
+ "stream",
77
+ "responseFormat",
78
+ "watermark",
79
+ ],
80
+ "doubao-seedream-3-0-t2i": [
81
+ "model",
82
+ "prompt",
83
+ "size",
84
+ "seed",
85
+ "guidanceScale",
86
+ "responseFormat",
87
+ "watermark",
88
+ ],
89
+ "doubao-seededit-3-0-i2i": [
90
+ "model",
91
+ "prompt",
92
+ "image",
93
+ "size",
94
+ "seed",
95
+ "guidanceScale",
96
+ "responseFormat",
97
+ "watermark",
98
+ ],
99
+ };
100
+ const key = Object.keys(map).find((key) => model.includes(key));
101
+ if (!key) {
102
+ throw new Error(`${this.name} only support ${Object.keys(map).join(", ")}`);
103
+ }
104
+ if (!map[key]) {
105
+ throw new Error(`${this.name} only support ${Object.keys(map).join(", ")}`);
106
+ }
107
+ const mergeInput = { ...this.modelOptions, ...input };
108
+ const body = { ...(0, camelize_js_1.snakelize)((0, type_utils_js_1.pick)(mergeInput, map[key])), model };
109
+ body.response_format =
110
+ mergeInput.responseFormat === "base64" ? "b64_json" : mergeInput.responseFormat || "url";
111
+ const response = await fetch((0, ufo_1.joinURL)(url, `/images/generations`), {
112
+ method: "POST",
113
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
114
+ body: JSON.stringify(body),
115
+ });
116
+ if (!response.ok) {
117
+ const error = await response.text();
118
+ throw new Error(`Doubao API error: ${response.status} ${response.statusText} ${error}`);
119
+ }
120
+ if (body.stream) {
121
+ if (!response.body)
122
+ throw new Error("Streaming not supported in this environment");
123
+ const reader = response.body.getReader();
124
+ const decoder = new TextDecoder();
125
+ let done = false;
126
+ let buffer = "";
127
+ while (!done) {
128
+ const { value, done: doneReading } = await reader.read();
129
+ done = doneReading;
130
+ if (value) {
131
+ buffer += decoder.decode(value);
132
+ }
133
+ }
134
+ const dataObjects = this.extractDataObjects(buffer);
135
+ const error = dataObjects.find((i) => i.type === "image_generation.partial_failed");
136
+ if (error) {
137
+ throw new Error(`Doubao API error: ${error.error.message}`);
138
+ }
139
+ const completed = dataObjects.find((i) => i.type === "image_generation.completed");
140
+ return {
141
+ images: dataObjects
142
+ .filter((i) => i.type === "image_generation.partial_succeeded")
143
+ .map((i) => {
144
+ return {
145
+ url: i.url,
146
+ base64: i.b64_json,
147
+ };
148
+ }),
149
+ usage: { inputTokens: 0, outputTokens: completed?.usage.output_tokens || 0 },
150
+ model: model,
151
+ };
152
+ }
153
+ const data = await response.json();
154
+ if (data.error) {
155
+ throw new Error(`Doubao API error: ${data.error.message}`);
156
+ }
157
+ return {
158
+ images: data.data.map((item) => ({
159
+ url: item.url,
160
+ base64: item.b64_json,
161
+ })),
162
+ usage: {
163
+ inputTokens: 0,
164
+ outputTokens: data?.usage?.output_tokens || 0,
165
+ },
166
+ model: data.model,
167
+ };
168
+ }
169
+ }
170
+ exports.DoubaoImageModel = DoubaoImageModel;
@@ -1 +1,2 @@
1
1
  export * from "./doubao-chat-model.js";
2
+ export * from "./doubao-image-model.js";
package/lib/cjs/index.js CHANGED
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./doubao-chat-model.js"), exports);
18
+ __exportStar(require("./doubao-image-model.js"), exports);
@@ -0,0 +1,34 @@
1
+ import { ImageModel, type ImageModelInput, type ImageModelOptions, type ImageModelOutput } from "@aigne/core";
2
+ export interface DoubaoImageModelInput extends ImageModelInput {
3
+ size?: string;
4
+ seed?: number;
5
+ sequentialImageGeneration?: boolean;
6
+ sequentialImageGenerationOptions?: {
7
+ maxImages: number;
8
+ };
9
+ stream?: boolean;
10
+ guidanceScale?: number;
11
+ watermark?: boolean;
12
+ }
13
+ export interface DoubaoImageModelOutput extends ImageModelOutput {
14
+ }
15
+ export interface DoubaoImageModelOptions extends ImageModelOptions<DoubaoImageModelInput, DoubaoImageModelOutput> {
16
+ apiKey?: string;
17
+ baseURL?: string;
18
+ model?: string;
19
+ modelOptions?: Omit<Partial<DoubaoImageModelInput>, "model">;
20
+ clientOptions?: Record<string, any>;
21
+ }
22
+ export declare class DoubaoImageModel extends ImageModel<DoubaoImageModelInput, DoubaoImageModelOutput> {
23
+ options?: DoubaoImageModelOptions | undefined;
24
+ constructor(options?: DoubaoImageModelOptions | undefined);
25
+ protected apiKeyEnvName: string;
26
+ get credential(): {
27
+ url: string;
28
+ apiKey: string | undefined;
29
+ model: string;
30
+ };
31
+ get modelOptions(): Omit<Partial<DoubaoImageModelInput>, "model"> | undefined;
32
+ private extractDataObjects;
33
+ process(input: DoubaoImageModelInput): Promise<ImageModelOutput>;
34
+ }
@@ -1 +1,2 @@
1
1
  export * from "./doubao-chat-model.js";
2
+ export * from "./doubao-image-model.js";
@@ -0,0 +1,34 @@
1
+ import { ImageModel, type ImageModelInput, type ImageModelOptions, type ImageModelOutput } from "@aigne/core";
2
+ export interface DoubaoImageModelInput extends ImageModelInput {
3
+ size?: string;
4
+ seed?: number;
5
+ sequentialImageGeneration?: boolean;
6
+ sequentialImageGenerationOptions?: {
7
+ maxImages: number;
8
+ };
9
+ stream?: boolean;
10
+ guidanceScale?: number;
11
+ watermark?: boolean;
12
+ }
13
+ export interface DoubaoImageModelOutput extends ImageModelOutput {
14
+ }
15
+ export interface DoubaoImageModelOptions extends ImageModelOptions<DoubaoImageModelInput, DoubaoImageModelOutput> {
16
+ apiKey?: string;
17
+ baseURL?: string;
18
+ model?: string;
19
+ modelOptions?: Omit<Partial<DoubaoImageModelInput>, "model">;
20
+ clientOptions?: Record<string, any>;
21
+ }
22
+ export declare class DoubaoImageModel extends ImageModel<DoubaoImageModelInput, DoubaoImageModelOutput> {
23
+ options?: DoubaoImageModelOptions | undefined;
24
+ constructor(options?: DoubaoImageModelOptions | undefined);
25
+ protected apiKeyEnvName: string;
26
+ get credential(): {
27
+ url: string;
28
+ apiKey: string | undefined;
29
+ model: string;
30
+ };
31
+ get modelOptions(): Omit<Partial<DoubaoImageModelInput>, "model"> | undefined;
32
+ private extractDataObjects;
33
+ process(input: DoubaoImageModelInput): Promise<ImageModelOutput>;
34
+ }
@@ -0,0 +1,166 @@
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 { joinURL } from "ufo";
5
+ import { z } from "zod";
6
+ const DOUBAO_DEFAULT_IMAGE_MODEL = "doubao-seedream-4-0-250828";
7
+ const DOUBAO_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3";
8
+ const doubaoImageModelInputSchema = imageModelInputSchema.extend({});
9
+ const doubaoImageModelOptionsSchema = z.object({
10
+ apiKey: z.string().optional(),
11
+ baseURL: z.string().optional(),
12
+ model: z.string().optional(),
13
+ modelOptions: z.object({}).optional(),
14
+ clientOptions: z.object({}).optional(),
15
+ });
16
+ export class DoubaoImageModel extends ImageModel {
17
+ options;
18
+ constructor(options) {
19
+ super({
20
+ ...options,
21
+ inputSchema: doubaoImageModelInputSchema,
22
+ description: options?.description ?? "Draw or edit image by Doubao image models",
23
+ });
24
+ this.options = options;
25
+ if (options)
26
+ checkArguments(this.name, doubaoImageModelOptionsSchema, options);
27
+ }
28
+ apiKeyEnvName = "DOUBAO_API_KEY";
29
+ get credential() {
30
+ return {
31
+ url: this.options?.baseURL || process.env.DOUBAO_BASE_URL || DOUBAO_BASE_URL,
32
+ apiKey: this.options?.apiKey || process.env[this.apiKeyEnvName],
33
+ model: this.options?.model || DOUBAO_DEFAULT_IMAGE_MODEL,
34
+ };
35
+ }
36
+ get modelOptions() {
37
+ return this.options?.modelOptions;
38
+ }
39
+ extractDataObjects(text) {
40
+ const dataObjects = [];
41
+ const lines = text.split("\n");
42
+ for (const line of lines) {
43
+ const trimmed = line.trim();
44
+ if (trimmed.startsWith("data:")) {
45
+ const jsonPart = trimmed.slice(5).trim();
46
+ if (jsonPart === "[DONE]")
47
+ continue;
48
+ try {
49
+ const obj = JSON.parse(jsonPart);
50
+ dataObjects.push(obj);
51
+ }
52
+ catch (e) {
53
+ console.warn("Failed to parse JSON data object:", jsonPart, e);
54
+ }
55
+ }
56
+ }
57
+ return dataObjects;
58
+ }
59
+ async process(input) {
60
+ const model = input.model || this.credential.model;
61
+ const { url, apiKey } = this.credential;
62
+ if (!apiKey) {
63
+ throw new Error(`${this.name} requires an API key. Please provide it via \`options.apiKey\`, or set the \`${this.apiKeyEnvName}\` environment variable`);
64
+ }
65
+ const map = {
66
+ "doubao-seedream-4": [
67
+ "model",
68
+ "prompt",
69
+ "image",
70
+ "size",
71
+ "sequentialImageGeneration",
72
+ "sequentialImageGenerationOptions",
73
+ "stream",
74
+ "responseFormat",
75
+ "watermark",
76
+ ],
77
+ "doubao-seedream-3-0-t2i": [
78
+ "model",
79
+ "prompt",
80
+ "size",
81
+ "seed",
82
+ "guidanceScale",
83
+ "responseFormat",
84
+ "watermark",
85
+ ],
86
+ "doubao-seededit-3-0-i2i": [
87
+ "model",
88
+ "prompt",
89
+ "image",
90
+ "size",
91
+ "seed",
92
+ "guidanceScale",
93
+ "responseFormat",
94
+ "watermark",
95
+ ],
96
+ };
97
+ const key = Object.keys(map).find((key) => model.includes(key));
98
+ if (!key) {
99
+ throw new Error(`${this.name} only support ${Object.keys(map).join(", ")}`);
100
+ }
101
+ if (!map[key]) {
102
+ throw new Error(`${this.name} only support ${Object.keys(map).join(", ")}`);
103
+ }
104
+ const mergeInput = { ...this.modelOptions, ...input };
105
+ const body = { ...snakelize(pick(mergeInput, map[key])), model };
106
+ body.response_format =
107
+ mergeInput.responseFormat === "base64" ? "b64_json" : mergeInput.responseFormat || "url";
108
+ const response = await fetch(joinURL(url, `/images/generations`), {
109
+ method: "POST",
110
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
111
+ body: JSON.stringify(body),
112
+ });
113
+ if (!response.ok) {
114
+ const error = await response.text();
115
+ throw new Error(`Doubao API error: ${response.status} ${response.statusText} ${error}`);
116
+ }
117
+ if (body.stream) {
118
+ if (!response.body)
119
+ throw new Error("Streaming not supported in this environment");
120
+ const reader = response.body.getReader();
121
+ const decoder = new TextDecoder();
122
+ let done = false;
123
+ let buffer = "";
124
+ while (!done) {
125
+ const { value, done: doneReading } = await reader.read();
126
+ done = doneReading;
127
+ if (value) {
128
+ buffer += decoder.decode(value);
129
+ }
130
+ }
131
+ const dataObjects = this.extractDataObjects(buffer);
132
+ const error = dataObjects.find((i) => i.type === "image_generation.partial_failed");
133
+ if (error) {
134
+ throw new Error(`Doubao API error: ${error.error.message}`);
135
+ }
136
+ const completed = dataObjects.find((i) => i.type === "image_generation.completed");
137
+ return {
138
+ images: dataObjects
139
+ .filter((i) => i.type === "image_generation.partial_succeeded")
140
+ .map((i) => {
141
+ return {
142
+ url: i.url,
143
+ base64: i.b64_json,
144
+ };
145
+ }),
146
+ usage: { inputTokens: 0, outputTokens: completed?.usage.output_tokens || 0 },
147
+ model: model,
148
+ };
149
+ }
150
+ const data = await response.json();
151
+ if (data.error) {
152
+ throw new Error(`Doubao API error: ${data.error.message}`);
153
+ }
154
+ return {
155
+ images: data.data.map((item) => ({
156
+ url: item.url,
157
+ base64: item.b64_json,
158
+ })),
159
+ usage: {
160
+ inputTokens: 0,
161
+ outputTokens: data?.usage?.output_tokens || 0,
162
+ },
163
+ model: data.model,
164
+ };
165
+ }
166
+ }
@@ -1 +1,2 @@
1
1
  export * from "./doubao-chat-model.js";
2
+ export * from "./doubao-image-model.js";
package/lib/esm/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export * from "./doubao-chat-model.js";
2
+ export * from "./doubao-image-model.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/doubao",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
4
4
  "description": "AIGNE doubao SDK for integrating with doubao AI models",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -35,16 +35,20 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@aigne/openai": "^0.15.3"
38
+ "ufo": "^1.6.1",
39
+ "zod": "^3.25.67",
40
+ "@aigne/openai": "^0.15.4"
39
41
  },
40
42
  "devDependencies": {
41
- "@types/bun": "^1.2.18",
42
- "@types/node": "^24.0.12",
43
+ "@types/bun": "^1.2.22",
44
+ "@types/node": "^24.5.1",
45
+ "detect-port": "^2.1.0",
46
+ "hono": "^4.9.7",
43
47
  "npm-run-all": "^4.1.5",
44
48
  "rimraf": "^6.0.1",
45
- "typescript": "^5.8.3",
46
- "@aigne/core": "^1.60.2",
47
- "@aigne/test-utils": "^0.5.51"
49
+ "typescript": "^5.9.2",
50
+ "@aigne/core": "^1.60.3",
51
+ "@aigne/test-utils": "^0.5.52"
48
52
  },
49
53
  "scripts": {
50
54
  "lint": "tsc --noEmit",