ai-world-sdk 1.0.12 → 1.0.15

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/README.md CHANGED
@@ -10,6 +10,7 @@ TypeScript SDK for AI World Platform - 一个功能完整的 AI 应用开发 SDK
10
10
  - 🤖 **聊天模型**: 兼容 LangChain.js 接口(OpenAI、Gemini、Claude、Doubao)
11
11
  - 🎨 **图像生成**: 支持豆包 Seedream 和 Google Gemini
12
12
  - 🎬 **视频生成**: 支持豆包 Seedance
13
+ - 📥 **下载代理**: 支持流式下载和普通下载任意 URL 的二进制文件
13
14
  - ⚙️ **全局配置**: 自动从浏览器环境获取配置,简化初始化
14
15
  - 🐛 **调试模式**: 支持详细的请求/响应日志
15
16
 
@@ -131,6 +132,38 @@ if (result.status === 'succeeded') {
131
132
  }
132
133
  ```
133
134
 
135
+ ### 5. 下载代理
136
+
137
+ ```typescript
138
+ import { DownloadClient } from 'ai-world-sdk';
139
+
140
+ const downloadClient = new DownloadClient({});
141
+
142
+ // 普通下载(返回 Blob)
143
+ const blob = await downloadClient.download({
144
+ url: 'https://example.com/file.zip',
145
+ filename: 'myfile.zip', // 可选
146
+ });
147
+ console.log('文件大小:', blob.size, '字节');
148
+
149
+ // 下载并保存到本地
150
+ await downloadClient.downloadAsFile({
151
+ url: 'https://example.com/file.zip',
152
+ filename: 'myfile.zip',
153
+ });
154
+
155
+ // 流式下载(使用 for await 迭代)
156
+ let totalSize = 0;
157
+ for await (const chunk of downloadClient.streamDownload({
158
+ url: 'https://example.com/large-file.zip',
159
+ filename: 'large-file.zip',
160
+ })) {
161
+ totalSize += chunk.length;
162
+ console.log(`已下载: ${totalSize} 字节`);
163
+ }
164
+ console.log('下载完成,总大小:', totalSize, '字节');
165
+ ```
166
+
134
167
  ## API 参考
135
168
 
136
169
  ### 聊天模型
@@ -339,6 +372,158 @@ const result = await client.poll(task.id, {
339
372
  });
340
373
  ```
341
374
 
