@kevisual/ai 0.0.19 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-provider-browser.d.ts +14 -7
- package/dist/ai-provider-browser.js +2295 -1620
- package/dist/ai-provider.d.ts +14 -7
- package/dist/ai-provider.js +21 -21
- package/package.json +15 -14
- package/src/jimeng/core.ts +113 -43
- package/src/provider/chat-adapter/mimo.ts +10 -0
- package/src/provider/chat-adapter/ollama.ts +0 -5
- package/src/provider/chat-adapter/siliconflow.ts +0 -4
- package/src/provider/core/chat.ts +28 -12
- package/src/provider/core/type.ts +7 -4
package/dist/ai-provider.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ type ChatMessage = {
|
|
|
5
5
|
role?: 'user' | 'assistant' | 'system' | 'tool';
|
|
6
6
|
content: string;
|
|
7
7
|
};
|
|
8
|
-
type ChatMessageOptions = {
|
|
9
|
-
messages
|
|
8
|
+
type ChatMessageOptions<T = {}> = {
|
|
9
|
+
messages?: ChatMessage[];
|
|
10
10
|
/**
|
|
11
11
|
* 模型名称
|
|
12
12
|
*/
|
|
@@ -46,7 +46,7 @@ type ChatMessageOptions = {
|
|
|
46
46
|
stream?: boolean;
|
|
47
47
|
/**
|
|
48
48
|
* 是否能够思考
|
|
49
|
-
* 如果会话是千文,服务器的接口,默认为
|
|
49
|
+
* 如果会话是千文,服务器的接口,默认为 false
|
|
50
50
|
*/
|
|
51
51
|
enable_thinking?: boolean;
|
|
52
52
|
response_format?: 'text' | 'json' | 'xml' | 'html';
|
|
@@ -54,7 +54,8 @@ type ChatMessageOptions = {
|
|
|
54
54
|
* 工具调用参数
|
|
55
55
|
*/
|
|
56
56
|
tool_calls?: any;
|
|
57
|
-
|
|
57
|
+
[key: string]: any;
|
|
58
|
+
} & T;
|
|
58
59
|
type Choice = {
|
|
59
60
|
finish_reason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | 'function_call';
|
|
60
61
|
index: number;
|
|
@@ -161,6 +162,7 @@ type EmbeddingMessageComplete = {
|
|
|
161
162
|
usage: Usage;
|
|
162
163
|
};
|
|
163
164
|
interface BaseChatInterface {
|
|
165
|
+
chat(options: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
164
166
|
chat(messages: ChatMessage[], options?: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
165
167
|
}
|
|
166
168
|
interface Usage {
|
|
@@ -305,8 +307,10 @@ declare class BaseChat implements BaseChatInterface, Usage {
|
|
|
305
307
|
/**
|
|
306
308
|
* 聊天
|
|
307
309
|
*/
|
|
310
|
+
chat(options: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
308
311
|
chat(messages: ChatMessage[], options?: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
309
|
-
chatStream(
|
|
312
|
+
chatStream(options: ChatMessageOptions): AsyncGenerator<ChatMessageComplete>;
|
|
313
|
+
chatStream(messages: ChatMessage[], options?: ChatMessageOptions): AsyncGenerator<ChatMessageComplete>;
|
|
310
314
|
/**
|
|
311
315
|
* 简单提问接口
|
|
312
316
|
* @param message
|
|
@@ -323,6 +327,11 @@ declare class BaseChat implements BaseChatInterface, Usage {
|
|
|
323
327
|
total_tokens: number;
|
|
324
328
|
completion_tokens: number;
|
|
325
329
|
};
|
|
330
|
+
setChatUsage(usage: {
|
|
331
|
+
prompt_tokens?: number;
|
|
332
|
+
total_tokens?: number;
|
|
333
|
+
completion_tokens?: number;
|
|
334
|
+
}): void;
|
|
326
335
|
getHeaders(headers?: Record<string, string>): {
|
|
327
336
|
'Content-Type': string;
|
|
328
337
|
Authorization: string;
|
|
@@ -362,7 +371,6 @@ type OllamaModel = {
|
|
|
362
371
|
declare class Ollama extends BaseChat {
|
|
363
372
|
static BASE_URL: string;
|
|
364
373
|
constructor(options: OllamaOptions$1);
|
|
365
|
-
chat(messages: ChatMessage[], options?: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
366
374
|
/**
|
|
367
375
|
* 获取模型列表
|
|
368
376
|
* @returns
|
|
@@ -400,7 +408,6 @@ declare class SiliconFlow extends BaseChat {
|
|
|
400
408
|
static BASE_URL: string;
|
|
401
409
|
constructor(options: SiliconFlowOptions);
|
|
402
410
|
getUsageInfo(): Promise<SiliconFlowUsageResponse>;
|
|
403
|
-
chat(messages: ChatMessage[], options?: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
404
411
|
}
|
|
405
412
|
|
|
406
413
|
type OllamaOptions = BaseChatOptions;
|
package/dist/ai-provider.js
CHANGED
|
@@ -1350,10 +1350,10 @@ class BaseChat {
|
|
|
1350
1350
|
baseURL;
|
|
1351
1351
|
model;
|
|
1352
1352
|
apiKey;
|
|
1353
|
-
prompt_tokens;
|
|
1354
|
-
total_tokens;
|
|
1355
|
-
completion_tokens;
|
|
1356
|
-
responseText;
|
|
1353
|
+
prompt_tokens = 0;
|
|
1354
|
+
total_tokens = 0;
|
|
1355
|
+
completion_tokens = 0;
|
|
1356
|
+
responseText = "";
|
|
1357
1357
|
utils = AIUtils;
|
|
1358
1358
|
constructor(options) {
|
|
1359
1359
|
this.baseURL = options.baseURL;
|
|
@@ -1385,11 +1385,14 @@ class BaseChat {
|
|
|
1385
1385
|
}
|
|
1386
1386
|
}).then((res) => res.json());
|
|
1387
1387
|
}
|
|
1388
|
-
async chat(
|
|
1388
|
+
async chat(messagesOrOptions, options) {
|
|
1389
|
+
const isFirstParamOptions = !Array.isArray(messagesOrOptions);
|
|
1390
|
+
const messages = isFirstParamOptions ? messagesOrOptions.messages : messagesOrOptions;
|
|
1391
|
+
const opts = isFirstParamOptions ? messagesOrOptions : options || {};
|
|
1389
1392
|
const requestBody = {
|
|
1390
1393
|
model: this.model,
|
|
1391
1394
|
messages,
|
|
1392
|
-
...
|
|
1395
|
+
...opts,
|
|
1393
1396
|
stream: false
|
|
1394
1397
|
};
|
|
1395
1398
|
const response = await this.post(`${this.baseURL}/chat/completions`, { data: requestBody });
|
|
@@ -1398,20 +1401,21 @@ class BaseChat {
|
|
|
1398
1401
|
throw new Error(`Chat API request failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
1399
1402
|
}
|
|
1400
1403
|
const res = await response.json();
|
|
1401
|
-
this.
|
|
1402
|
-
this.total_tokens = res.usage?.total_tokens ?? 0;
|
|
1403
|
-
this.completion_tokens = res.usage?.completion_tokens ?? 0;
|
|
1404
|
+
this.setChatUsage(res.usage);
|
|
1404
1405
|
this.responseText = res.choices[0]?.message?.content || "";
|
|
1405
1406
|
return res;
|
|
1406
1407
|
}
|
|
1407
|
-
async chatStream(
|
|
1408
|
-
|
|
1408
|
+
async* chatStream(messagesOrOptions, options) {
|
|
1409
|
+
const isFirstParamOptions = !Array.isArray(messagesOrOptions);
|
|
1410
|
+
const messages = isFirstParamOptions ? messagesOrOptions.messages : messagesOrOptions;
|
|
1411
|
+
const opts = isFirstParamOptions ? messagesOrOptions : options || {};
|
|
1412
|
+
if (opts.response_format) {
|
|
1409
1413
|
throw new Error("response_format is not supported in stream mode");
|
|
1410
1414
|
}
|
|
1411
1415
|
const requestBody = {
|
|
1412
1416
|
model: this.model,
|
|
1413
1417
|
messages,
|
|
1414
|
-
...
|
|
1418
|
+
...opts,
|
|
1415
1419
|
stream: true
|
|
1416
1420
|
};
|
|
1417
1421
|
const response = await this.post(`${this.baseURL}/chat/completions`, { data: requestBody });
|
|
@@ -1465,6 +1469,11 @@ class BaseChat {
|
|
|
1465
1469
|
completion_tokens: this.completion_tokens
|
|
1466
1470
|
};
|
|
1467
1471
|
}
|
|
1472
|
+
setChatUsage(usage) {
|
|
1473
|
+
this.prompt_tokens = usage.prompt_tokens ?? this.prompt_tokens;
|
|
1474
|
+
this.total_tokens = usage.total_tokens ?? this.total_tokens;
|
|
1475
|
+
this.completion_tokens = usage.completion_tokens ?? this.completion_tokens;
|
|
1476
|
+
}
|
|
1468
1477
|
getHeaders(headers) {
|
|
1469
1478
|
return {
|
|
1470
1479
|
"Content-Type": "application/json",
|
|
@@ -1505,11 +1514,6 @@ class Ollama extends BaseChat {
|
|
|
1505
1514
|
const baseURL = options.baseURL || Ollama.BASE_URL;
|
|
1506
1515
|
super({ ...options, baseURL });
|
|
1507
1516
|
}
|
|
1508
|
-
async chat(messages, options) {
|
|
1509
|
-
const res = await super.chat(messages, options);
|
|
1510
|
-
console.log("thunk", this.getChatUsage());
|
|
1511
|
-
return res;
|
|
1512
|
-
}
|
|
1513
1517
|
async listModels() {
|
|
1514
1518
|
const _url = new URL(this.baseURL);
|
|
1515
1519
|
const tagsURL = new URL("/api/tags", _url);
|
|
@@ -1532,10 +1536,6 @@ class SiliconFlow extends BaseChat {
|
|
|
1532
1536
|
async getUsageInfo() {
|
|
1533
1537
|
return this.get("/user/info");
|
|
1534
1538
|
}
|
|
1535
|
-
async chat(messages, options) {
|
|
1536
|
-
const res = await super.chat(messages, options);
|
|
1537
|
-
return res;
|
|
1538
|
-
}
|
|
1539
1539
|
}
|
|
1540
1540
|
|
|
1541
1541
|
// src/provider/chat-adapter/custom.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kevisual/ai",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "AI Center Services",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"basename": "/root/ai-center-services",
|
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
"src",
|
|
14
14
|
"types"
|
|
15
15
|
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "npm run clean && bun bun.config.mjs",
|
|
18
|
+
"dev": "bun run --watch bun.config.mjs",
|
|
19
|
+
"test": "tsx test/**/*.ts",
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"pub": "envision pack -p -u"
|
|
22
|
+
},
|
|
16
23
|
"keywords": [
|
|
17
24
|
"kevisual",
|
|
18
25
|
"ai",
|
|
@@ -20,6 +27,7 @@
|
|
|
20
27
|
],
|
|
21
28
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
|
22
29
|
"license": "MIT",
|
|
30
|
+
"packageManager": "pnpm@10.28.0",
|
|
23
31
|
"type": "module",
|
|
24
32
|
"publishConfig": {
|
|
25
33
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -45,34 +53,27 @@
|
|
|
45
53
|
}
|
|
46
54
|
},
|
|
47
55
|
"devDependencies": {
|
|
48
|
-
"@kevisual/router": "0.0.
|
|
56
|
+
"@kevisual/router": "0.0.52",
|
|
49
57
|
"@kevisual/types": "^0.0.10",
|
|
50
58
|
"@kevisual/use-config": "^1.0.21",
|
|
51
|
-
"@types/bun": "^1.3.
|
|
59
|
+
"@types/bun": "^1.3.5",
|
|
52
60
|
"@types/crypto-js": "^4.2.2",
|
|
53
61
|
"@types/formidable": "^3.4.6",
|
|
54
|
-
"@types/node": "^
|
|
62
|
+
"@types/node": "^25.0.5",
|
|
55
63
|
"cross-env": "^10.1.0",
|
|
56
64
|
"crypto-js": "^4.2.0",
|
|
57
65
|
"dayjs": "^1.11.19",
|
|
58
66
|
"dotenv": "^17.2.3",
|
|
59
67
|
"formidable": "^3.5.4",
|
|
60
|
-
"openai": "6.
|
|
68
|
+
"openai": "6.16.0",
|
|
61
69
|
"pm2": "^6.0.14",
|
|
62
70
|
"rimraf": "^6.1.2",
|
|
63
71
|
"typescript": "^5.9.3",
|
|
64
|
-
"vite": "^7.
|
|
72
|
+
"vite": "^7.3.1"
|
|
65
73
|
},
|
|
66
74
|
"dependencies": {
|
|
67
75
|
"@kevisual/logger": "^0.0.4",
|
|
68
76
|
"@kevisual/permission": "^0.0.3",
|
|
69
|
-
"@kevisual/query": "^0.0.
|
|
70
|
-
},
|
|
71
|
-
"scripts": {
|
|
72
|
-
"build": "npm run clean && bun bun.config.mjs",
|
|
73
|
-
"dev": "bun run --watch bun.config.mjs",
|
|
74
|
-
"test": "tsx test/**/*.ts",
|
|
75
|
-
"clean": "rm -rf dist",
|
|
76
|
-
"pub": "envision pack -p -u"
|
|
77
|
+
"@kevisual/query": "^0.0.35"
|
|
77
78
|
}
|
|
78
79
|
}
|
package/src/jimeng/core.ts
CHANGED
|
@@ -1,51 +1,121 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { Result } from '@kevisual/query'
|
|
2
|
+
export interface JimengOptions {
|
|
3
|
+
/** API密钥,用于认证请求 */
|
|
4
|
+
apiKey: string;
|
|
5
|
+
/** API基础URL */
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
/** 请求超时时间(毫秒) */
|
|
8
|
+
timeout: number;
|
|
5
9
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
|
|
11
|
+
export interface JimengGenerateOptions {
|
|
12
|
+
/** 图片生成提示词 */
|
|
13
|
+
prompt: string;
|
|
14
|
+
/** 使用的模型版本,默认 jimeng-4.0 */
|
|
15
|
+
model?: string;
|
|
16
|
+
/** 图片比例,默认 1:1 */
|
|
17
|
+
ratio?: string;
|
|
18
|
+
/** 图片分辨率,默认 2k */
|
|
19
|
+
resolution?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface JimengResponse {
|
|
23
|
+
/** 请求创建时间戳 */
|
|
24
|
+
created: number;
|
|
25
|
+
/** 生成的图片列表 */
|
|
26
|
+
data: Array<{
|
|
27
|
+
/** 图片URL */
|
|
28
|
+
url: string;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class JimengService {
|
|
33
|
+
private apiKey: string;
|
|
34
|
+
private baseUrl: string;
|
|
35
|
+
private timeout: number;
|
|
36
|
+
|
|
37
|
+
constructor(options: JimengOptions) {
|
|
38
|
+
this.apiKey = options.apiKey;
|
|
39
|
+
this.baseUrl = options.baseUrl || 'https://jimeng-api.kevisual.cn/v1';
|
|
40
|
+
this.timeout = options.timeout;
|
|
17
41
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
42
|
+
|
|
43
|
+
async generateImage(options: JimengGenerateOptions): Promise<Result<JimengResponse>> {
|
|
44
|
+
const {
|
|
45
|
+
prompt,
|
|
46
|
+
model = 'jimeng-4.6',
|
|
47
|
+
ratio = '1:1',
|
|
48
|
+
resolution = '2k'
|
|
49
|
+
} = options;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
54
|
+
|
|
55
|
+
const response = await fetch(`${this.baseUrl}/images/generations`, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: {
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
60
|
+
},
|
|
61
|
+
body: JSON.stringify({
|
|
62
|
+
model,
|
|
63
|
+
prompt,
|
|
64
|
+
ratio,
|
|
65
|
+
resolution,
|
|
66
|
+
}),
|
|
67
|
+
signal: controller.signal,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
clearTimeout(timeoutId);
|
|
71
|
+
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
throw new Error(`jimeng API error: ${response.status} ${response.statusText}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const result = await response.json() as JimengResponse;
|
|
77
|
+
return { code: 200, data: result };
|
|
78
|
+
} catch (error: any) {
|
|
79
|
+
return { code: 500, message: error.message || 'Unknown error' };
|
|
22
80
|
}
|
|
23
81
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
82
|
+
|
|
83
|
+
async downloadImage(url: string): Promise<Uint8Array> {
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const response = await fetch(url, {
|
|
89
|
+
signal: controller.signal,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
clearTimeout(timeoutId);
|
|
93
|
+
|
|
94
|
+
if (!response.ok) {
|
|
95
|
+
throw new Error(`Failed to download image: ${response.statusText}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
99
|
+
return new Uint8Array(arrayBuffer);
|
|
100
|
+
} catch (error: any) {
|
|
101
|
+
clearTimeout(timeoutId);
|
|
102
|
+
if (error.name === 'AbortError') {
|
|
103
|
+
throw new Error('Image download timeout');
|
|
33
104
|
}
|
|
34
|
-
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** 获取图片过期时间 */
|
|
109
|
+
async getExpiredTime(url: string): Promise<{ expiredAt: number, expired: boolean }> {
|
|
110
|
+
// https://p3-dreamina-sign.byteimg.com/tos-cn-i-tb4s082cfz/c018e06ee6654dd78ccacb29eff4744e~tplv-tb4s082cfz-aigc_resize:0:0.png?lk3s=43402efa&x-expires=1767852000&x-signature=34yf37N955BP37eLaYEzKeLQn0Q%3D&format=.png
|
|
111
|
+
const urlObj = new URL(url);
|
|
112
|
+
let expires = urlObj.searchParams.get('x-expires');
|
|
113
|
+
if (!expires) {
|
|
114
|
+
expires = '0';
|
|
115
|
+
}
|
|
116
|
+
const expiredAt = parseInt(expires) * 1000;
|
|
117
|
+
const expired = Date.now() > expiredAt;
|
|
118
|
+
return { expiredAt, expired };
|
|
35
119
|
}
|
|
36
120
|
}
|
|
37
121
|
|
|
38
|
-
export type ImageOptions = {
|
|
39
|
-
model?: string;
|
|
40
|
-
prompt: string;
|
|
41
|
-
/**
|
|
42
|
-
* 宽高比,如 "16:9", "4:3", "1:1" 等
|
|
43
|
-
*/
|
|
44
|
-
ratio?: string;
|
|
45
|
-
/**
|
|
46
|
-
*
|
|
47
|
-
* 图片分辨率,如 "1024x768", "512x512" 等
|
|
48
|
-
* 4k 2k
|
|
49
|
-
*/
|
|
50
|
-
resolution?: string;
|
|
51
|
-
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseChat, type BaseChatOptions } from "../chat.ts";
|
|
2
|
+
|
|
3
|
+
type MimoOptions = Partial<BaseChatOptions>;
|
|
4
|
+
export class MimoChat extends BaseChat {
|
|
5
|
+
static BASE_URL = 'https://api.xiaomimimo.com/v1';
|
|
6
|
+
constructor(options: MimoOptions) {
|
|
7
|
+
const baseURL = options.baseURL || MimoChat.BASE_URL;
|
|
8
|
+
super({ ...(options as BaseChatOptions), baseURL: baseURL });
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -25,11 +25,6 @@ export class Ollama extends BaseChat {
|
|
|
25
25
|
const baseURL = options.baseURL || Ollama.BASE_URL;
|
|
26
26
|
super({ ...(options as BaseChatOptions), baseURL: baseURL });
|
|
27
27
|
}
|
|
28
|
-
async chat(messages: ChatMessage[], options?: ChatMessageOptions) {
|
|
29
|
-
const res = await super.chat(messages, options);
|
|
30
|
-
console.log('thunk', this.getChatUsage());
|
|
31
|
-
return res;
|
|
32
|
-
}
|
|
33
28
|
/**
|
|
34
29
|
* 获取模型列表
|
|
35
30
|
* @returns
|
|
@@ -32,8 +32,4 @@ export class SiliconFlow extends BaseChat {
|
|
|
32
32
|
async getUsageInfo(): Promise<SiliconFlowUsageResponse> {
|
|
33
33
|
return this.get('/user/info');
|
|
34
34
|
}
|
|
35
|
-
async chat(messages: ChatMessage[], options?: ChatMessageOptions) {
|
|
36
|
-
const res = await super.chat(messages, options);
|
|
37
|
-
return res;
|
|
38
|
-
}
|
|
39
35
|
}
|
|
@@ -50,10 +50,10 @@ export class BaseChat implements BaseChatInterface, Usage {
|
|
|
50
50
|
* 默认apiKey
|
|
51
51
|
*/
|
|
52
52
|
apiKey: string;
|
|
53
|
-
prompt_tokens: number;
|
|
54
|
-
total_tokens: number;
|
|
55
|
-
completion_tokens: number;
|
|
56
|
-
responseText: string;
|
|
53
|
+
prompt_tokens: number = 0;
|
|
54
|
+
total_tokens: number = 0;
|
|
55
|
+
completion_tokens: number = 0;
|
|
56
|
+
responseText: string = '';
|
|
57
57
|
utils = AIUtils;
|
|
58
58
|
constructor(options: BaseChatOptions) {
|
|
59
59
|
this.baseURL = options.baseURL;
|
|
@@ -88,11 +88,17 @@ export class BaseChat implements BaseChatInterface, Usage {
|
|
|
88
88
|
/**
|
|
89
89
|
* 聊天
|
|
90
90
|
*/
|
|
91
|
-
|
|
91
|
+
chat(options: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
92
|
+
chat(messages: ChatMessage[], options?: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
93
|
+
async chat(messagesOrOptions: ChatMessage[] | ChatMessageOptions, options?: ChatMessageOptions): Promise<ChatMessageComplete> {
|
|
94
|
+
const isFirstParamOptions = !Array.isArray(messagesOrOptions);
|
|
95
|
+
const messages: ChatMessage[] = isFirstParamOptions ? messagesOrOptions.messages! : messagesOrOptions;
|
|
96
|
+
const opts: ChatMessageOptions = isFirstParamOptions ? messagesOrOptions : options || {};
|
|
97
|
+
|
|
92
98
|
const requestBody = {
|
|
93
99
|
model: this.model,
|
|
94
100
|
messages,
|
|
95
|
-
...
|
|
101
|
+
...opts,
|
|
96
102
|
stream: false,
|
|
97
103
|
};
|
|
98
104
|
|
|
@@ -105,21 +111,26 @@ export class BaseChat implements BaseChatInterface, Usage {
|
|
|
105
111
|
|
|
106
112
|
const res = await response.json() as ChatMessageComplete;
|
|
107
113
|
|
|
108
|
-
this.
|
|
109
|
-
|
|
110
|
-
this.completion_tokens = res.usage?.completion_tokens ?? 0;
|
|
114
|
+
this.setChatUsage(res.usage);
|
|
115
|
+
|
|
111
116
|
this.responseText = res.choices[0]?.message?.content || '';
|
|
112
117
|
return res;
|
|
113
118
|
}
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
chatStream(options: ChatMessageOptions): AsyncGenerator<ChatMessageComplete>;
|
|
120
|
+
chatStream(messages: ChatMessage[], options?: ChatMessageOptions): AsyncGenerator<ChatMessageComplete>;
|
|
121
|
+
async *chatStream(messagesOrOptions: ChatMessage[] | ChatMessageOptions, options?: ChatMessageOptions) {
|
|
122
|
+
const isFirstParamOptions = !Array.isArray(messagesOrOptions);
|
|
123
|
+
const messages: ChatMessage[] = isFirstParamOptions ? messagesOrOptions.messages! : messagesOrOptions;
|
|
124
|
+
const opts: ChatMessageOptions = isFirstParamOptions ? messagesOrOptions : options || {};
|
|
125
|
+
|
|
126
|
+
if (opts.response_format) {
|
|
116
127
|
throw new Error('response_format is not supported in stream mode');
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
const requestBody = {
|
|
120
131
|
model: this.model,
|
|
121
132
|
messages,
|
|
122
|
-
...
|
|
133
|
+
...opts,
|
|
123
134
|
stream: true,
|
|
124
135
|
};
|
|
125
136
|
|
|
@@ -191,6 +202,11 @@ export class BaseChat implements BaseChatInterface, Usage {
|
|
|
191
202
|
completion_tokens: this.completion_tokens,
|
|
192
203
|
};
|
|
193
204
|
}
|
|
205
|
+
setChatUsage(usage: { prompt_tokens?: number; total_tokens?: number; completion_tokens?: number }) {
|
|
206
|
+
this.prompt_tokens = usage.prompt_tokens ?? this.prompt_tokens;
|
|
207
|
+
this.total_tokens = usage.total_tokens ?? this.total_tokens;
|
|
208
|
+
this.completion_tokens = usage.completion_tokens ?? this.completion_tokens;
|
|
209
|
+
}
|
|
194
210
|
|
|
195
211
|
getHeaders(headers?: Record<string, string>) {
|
|
196
212
|
return {
|
|
@@ -2,8 +2,8 @@ export type ChatMessage = {
|
|
|
2
2
|
role?: 'user' | 'assistant' | 'system' | 'tool';
|
|
3
3
|
content: string;
|
|
4
4
|
}
|
|
5
|
-
export type ChatMessageOptions = {
|
|
6
|
-
messages
|
|
5
|
+
export type ChatMessageOptions<T = {}> = {
|
|
6
|
+
messages?: ChatMessage[];
|
|
7
7
|
/**
|
|
8
8
|
* 模型名称
|
|
9
9
|
*/
|
|
@@ -43,7 +43,7 @@ export type ChatMessageOptions = {
|
|
|
43
43
|
stream?: boolean;
|
|
44
44
|
/**
|
|
45
45
|
* 是否能够思考
|
|
46
|
-
* 如果会话是千文,服务器的接口,默认为
|
|
46
|
+
* 如果会话是千文,服务器的接口,默认为 false
|
|
47
47
|
*/
|
|
48
48
|
enable_thinking?: boolean;
|
|
49
49
|
response_format?: 'text' | 'json' | 'xml' | 'html';
|
|
@@ -53,7 +53,9 @@ export type ChatMessageOptions = {
|
|
|
53
53
|
*/
|
|
54
54
|
tool_calls?: any;
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
[key: string]: any;
|
|
57
|
+
|
|
58
|
+
} & T;
|
|
57
59
|
|
|
58
60
|
type Choice = {
|
|
59
61
|
finish_reason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | 'function_call';
|
|
@@ -167,6 +169,7 @@ export type EmbeddingMessageComplete = {
|
|
|
167
169
|
|
|
168
170
|
|
|
169
171
|
export interface BaseChatInterface {
|
|
172
|
+
chat(options: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
170
173
|
chat(messages: ChatMessage[], options?: ChatMessageOptions): Promise<ChatMessageComplete>;
|
|
171
174
|
}
|
|
172
175
|
|