@scotthuang/openclaw-bytedance 0.1.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.
@@ -0,0 +1,288 @@
1
+ import { DEFAULT_VISION_MODEL, buildArkEndpoint, resolveArkApiKey, resolveArkBaseUrl, } from "./credentials.js";
2
+ import { formatErr, log } from "./logger.js";
3
+ /**
4
+ * ByteDance (Volcengine Ark) media-understanding provider.
5
+ *
6
+ * Default model: doubao-seed-2.0-pro
7
+ *
8
+ * Strategy:
9
+ * - Volcengine Ark exposes the OpenAI-compatible Chat Completions API at
10
+ * `/api/v3/chat/completions`. We send a multimodal user message with
11
+ * `image_url` content blocks and ask the model to describe the image(s).
12
+ * - Image bytes are inlined as base64 data URLs since the framework hands
13
+ * us a raw `Buffer` per image. (Ark also accepts http/https URLs but
14
+ * here we always have local bytes.)
15
+ *
16
+ * Credentials/baseUrl are resolved from the ByteDance plugin config or env
17
+ * vars (ARK_API_KEY / ARK_BASE_URL); falling back to the public CN endpoint.
18
+ */
19
+ const SUPPORTED_IMAGE_MIME_TYPES = new Set([
20
+ "image/png",
21
+ "image/jpeg",
22
+ "image/jpg",
23
+ "image/webp",
24
+ "image/gif",
25
+ ]);
26
+ const DEFAULT_PROMPT = "Describe the image in detail.";
27
+ const DEFAULT_MAX_TOKENS = 1024;
28
+ function normalizeMime(mime) {
29
+ if (typeof mime === "string") {
30
+ const lower = mime.trim().toLowerCase();
31
+ if (SUPPORTED_IMAGE_MIME_TYPES.has(lower)) {
32
+ // Ark expects standard image/jpeg, image/png, etc.
33
+ return lower === "image/jpg" ? "image/jpeg" : lower;
34
+ }
35
+ }
36
+ // Fall back to jpeg for unknown/unspecified types.
37
+ return "image/jpeg";
38
+ }
39
+ function bufferToDataUrl(buffer, mime) {
40
+ const normalizedMime = normalizeMime(mime);
41
+ const base64 = buffer.toString("base64");
42
+ return `data:${normalizedMime};base64,${base64}`;
43
+ }
44
+ function resolveCredentialsFromConfig(cfg) {
45
+ if (!cfg || typeof cfg !== "object")
46
+ return {};
47
+ const config = cfg;
48
+ const plugins = config.plugins;
49
+ const entries = plugins?.entries;
50
+ const bytedance = entries?.bytedance;
51
+ const pluginConfig = bytedance?.config;
52
+ const vision = pluginConfig?.vision;
53
+ if (!vision)
54
+ return {};
55
+ return {
56
+ apiKey: typeof vision.apiKey === "string" ? vision.apiKey : undefined,
57
+ baseUrl: typeof vision.baseUrl === "string" ? vision.baseUrl : undefined,
58
+ model: typeof vision.model === "string" ? vision.model : undefined,
59
+ };
60
+ }
61
+ function resolveModel(requested, fromConfig) {
62
+ if (typeof requested === "string" && requested.trim().length > 0 && requested !== "bytedance") {
63
+ return requested.trim();
64
+ }
65
+ if (typeof fromConfig === "string" && fromConfig.trim().length > 0) {
66
+ return fromConfig.trim();
67
+ }
68
+ return DEFAULT_VISION_MODEL;
69
+ }
70
+ function extractTextFromChoice(resp) {
71
+ const choice = resp.choices?.[0];
72
+ const content = choice?.message?.content;
73
+ if (typeof content === "string") {
74
+ return content;
75
+ }
76
+ if (Array.isArray(content)) {
77
+ const parts = [];
78
+ for (const c of content) {
79
+ if (c && c.type === "text" && typeof c.text === "string") {
80
+ parts.push(c.text);
81
+ }
82
+ }
83
+ return parts.join("\n").trim();
84
+ }
85
+ return "";
86
+ }
87
+ function buildErrorMessage(prefix, status, body) {
88
+ const trimmed = body.length > 500 ? `${body.slice(0, 500)}…` : body;
89
+ return `${prefix} (HTTP ${status}): ${trimmed || "<empty body>"}`;
90
+ }
91
+ async function callArkChatCompletions(params) {
92
+ const url = buildArkEndpoint(params.baseUrl, "/chat/completions");
93
+ const tag = params.logTag ? ` ${params.logTag}` : "";
94
+ const imageBlocks = params.body.messages
95
+ ?.flatMap((m) => (Array.isArray(m.content) ? m.content : []))
96
+ .filter((c) => c.type === "image_url").length ?? 0;
97
+ log.debug(`vision:${tag} POST ${url} model=${params.body.model} max_tokens=${params.body.max_tokens ?? "-"} ` +
98
+ `images=${imageBlocks} timeoutMs=${params.timeoutMs}`);
99
+ const controller = new AbortController();
100
+ const timer = setTimeout(() => controller.abort(), Math.max(1000, params.timeoutMs));
101
+ const startedAt = Date.now();
102
+ let res;
103
+ try {
104
+ res = await fetch(url, {
105
+ method: "POST",
106
+ headers: {
107
+ Authorization: `Bearer ${params.apiKey}`,
108
+ "Content-Type": "application/json",
109
+ Accept: "application/json",
110
+ },
111
+ body: JSON.stringify(params.body),
112
+ signal: controller.signal,
113
+ });
114
+ }
115
+ catch (err) {
116
+ if (err.name === "AbortError") {
117
+ log.error(`vision:${tag} request timed out after ${params.timeoutMs}ms (model=${params.body.model})`);
118
+ throw new Error(`Volcengine Ark vision request timed out after ${params.timeoutMs}ms`);
119
+ }
120
+ log.error(`vision:${tag} fetch threw before response: ${formatErr(err)}`);
121
+ throw err;
122
+ }
123
+ finally {
124
+ clearTimeout(timer);
125
+ }
126
+ const elapsed = Date.now() - startedAt;
127
+ if (!res.ok) {
128
+ const text = await res.text().catch(() => "");
129
+ log.warn(`vision:${tag} HTTP ${res.status} after ${elapsed}ms ` +
130
+ `(model=${params.body.model}): ${truncate(text, 300)}`);
131
+ throw new Error(buildErrorMessage("Volcengine Ark vision error", res.status, text));
132
+ }
133
+ let data;
134
+ try {
135
+ data = (await res.json());
136
+ }
137
+ catch (err) {
138
+ log.error(`vision:${tag} malformed JSON response after ${elapsed}ms: ${formatErr(err)}`);
139
+ throw err;
140
+ }
141
+ if (data.error?.message) {
142
+ log.warn(`vision:${tag} provider error envelope (code=${data.error.code ?? "?"}, ` +
143
+ `type=${data.error.type ?? "?"}): ${truncate(data.error.message, 200)}`);
144
+ throw new Error(`Volcengine Ark vision error: ${data.error.message}`);
145
+ }
146
+ log.debug(`vision:${tag} HTTP 200 in ${elapsed}ms model=${data.model ?? params.body.model} ` +
147
+ `finishReason=${data.choices?.[0]?.finish_reason ?? "-"} id=${data.id ?? "-"}`);
148
+ return data;
149
+ }
150
+ function truncate(text, max) {
151
+ if (typeof text !== "string")
152
+ return "";
153
+ return text.length > max ? `${text.slice(0, max)}…` : text;
154
+ }
155
+ async function describeImage(req) {
156
+ const invokeStartedAt = Date.now();
157
+ const { apiKey: cfgKey, baseUrl: cfgBaseUrl, model: cfgModel } = resolveCredentialsFromConfig(req.cfg);
158
+ const apiKey = resolveArkApiKey(cfgKey);
159
+ if (!apiKey) {
160
+ log.warn("vision: describeImage missing ARK_API_KEY (and no plugins.entries.bytedance.config.vision.apiKey)");
161
+ throw new Error("Volcengine Ark vision: missing ARK_API_KEY. Set the environment variable or configure plugins.entries.bytedance.config.vision.apiKey.");
162
+ }
163
+ const baseUrl = resolveArkBaseUrl(cfgBaseUrl);
164
+ const model = resolveModel(req.model, cfgModel);
165
+ const dataUrl = bufferToDataUrl(req.buffer, req.mime);
166
+ const prompt = (req.prompt && req.prompt.trim().length > 0 ? req.prompt : DEFAULT_PROMPT).trim();
167
+ log.debug(`vision: describeImage model=${model} fileName=${req.fileName} ` +
168
+ `mime=${req.mime ?? "-"} bytes=${req.buffer.length} ` +
169
+ `prompt="${truncate(prompt, 80)}" timeoutMs=${req.timeoutMs}`);
170
+ const body = {
171
+ model,
172
+ messages: [
173
+ {
174
+ role: "user",
175
+ content: [
176
+ { type: "text", text: prompt },
177
+ { type: "image_url", image_url: { url: dataUrl } },
178
+ ],
179
+ },
180
+ ],
181
+ max_tokens: req.maxTokens && req.maxTokens > 0 ? req.maxTokens : DEFAULT_MAX_TOKENS,
182
+ };
183
+ let data;
184
+ try {
185
+ data = await callArkChatCompletions({
186
+ apiKey,
187
+ baseUrl,
188
+ body,
189
+ timeoutMs: req.timeoutMs,
190
+ logTag: `single fileName=${req.fileName}`,
191
+ });
192
+ }
193
+ catch (err) {
194
+ log.error(`vision: describeImage failed after ${Date.now() - invokeStartedAt}ms ` +
195
+ `(model=${model}, fileName=${req.fileName}): ${formatErr(err)}`);
196
+ throw err;
197
+ }
198
+ const text = extractTextFromChoice(data);
199
+ log.info(`vision: describeImage ok model=${data.model ?? model} fileName=${req.fileName} ` +
200
+ `bytes=${req.buffer.length} totalMs=${Date.now() - invokeStartedAt} textLen=${text.length}`);
201
+ return {
202
+ text,
203
+ model: data.model ?? model,
204
+ };
205
+ }
206
+ async function describeSingleImageFromBatch(apiKey, baseUrl, model, prompt, image, index, total, maxTokens, timeoutMs) {
207
+ const dataUrl = bufferToDataUrl(image.buffer, image.mime);
208
+ const indexedPrompt = total > 1 ? `${prompt}\n(Describe image ${index + 1} of ${total} independently.)` : prompt;
209
+ const body = {
210
+ model,
211
+ messages: [
212
+ {
213
+ role: "user",
214
+ content: [
215
+ { type: "text", text: indexedPrompt },
216
+ { type: "image_url", image_url: { url: dataUrl } },
217
+ ],
218
+ },
219
+ ],
220
+ max_tokens: maxTokens,
221
+ };
222
+ const data = await callArkChatCompletions({
223
+ apiKey,
224
+ baseUrl,
225
+ body,
226
+ timeoutMs,
227
+ logTag: `batch[${index + 1}/${total}] fileName=${image.fileName}`,
228
+ });
229
+ return extractTextFromChoice(data);
230
+ }
231
+ async function describeImages(req) {
232
+ const invokeStartedAt = Date.now();
233
+ const { apiKey: cfgKey, baseUrl: cfgBaseUrl, model: cfgModel } = resolveCredentialsFromConfig(req.cfg);
234
+ const apiKey = resolveArkApiKey(cfgKey);
235
+ if (!apiKey) {
236
+ log.warn("vision: describeImages missing ARK_API_KEY (and no plugins.entries.bytedance.config.vision.apiKey)");
237
+ throw new Error("Volcengine Ark vision: missing ARK_API_KEY. Set the environment variable or configure plugins.entries.bytedance.config.vision.apiKey.");
238
+ }
239
+ const baseUrl = resolveArkBaseUrl(cfgBaseUrl);
240
+ const model = resolveModel(req.model, cfgModel);
241
+ if (req.images.length === 0) {
242
+ log.debug("vision: describeImages called with empty images array; returning early");
243
+ return { text: "", model };
244
+ }
245
+ const prompt = (req.prompt && req.prompt.trim().length > 0 ? req.prompt : DEFAULT_PROMPT).trim();
246
+ const maxTokens = req.maxTokens && req.maxTokens > 0 ? req.maxTokens : DEFAULT_MAX_TOKENS;
247
+ const totalBytes = req.images.reduce((acc, img) => acc + (img?.buffer?.length ?? 0), 0);
248
+ log.debug(`vision: describeImages model=${model} count=${req.images.length} totalBytes=${totalBytes} ` +
249
+ `prompt="${truncate(prompt, 80)}" timeoutMs=${req.timeoutMs} max_tokens=${maxTokens}`);
250
+ // Run sequentially to keep behavior predictable and to match how MiniMax's
251
+ // VLM provider handles batches. Could be parallelized later if needed.
252
+ const parts = [];
253
+ for (let i = 0; i < req.images.length; i += 1) {
254
+ const image = req.images[i];
255
+ if (!image)
256
+ continue;
257
+ const itemStartedAt = Date.now();
258
+ let text;
259
+ try {
260
+ text = await describeSingleImageFromBatch(apiKey, baseUrl, model, prompt, image, i, req.images.length, maxTokens, req.timeoutMs);
261
+ }
262
+ catch (err) {
263
+ log.error(`vision: describeImages item ${i + 1}/${req.images.length} failed after ` +
264
+ `${Date.now() - itemStartedAt}ms (fileName=${image.fileName}): ${formatErr(err)}`);
265
+ throw err;
266
+ }
267
+ log.debug(`vision: describeImages item ${i + 1}/${req.images.length} ok ` +
268
+ `fileName=${image.fileName} bytes=${image.buffer.length} ` +
269
+ `tookMs=${Date.now() - itemStartedAt} textLen=${text.length}`);
270
+ parts.push(req.images.length > 1 ? `Image ${i + 1}: ${text}` : text);
271
+ }
272
+ const joined = parts.join("\n\n").trim();
273
+ log.info(`vision: describeImages ok model=${model} count=${req.images.length} ` +
274
+ `totalBytes=${totalBytes} totalMs=${Date.now() - invokeStartedAt} textLen=${joined.length}`);
275
+ return {
276
+ text: joined,
277
+ model,
278
+ };
279
+ }
280
+ export const bytedanceMediaUnderstandingProvider = {
281
+ id: "bytedance",
282
+ capabilities: ["image"],
283
+ defaultModels: { image: DEFAULT_VISION_MODEL },
284
+ autoPriority: { image: 45 },
285
+ describeImage,
286
+ describeImages,
287
+ };
288
+ //# sourceMappingURL=media-understanding-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-understanding-provider.js","sourceRoot":"","sources":["../../src/media-understanding-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ {
2
+ "id": "bytedance",
3
+ "activation": {
4
+ "onStartup": false
5
+ },
6
+ "enabledByDefault": true,
7
+ "providers": ["bytedance"],
8
+ "autoEnableWhenConfiguredProviders": ["bytedance"],
9
+ "providerAuthEnvVars": {
10
+ "bytedance": ["ARK_API_KEY", "ARK_SEARCH_API_KEY"]
11
+ },
12
+ "contracts": {
13
+ "mediaUnderstandingProviders": ["bytedance"],
14
+ "webSearchProviders": ["bytedance"]
15
+ },
16
+ "mediaUnderstandingProviderMetadata": {
17
+ "bytedance": {
18
+ "capabilities": ["image"],
19
+ "defaultModels": {
20
+ "image": "doubao-seed-2.0-pro"
21
+ },
22
+ "autoPriority": {
23
+ "image": 45
24
+ }
25
+ }
26
+ },
27
+ "uiHints": {
28
+ "webSearch.apiKey": {
29
+ "label": "Ark Harness search API key",
30
+ "help": "Volcengine Ark Harness search API key (from Agent Plan → 配置 Harness → 联网 API Key). Falls back to ARK_SEARCH_API_KEY / ASK_ECHO_SEARCH_INFINITY_API_KEY env vars.",
31
+ "sensitive": true,
32
+ "placeholder": "<harness-search-key>"
33
+ },
34
+ "webSearch.baseUrl": {
35
+ "label": "Search Base URL",
36
+ "help": "Optional. Defaults to https://open.feedcoopapi.com (set ARK_SEARCH_BASE_URL to override)."
37
+ },
38
+ "webSearch.searchType": {
39
+ "label": "Default search type",
40
+ "help": "Optional. 'web' (default) or 'image'."
41
+ },
42
+ "vision.apiKey": {
43
+ "label": "Volcengine Ark API key",
44
+ "help": "Volcengine Ark API key for chat/vision (Agent Plan). Falls back to ARK_API_KEY env var.",
45
+ "sensitive": true,
46
+ "placeholder": "ark-..."
47
+ },
48
+ "vision.baseUrl": {
49
+ "label": "Ark Base URL (vision)",
50
+ "help": "Optional. Defaults to https://ark.cn-beijing.volces.com/api/plan/v3 (set ARK_BASE_URL to override)."
51
+ },
52
+ "vision.model": {
53
+ "label": "Vision Model",
54
+ "help": "Optional. Model used for image understanding. Defaults to doubao-seed-2.0-pro."
55
+ }
56
+ },
57
+ "configSchema": {
58
+ "type": "object",
59
+ "additionalProperties": false,
60
+ "properties": {
61
+ "webSearch": {
62
+ "type": "object",
63
+ "additionalProperties": false,
64
+ "properties": {
65
+ "apiKey": { "type": ["string", "object"] },
66
+ "baseUrl": { "type": "string" },
67
+ "searchType": { "type": "string", "enum": ["web", "image"] },
68
+ "timeRange": { "type": "string" }
69
+ }
70
+ },
71
+ "vision": {
72
+ "type": "object",
73
+ "additionalProperties": false,
74
+ "properties": {
75
+ "apiKey": { "type": ["string", "object"] },
76
+ "baseUrl": { "type": "string" },
77
+ "model": { "type": "string" }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@scotthuang/openclaw-bytedance",
3
+ "version": "0.1.0",
4
+ "description": "OpenClaw plugin for ByteDance Volcengine Ark: web search and image understanding (doubao-seed-2.0-pro)",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/scotthuang/openclaw-bytedance"
11
+ },
12
+ "keywords": [
13
+ "openclaw",
14
+ "openclaw-plugin",
15
+ "bytedance",
16
+ "volcengine",
17
+ "ark",
18
+ "doubao",
19
+ "web-search",
20
+ "image-understanding",
21
+ "vision"
22
+ ],
23
+ "main": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "files": [
26
+ "dist",
27
+ "!dist/**/*.test.js",
28
+ "!dist/**/*.test.d.ts",
29
+ "!dist/**/*.test.js.map",
30
+ "openclaw.plugin.json",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "scripts": {
35
+ "setup:openclaw": "npm install --save-dev --no-save file:${OPENCLAW_REPO:-../openclaw}",
36
+ "build": "tsc -p tsconfig.build.json",
37
+ "prepublishOnly": "npm run build",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest",
40
+ "compare": "tsx scripts/compare-with-minimax.ts",
41
+ "compare:search": "tsx scripts/compare-with-minimax.ts --only=search",
42
+ "compare:vision": "tsx scripts/compare-with-minimax.ts --only=vision",
43
+ "typecheck": "tsc --noEmit"
44
+ },
45
+ "peerDependencies": {
46
+ "openclaw": "*"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "openclaw": {
50
+ "optional": true
51
+ }
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^22.0.0",
55
+ "tsx": "^4.19.0",
56
+ "typescript": "^5.4.0",
57
+ "vitest": "^2.1.0"
58
+ },
59
+ "openclaw": {
60
+ "extensions": [
61
+ "./dist/index.js"
62
+ ]
63
+ }
64
+ }