375
+ ### 下载代理
376
+
377
+ #### DownloadClient
378
+
379
+ ```typescript
380
+ const client = new DownloadClient({
381
+ baseUrl: 'https://your-api.com', // 可选,默认使用全局配置
382
+ token: 'your-token', // 可选,默认使用全局配置
383
+ headers: { // 可选,自定义请求头
384
+ 'X-Custom-Header': 'value',
385
+ },
386
+ });
387
+ ```
388
+
389
+ #### 方法
390
+
391
+ ##### `download(options: DownloadOptions): Promise<Blob>`
392
+
393
+ 普通下载文件,返回 Blob 对象。
394
+
395
+ ```typescript
396
+ const blob = await client.download({
397
+ url: 'https://example.com/file.zip',
398
+ filename: 'myfile.zip', // 可选,下载时的文件名
399
+ });
400
+
401
+ // 使用 Blob
402
+ const text = await blob.text(); // 读取为文本
403
+ const arrayBuffer = await blob.arrayBuffer(); // 读取为 ArrayBuffer
404
+ const url = URL.createObjectURL(blob); // 创建对象 URL
405
+ ```
406
+
407
+ **参数:**
408
+ - `url` (string, 必需): 要下载的文件 URL(必须是 http:// 或 https://)
409
+ - `filename` (string, 可选): 下载时的文件名,不提供则从 URL 或响应头中提取
410
+
411
+ **返回:** `Promise<Blob>` - 文件的 Blob 对象
412
+
413
+ **错误处理:**
414
+ ```typescript
415
+ try {
416
+ const blob = await client.download({
417
+ url: 'https://example.com/file.zip',
418
+ });
419
+ console.log('下载成功:', blob.size, '字节');
420
+ } catch (error) {
421
+ if (error instanceof Error) {
422
+ console.error('下载失败:', error.message);
423
+ // 常见错误:
424
+ // - "URL is required" - URL 参数缺失
425
+ // - "Download failed: HTTP 404" - 文件不存在
426
+ // - "Download failed: HTTP 403" - 访问被拒绝
427
+ // - "下载超时,请稍后重试" - 下载超时
428
+ }
429
+ }
430
+ ```
431
+
432
+ ##### `downloadAsFile(options: DownloadOptions, downloadFilename?: string): Promise<void>`
433
+
434
+ 下载文件并触发浏览器保存对话框。
435
+
436
+ ```typescript
437
+ await client.downloadAsFile({
438
+ url: 'https://example.com/file.zip',
439
+ filename: 'myfile.zip',
440
+ }, 'custom-filename.zip'); // 可选,覆盖 filename
441
+ ```
442
+
443
+ **参数:**
444
+ - `options`: 同 `download()` 方法
445
+ - `downloadFilename` (string, 可选): 下载时的文件名,会覆盖 `options.filename`
446
+
447
+ **返回:** `Promise<void>`
448
+
449
+ ##### `streamDownload(options: StreamDownloadOptions): AsyncGenerator<Uint8Array>`
450
+
451
+ 流式下载文件,返回异步迭代器,支持 `for await` 迭代。
452
+
453
+ ```typescript
454
+ let totalSize = 0;
455
+ for await (const chunk of client.streamDownload({
456
+ url: 'https://example.com/large-file.zip',
457
+ filename: 'large-file.zip',
458
+ })) {
459
+ totalSize += chunk.length;
460
+ console.log(`已下载: ${totalSize} 字节`);
461
+ // 可以在这里处理每个数据块(例如:实时处理、显示进度等)
462
+ }
463
+ console.log('下载完成,总大小:', totalSize, '字节');
464
+ ```
465
+
466
+ **参数:**
467
+ - `url` (string, 必需): 要下载的文件 URL
468
+ - `filename` (string, 可选): 下载时的文件名
469
+
470
+ **返回:** `AsyncGenerator<Uint8Array>` - 异步迭代器,每次迭代返回一个 `Uint8Array` 数据块
471
+
472
+ **使用场景:**
473
+ - 下载大文件时显示实时进度(通过累计 chunk.length)
474
+ - 需要处理数据流(例如:实时解析、转码等)
475
+ - 需要监控下载状态
476
+ - 需要流式处理文件内容
477
+
478
+ **示例:带进度显示**
479
+
480
+ ```typescript
481
+ // 如果服务器提供了 Content-Length,可以从响应头获取总大小
482
+ let totalSize = 0;
483
+ const total = 1024 * 1024 * 10; // 假设已知总大小(实际应从响应头获取)
484
+
485
+ for await (const chunk of client.streamDownload({
486
+ url: 'https://example.com/large-file.zip',
487
+ })) {
488
+ totalSize += chunk.length;
489
+ const percent = total ? Math.round((totalSize / total) * 100) : null;
490
+ console.log(`下载进度: ${totalSize}/${total || '未知'} (${percent || '?'}%)`);
491
+ }
492
+ ```
493
+
494
+ **示例:流式处理并保存**
495
+
496
+ ```typescript
497
+ // 流式下载并实时保存到文件系统(Node.js 环境)
498
+ import { createWriteStream } from 'fs';
499
+ import { pipeline } from 'stream/promises';
500
+
501
+ const chunks: Uint8Array[] = [];
502
+ let totalSize = 0;
503
+
504
+ for await (const chunk of client.streamDownload({
505
+ url: 'https://example.com/large-file.zip',
506
+ filename: 'large-file.zip',
507
+ })) {
508
+ chunks.push(chunk);
509
+ totalSize += chunk.length;
510
+ console.log(`已接收: ${totalSize} 字节`);
511
+ }
512
+
513
+ // 合并所有块并保存
514
+ const blob = new Blob(chunks);
515
+ const buffer = Buffer.from(await blob.arrayBuffer());
516
+ await fs.promises.writeFile('large-file.zip', buffer);
517
+ console.log('文件保存完成');
518
+ ```
519
+
520
+ **注意事项:**
521
+ - 流式下载适合大文件,可以实时显示进度
522
+ - 普通下载适合小文件,会一次性加载到内存
523
+ - URL 必须是 `http://` 或 `https://` 协议
524
+ - 文件名会自动从 URL 或响应头中提取(如果未提供)
525
+ - 流式下载时,数据块大小由服务器决定,通常为 8KB-64KB
526
+
342
527
  ### 全局配置
343
528
 
344
529
  ```typescript
@@ -740,6 +925,94 @@ if (result.status === 'succeeded') {
740
925
  }
741
926
  ```
742
927
 
928
+ ### 下载代理工作流
929
+
930
+ ```typescript
931
+ import { DownloadClient } from 'ai-world-sdk';
932
+
933
+ const client = new DownloadClient({});
934
+
935
+ // 1. 普通下载(小文件)
936
+ const blob = await client.download({
937
+ url: 'https://example.com/small-file.json',
938
+ filename: 'data.json',
939
+ });
940
+
941
+ // 读取文件内容
942
+ const text = await blob.text();
943
+ const data = JSON.parse(text);
944
+ console.log('下载的数据:', data);
945
+
946
+ // 2. 下载并保存到本地
947
+ await client.downloadAsFile({
948
+ url: 'https://example.com/document.pdf',
949
+ filename: 'document.pdf',
950
+ });
951
+
952
+ // 3. 流式下载大文件(带进度显示)
953
+ let totalSize = 0;
954
+ const total = 1024 * 1024 * 100; // 假设已知总大小(实际应从响应头获取)
955
+
956
+ for await (const chunk of client.streamDownload({
957
+ url: 'https://example.com/large-video.mp4',
958
+ filename: 'video.mp4',
959
+ })) {
960
+ totalSize += chunk.length;
961
+
962
+ // 显示进度
963
+ if (total) {
964
+ const percent = Math.round((totalSize / total) * 100);
965
+ console.log(`下载进度: ${percent}% (${totalSize}/${total} 字节)`);
966
+ } else {
967
+ console.log(`已下载: ${totalSize} 字节`);
968
+ }
969
+
970
+ // 可以在这里处理每个数据块
971
+ // 例如:实时验证、显示下载速度等
972
+ }
973
+
974
+ console.log(`下载完成!总大小: ${totalSize} 字节`);
975
+
976
+ // 4. 批量下载多个文件
977
+ const urls = [
978
+ 'https://example.com/file1.zip',
979
+ 'https://example.com/file2.zip',
980
+ 'https://example.com/file3.zip',
981
+ ];
982
+
983
+ for (const url of urls) {
984
+ try {
985
+ await client.downloadAsFile({
986
+ url,
987
+ filename: url.split('/').pop() || 'download',
988
+ });
989
+ console.log(`已下载: ${url}`);
990
+ } catch (error) {
991
+ console.error(`下载失败 ${url}:`, error);
992
+ }
993
+ }
994
+
995
+ // 5. 流式下载并实时处理(例如:解析 JSON 流)
996
+ let buffer = '';
997
+ for await (const chunk of client.streamDownload({
998
+ url: 'https://example.com/large-data.jsonl',
999
+ })) {
1000
+ // 将 Uint8Array 转换为字符串
1001
+ buffer += new TextDecoder().decode(chunk);
1002
+
1003
+ // 处理完整的行
1004
+ const lines = buffer.split('\n');
1005
+ buffer = lines.pop() || ''; // 保留不完整的行
1006
+
1007
+ for (const line of lines) {
1008
+ if (line.trim()) {
1009
+ const data = JSON.parse(line);
1010
+ console.log('处理数据:', data);
1011
+ }
1012
+ }
1013
+ }
1014
+ ```
1015
+
743
1016
  ## 支持的模型
744
1017
 
745
1018
  ### 聊天模型
@@ -670,6 +670,68 @@ describe("Langchain SDK Tests", () => {
670
670
  console.log("图像描述:", result.text);
671
671
  }
672
672
  }, 120000);
673
+ test("ChatGoogleGenerativeAI - api2img.com no stream", async () => {
674
+ const gemini = new index_1.ChatGoogleGenerativeAI({
675
+ modelName: "gemini-2.5-flash-image",
676
+ temperature: 0.7,
677
+ provider: "api2img",
678
+ });
679
+ const response = await gemini.invoke([
680
+ new index_1.HumanMessage("api2img.com是做什么的"),
681
+ ]);
682
+ expect(response).toBeDefined();
683
+ expect(response.content).toBeDefined();
684
+ expect(typeof response.content).toBe("string");
685
+ console.log("✅ api2img.com no stream 测试成功");
686
+ console.log("AI:", response.content);
687
+ }, 30000);
688
+ test("ChatGoogleGenerativeAI - api2img.com stream", async () => {
689
+ const gemini = new index_1.ChatGoogleGenerativeAI({
690
+ modelName: "gemini-2.5-flash-image",
691
+ temperature: 0.7,
692
+ provider: "api2img",
693
+ });
694
+ let fullText = "";
695
+ for await (const chunk of gemini.stream([
696
+ new index_1.HumanMessage("api2img.com是做什么的"),
697
+ ])) {
698
+ fullText += extractTextFromChunk(chunk);
699
+ console.log("AI stream chunk:", chunk);
700
+ console.log("AI stream text:", fullText);
701
+ }
702
+ expect(fullText.length).toBeGreaterThan(0);
703
+ console.log("✅ api2img.com stream 测试成功");
704
+ console.log("AI:", fullText);
705
+ console.log(`完整回复长度: ${fullText.length} 字符`);
706
+ }, 30000);
707
+ test("GeminiImageGenerationClient - 使用 api2img provider", async () => {
708
+ const imageClient = new index_1.GeminiImageGenerationClient({
709
+ provider: "api2img",
710
+ });
711
+ const result = await imageClient.generate({
712
+ prompt: 'A beautiful sunset over the ocean',
713
+ model: 'gemini-3-pro-image-preview',
714
+ aspect_ratio: '16:9',
715
+ image_size: '1K',
716
+ response_modalities: ['IMAGE'], // 仅返回图片
717
+ });
718
+ expect(result).toBeDefined();
719
+ expect(result.created).toBeDefined();
720
+ expect(typeof result.created).toBe("number");
721
+ expect(result.data).toBeDefined();
722
+ expect(Array.isArray(result.data)).toBe(true);
723
+ expect(result.data.length).toBeGreaterThan(0);
724
+ result.data.forEach((item) => {
725
+ expect(item).toBeDefined();
726
+ expect(item.url || item.b64_json).toBeDefined();
727
+ });
728
+ console.log("✅ GeminiImageGenerationClient (api2img provider) 测试成功");
729
+ console.log(`生成图像数量: ${result.data.length}`);
730
+ console.log("图像 URL:", result.data[0]?.url || ("Base64 编码" + result.data[0]?.b64_json));
731
+ if (result.text) {
732
+ console.log("图像描述:", result.text);
733
+ }
734
+ }, 120000);
673
735
  test("GeminiImageGenerationClient - 多轮图片修改", async () => {
674
736
  const imageClient = new index_1.GeminiImageGenerationClient({});
675
737
  // 第一轮:创建初始图片
package/dist/base.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Similar to LangChain.js BaseChatModel
4
4
  */
5
5
  import { BaseMessage, AIMessage, AIMessageChunk } from "./messages";
6
- export type AIModelProvider = "aihubmix" | "doubao" | "gemini";
6
+ export type AIModelProvider = "aihubmix" | "api2img" | "doubao" | "gemini";
7
7
  export interface BaseChatModelParams {
8
8
  provider: AIModelProvider;
9
9
  baseUrl?: string;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Download Client
3
+ * 下载客户端
4
+ * 支持流式下载和普通下载任意 URL 的二进制文件
5
+ */
6
+ export interface DownloadConfig {
7
+ baseUrl?: string;
8
+ token?: string;
9
+ headers?: Record<string, string>;
10
+ }
11
+ export interface DownloadOptions {
12
+ url: string;
13
+ filename?: string;
14
+ }
15
+ export interface StreamDownloadOptions extends DownloadOptions {
16
+ }
17
+ /**
18
+ * Download Client
19
+ * 下载客户端类
20
+ */
21
+ export declare class DownloadClient {
22
+ private baseUrl;
23
+ private headers;
24
+ constructor(config?: DownloadConfig);
25
+ /**
26
+ * Download a file from a URL (non-streaming)
27
+ * 从 URL 下载文件(非流式)
28
+ *
29
+ * @param options - Download options
30
+ * @returns Promise that resolves to a Blob
31
+ */
32
+ download(options: DownloadOptions): Promise<Blob>;
33
+ /**
34
+ * Stream download a file from a URL
35
+ * 流式下载文件,返回异步迭代器
36
+ *
37
+ * @param options - Stream download options
38
+ * @returns AsyncGenerator that yields data chunks
39
+ * @example
40
+ * ```typescript
41
+ * let totalSize = 0;
42
+ * for await (const chunk of client.streamDownload({ url: '...' })) {
43
+ * totalSize += chunk.length;
44
+ * // 处理每个数据块
45
+ * console.log(`已下载: ${totalSize} 字节`);
46
+ * }
47
+ * console.log(`下载完成,总大小: ${totalSize} 字节`);
48
+ * ```
49
+ */
50
+ streamDownload(options: StreamDownloadOptions): AsyncGenerator<Uint8Array, void, unknown>;
51
+ /**
52
+ * Download a file and trigger browser download
53
+ * 下载文件并触发浏览器下载
54
+ *
55
+ * @param options - Download options
56
+ * @param downloadFilename - Optional filename for the download (overrides options.filename)
57
+ */
58
+ downloadAsFile(options: DownloadOptions, downloadFilename?: string): Promise<void>;
59
+ /**
60
+ * Extract filename from URL
61
+ * 从 URL 中提取文件名
62
+ */
63
+ private extractFilenameFromUrl;
64
+ }
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ /**
3
+ * Download Client
4
+ * 下载客户端
5
+ * 支持流式下载和普通下载任意 URL 的二进制文件
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.DownloadClient = void 0;
9
+ const config_1 = require("./config");
10
+ const log_1 = require("./log");
11
+ /**
12
+ * Download Client
13
+ * 下载客户端类
14
+ */
15
+ class DownloadClient {
16
+ constructor(config = {}) {
17
+ // 使用配置的 baseUrl 或全局配置
18
+ this.baseUrl =
19
+ config.baseUrl ||
20
+ config_1.sdkConfig.getServerUrl() ||
21
+ (typeof window !== "undefined" ? window.location.origin : "");
22
+ // 合并 headers
23
+ const globalHeaders = config_1.sdkConfig.getHeaders();
24
+ const globalToken = config_1.sdkConfig.getToken() || config.token;
25
+ this.headers = {
26
+ "Content-Type": "application/json",
27
+ ...globalHeaders,
28
+ ...config.headers,
29
+ };
30
+ // 如果有 token,添加到 Authorization header
31
+ if (globalToken) {
32
+ this.headers["Authorization"] = `Bearer ${globalToken}`;
33
+ }
34
+ else if (config.token) {
35
+ this.headers["Authorization"] = `Bearer ${config.token}`;
36
+ }
37
+ }
38
+ /**
39
+ * Download a file from a URL (non-streaming)
40
+ * 从 URL 下载文件(非流式)
41
+ *
42
+ * @param options - Download options
43
+ * @returns Promise that resolves to a Blob
44
+ */
45
+ async download(options) {
46
+ const { url, filename } = options;
47
+ if (!url) {
48
+ throw new Error("URL is required");
49
+ }
50
+ // 构建请求 URL
51
+ const apiUrl = `${this.baseUrl}/api/download-proxy/download`;
52
+ const params = new URLSearchParams({ url });
53
+ if (filename) {
54
+ params.append("filename", filename);
55
+ }
56
+ const fullUrl = `${apiUrl}?${params.toString()}`;
57
+ (0, log_1.debugLog)("Download request:", { url, filename, fullUrl });
58
+ (0, log_1.logRequest)("GET", fullUrl, this.headers);
59
+ try {
60
+ const response = await fetch(fullUrl, {
61
+ method: "GET",
62
+ headers: this.headers,
63
+ });
64
+ if (!response.ok) {
65
+ const errorText = await response.text();
66
+ let errorMessage = `Download failed: ${response.status} ${response.statusText}`;
67
+ try {
68
+ const errorJson = JSON.parse(errorText);
69
+ errorMessage = errorJson.detail || errorMessage;
70
+ }
71
+ catch {
72
+ errorMessage = errorText || errorMessage;
73
+ }
74
+ throw new Error(errorMessage);
75
+ }
76
+ const blob = await response.blob();
77
+ (0, log_1.debugLog)("Download completed:", {
78
+ size: blob.size,
79
+ type: blob.type,
80
+ });
81
+ return blob;
82
+ }
83
+ catch (error) {
84
+ const errorMessage = error instanceof Error ? error.message : String(error);
85
+ (0, log_1.debugLog)("Download error:", errorMessage);
86
+ throw error;
87
+ }
88
+ }
89
+ /**
90
+ * Stream download a file from a URL
91
+ * 流式下载文件,返回异步迭代器
92
+ *
93
+ * @param options - Stream download options
94
+ * @returns AsyncGenerator that yields data chunks
95
+ * @example
96
+ * ```typescript
97
+ * let totalSize = 0;
98
+ * for await (const chunk of client.streamDownload({ url: '...' })) {
99
+ * totalSize += chunk.length;
100
+ * // 处理每个数据块
101
+ * console.log(`已下载: ${totalSize} 字节`);
102
+ * }
103
+ * console.log(`下载完成,总大小: ${totalSize} 字节`);
104
+ * ```
105
+ */
106
+ async *streamDownload(options) {
107
+ const { url, filename } = options;
108
+ if (!url) {
109
+ throw new Error("URL is required");
110
+ }
111
+ // 构建请求 URL
112
+ const apiUrl = `${this.baseUrl}/api/download-proxy/download`;
113
+ const params = new URLSearchParams({ url });
114
+ if (filename) {
115
+ params.append("filename", filename);
116
+ }
117
+ const fullUrl = `${apiUrl}?${params.toString()}`;
118
+ (0, log_1.debugLog)("Stream download request:", { url, filename, fullUrl });
119
+ (0, log_1.logRequest)("GET", fullUrl, this.headers);
120
+ let response;
121
+ try {
122
+ response = await fetch(fullUrl, {
123
+ method: "GET",
124
+ headers: this.headers,
125
+ });
126
+ if (!response.ok) {
127
+ const errorText = await response.text();
128
+ let errorMessage = `Download failed: ${response.status} ${response.statusText}`;
129
+ try {
130
+ const errorJson = JSON.parse(errorText);
131
+ errorMessage = errorJson.detail || errorMessage;
132
+ }
133
+ catch {
134
+ errorMessage = errorText || errorMessage;
135
+ }
136
+ throw new Error(errorMessage);
137
+ }
138
+ }
139
+ catch (error) {
140
+ const errorMessage = error instanceof Error ? error.message : String(error);
141
+ (0, log_1.debugLog)("Stream download error:", errorMessage);
142
+ throw error;
143
+ }
144
+ // // 获取内容长度(如果可用)
145
+ // const contentLength = response.headers.get("content-length");
146
+ // const total = contentLength ? parseInt(contentLength, 10) : undefined;
147
+ // 创建 ReadableStream 来处理流式数据
148
+ const reader = response.body?.getReader();
149
+ if (!reader) {
150
+ throw new Error("Response body is not readable");
151
+ }
152
+ try {
153
+ while (true) {
154
+ const { done, value } = await reader.read();
155
+ if (done) {
156
+ break;
157
+ }
158
+ if (value) {
159
+ // yield 数据块,用户可以在迭代时累计大小
160
+ yield value;
161
+ }
162
+ }
163
+ }
164
+ finally {
165
+ reader.releaseLock();
166
+ }
167
+ }
168
+ /**
169
+ * Download a file and trigger browser download
170
+ * 下载文件并触发浏览器下载
171
+ *
172
+ * @param options - Download options
173
+ * @param downloadFilename - Optional filename for the download (overrides options.filename)
174
+ */
175
+ async downloadAsFile(options, downloadFilename) {
176
+ const blob = await this.download(options);
177
+ // 确定下载文件名
178
+ const filename = downloadFilename ||
179
+ options.filename ||
180
+ this.extractFilenameFromUrl(options.url) ||
181
+ "download";
182
+ // 创建下载链接
183
+ const url = URL.createObjectURL(blob);
184
+ const a = document.createElement("a");
185
+ a.href = url;
186
+ a.download = filename;
187
+ document.body.appendChild(a);
188
+ a.click();
189
+ document.body.removeChild(a);
190
+ // 清理 URL
191
+ setTimeout(() => URL.revokeObjectURL(url), 100);
192
+ }
193
+ /**
194
+ * Extract filename from URL
195
+ * 从 URL 中提取文件名
196
+ */
197
+ extractFilenameFromUrl(url) {
198
+ try {
199
+ const urlObj = new URL(url);
200
+ const pathname = urlObj.pathname;
201
+ const filename = pathname.split("/").pop();
202
+ if (filename && filename.includes(".")) {
203
+ return filename.split("?")[0]; // 移除查询参数
204
+ }
205
+ return null;
206
+ }
207
+ catch {
208
+ return null;
209
+ }
210
+ }
211
+ }
212
+ exports.DownloadClient = DownloadClient;
@@ -2,8 +2,9 @@
2
2
  * Gemini Image Generation Client
3
3
  * Google Gemini 图像生成客户端
4
4
  */
5
+ export type GeminiImageGenerationProvider = "aihubmix" | "api2img" | "gemini";
5
6
  export interface GeminiImageGenerationConfig {
6
- provider?: "aihubmix" | "gemini";
7
+ provider?: GeminiImageGenerationProvider;
7
8
  baseUrl?: string;
8
9
  headers?: Record<string, string>;
9
10
  }
@@ -32,7 +33,7 @@ export interface GeminiImageChatRequest {
32
33
  message: string;
33
34
  chat_id?: string;
34
35
  model?: string;
35
- provider?: "aihubmix" | "gemini";
36
+ provider?: GeminiImageGenerationProvider;
36
37
  aspect_ratio?: "1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "4:5" | "5:4" | "9:16" | "16:9" | "21:9";
37
38
  image_size?: "1K" | "2K" | "4K";
38
39
  response_modalities?: ("TEXT" | "IMAGE")[];
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ import { BaseChatModel, BaseChatModelParams } from "./base";
8
8
  import { DoubaoImageGenerationClient, DoubaoImageSize, type DoubaoImageGenerationConfig, type DoubaoImageGenerationRequest, type DoubaoImageGenerationResponse } from "./doubao-image-generation";
9
9
  import { GeminiImageGenerationClient, type GeminiImageGenerationConfig, type GeminiImageGenerationRequest, type GeminiImageGenerationResponse } from "./gemini-image-generation";
10
10
  import { VideoGenerationClient, type VideoGenerationConfig, type VideoGenerationRequest, type ContentGenerationTaskID, type ContentGenerationTask } from "./video_generation";
11
+ import { DownloadClient, type DownloadConfig, type DownloadOptions, type StreamDownloadOptions } from "./download";
11
12
  import { sdkConfig } from "./config";
12
13
  export { BaseMessage, HumanMessage, AIMessage, SystemMessage, AIMessageChunk, type MessageContent, type AIMessageChunkData, } from "./messages";
13
14
  export { BaseChatModel, type BaseChatModelParams, type AIModelProvider, type ToolDefinition, type BindOptions, } from "./base";
@@ -22,6 +23,7 @@ export interface LangchainClientConfig {
22
23
  export { DoubaoImageGenerationClient, type DoubaoImageGenerationConfig, type DoubaoImageGenerationRequest, type DoubaoImageGenerationResponse, type DoubaoImageSize, };
23
24
  export { GeminiImageGenerationClient, type GeminiImageGenerationConfig, type GeminiImageGenerationRequest, type GeminiImageGenerationResponse, };
24
25
  export { VideoGenerationClient, type VideoGenerationConfig, type VideoGenerationRequest, type ContentGenerationTaskID, type ContentGenerationTask, };
26
+ export { DownloadClient, type DownloadConfig, type DownloadOptions, type StreamDownloadOptions, };
25
27
  export { sdkConfig };
26
28
  /**
27
29
  * Create a chat model instance based on model name
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * @see https://github.com/langchain-ai/langchainjs
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.sdkConfig = exports.VideoGenerationClient = exports.GeminiImageGenerationClient = exports.DoubaoImageGenerationClient = exports.ChatAnthropic = exports.ChatGoogleGenerativeAI = exports.ChatOpenAI = exports.BaseChatModel = exports.AIMessageChunk = exports.SystemMessage = exports.AIMessage = exports.HumanMessage = void 0;
9
+ exports.sdkConfig = exports.DownloadClient = exports.VideoGenerationClient = exports.GeminiImageGenerationClient = exports.DoubaoImageGenerationClient = exports.ChatAnthropic = exports.ChatGoogleGenerativeAI = exports.ChatOpenAI = exports.BaseChatModel = exports.AIMessageChunk = exports.SystemMessage = exports.AIMessage = exports.HumanMessage = void 0;
10
10
  exports.createChatModel = createChatModel;
11
11
  const openai_1 = require("./chat_models/openai");
12
12
  const google_1 = require("./chat_models/google");
@@ -16,6 +16,8 @@ const gemini_image_generation_1 = require("./gemini-image-generation");
16
16
  Object.defineProperty(exports, "GeminiImageGenerationClient", { enumerable: true, get: function () { return gemini_image_generation_1.GeminiImageGenerationClient; } });
17
17
  const video_generation_1 = require("./video_generation");
18
18
  Object.defineProperty(exports, "VideoGenerationClient", { enumerable: true, get: function () { return video_generation_1.VideoGenerationClient; } });
19
+ const download_1 = require("./download");
20
+ Object.defineProperty(exports, "DownloadClient", { enumerable: true, get: function () { return download_1.DownloadClient; } });
19
21
  const config_1 = require("./config");
20
22
  Object.defineProperty(exports, "sdkConfig", { enumerable: true, get: function () { return config_1.sdkConfig; } });
21
23
  // Re-export types and classes
@@ -50,6 +52,7 @@ function createChatModel(model, config) {
50
52
  switch (config.provider) {
51
53
  case "doubao":
52
54
  case "aihubmix":
55
+ case "api2img":
53
56
  return new openai_1.ChatOpenAI({
54
57
  modelName: model,
55
58
  ...finalConfig,
package/dist/log.d.ts CHANGED
@@ -11,7 +11,7 @@ export declare function debugLog(...args: any[]): void;
11
11
  * Log HTTP request
12
12
  * 记录 HTTP 请求
13
13
  */
14
- export declare function logRequest(method: string, url: string, headers: Record<string, string>, body?: any): void;
14
+ export declare function logRequest(method: string, url: string, headers?: Record<string, string>, body?: any): void;
15
15
  /**
16
16
  * Log HTTP response
17
17
  * 记录 HTTP 响应
package/dist/log.js CHANGED
@@ -27,7 +27,7 @@ function logRequest(method, url, headers, body) {
27
27
  debugLog("📤 HTTP Request");
28
28
  debugLog(" Method:", method);
29
29
  debugLog(" URL:", url);
30
- debugLog(" Headers:", { ...headers, Authorization: headers.Authorization ? "Bearer ***" : undefined });
30
+ debugLog(" Headers:", { ...headers, Authorization: headers?.Authorization ? "Bearer ***" : undefined });
31
31
  if (body) {
32
32
  try {
33
33
  const bodyStr = typeof body === "string" ? body : JSON.stringify(body, null, 2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-world-sdk",
3
- "version": "1.0.12",
3
+ "version": "1.0.15",
4
4
  "description": "TypeScript SDK for AI World Platform - Chat Models, Image Generation, and Video Generation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",