@opentiny/tiny-robot-kit 0.4.0-alpha.4 → 0.4.0-alpha.6
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/index.d.mts +357 -46
- package/dist/index.d.ts +357 -46
- package/dist/index.js +4 -2
- package/dist/index.mjs +4 -2
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,27 +1,57 @@
|
|
|
1
1
|
import { Ref, ComputedRef } from 'vue';
|
|
2
2
|
|
|
3
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
|
-
*
|
|
7
|
-
* 提供一些实用的辅助函数
|
|
4
|
+
* 模型Provider基类
|
|
8
5
|
*/
|
|
6
|
+
declare abstract class BaseModelProvider {
|
|
7
|
+
protected config: AIModelConfig;
|
|
8
|
+
/**
|
|
9
|
+
* @param config AI模型配置
|
|
10
|
+
*/
|
|
11
|
+
constructor(config: AIModelConfig);
|
|
12
|
+
/**
|
|
13
|
+
* 发送聊天请求并获取响应
|
|
14
|
+
* @param request 聊天请求参数
|
|
15
|
+
* @returns 聊天响应
|
|
16
|
+
*/
|
|
17
|
+
abstract chat(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
18
|
+
/**
|
|
19
|
+
* 发送流式聊天请求并通过处理器处理响应
|
|
20
|
+
* @param request 聊天请求参数
|
|
21
|
+
* @param handler 流式响应处理器
|
|
22
|
+
*/
|
|
23
|
+
abstract chatStream(request: ChatCompletionRequest, handler: StreamHandler): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* 更新配置
|
|
26
|
+
* @param config 新的AI模型配置
|
|
27
|
+
*/
|
|
28
|
+
updateConfig(config: AIModelConfig): void;
|
|
29
|
+
/**
|
|
30
|
+
* 获取当前配置
|
|
31
|
+
* @returns AI模型配置
|
|
32
|
+
*/
|
|
33
|
+
getConfig(): AIModelConfig;
|
|
34
|
+
/**
|
|
35
|
+
* 验证请求参数
|
|
36
|
+
* @param request 聊天请求参数
|
|
37
|
+
*/
|
|
38
|
+
protected validateRequest(request: ChatCompletionRequest): void;
|
|
39
|
+
}
|
|
9
40
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
41
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
42
|
+
/**
|
|
43
|
+
* 消息角色类型
|
|
44
|
+
*/
|
|
45
|
+
type MessageRole = 'system' | 'user' | 'assistant';
|
|
46
|
+
interface ToolCall {
|
|
47
|
+
index: number;
|
|
48
|
+
id: string;
|
|
15
49
|
type: 'function';
|
|
16
50
|
function: {
|
|
17
51
|
name: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* function 的输入参数,以 JSON Schema 对象描述
|
|
21
|
-
*/
|
|
22
|
-
parameters: any;
|
|
52
|
+
arguments: string;
|
|
53
|
+
result?: string;
|
|
23
54
|
};
|
|
24
|
-
[key: string]: any;
|
|
25
55
|
}
|
|
26
56
|
interface MessageMetadata {
|
|
27
57
|
createdAt?: number;
|
|
@@ -30,6 +60,9 @@ interface MessageMetadata {
|
|
|
30
60
|
model?: string;
|
|
31
61
|
[key: string]: any;
|
|
32
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* 聊天消息接口
|
|
65
|
+
*/
|
|
33
66
|
interface ChatMessage {
|
|
34
67
|
role: string;
|
|
35
68
|
content: string;
|
|
@@ -40,22 +73,209 @@ interface ChatMessage {
|
|
|
40
73
|
[key: string]: any;
|
|
41
74
|
[key: symbol]: any;
|
|
42
75
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
76
|
+
/**
|
|
77
|
+
* 聊天历史记录
|
|
78
|
+
*/
|
|
79
|
+
type ChatHistory = ChatMessage[];
|
|
80
|
+
/**
|
|
81
|
+
* 聊天完成请求选项
|
|
82
|
+
*/
|
|
83
|
+
interface ChatCompletionOptions {
|
|
84
|
+
model?: string;
|
|
85
|
+
temperature?: number;
|
|
86
|
+
top_p?: number;
|
|
87
|
+
n?: number;
|
|
88
|
+
stream?: boolean;
|
|
89
|
+
max_tokens?: number;
|
|
90
|
+
signal?: AbortSignal;
|
|
46
91
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
92
|
+
/**
|
|
93
|
+
* 聊天完成请求参数
|
|
94
|
+
*/
|
|
95
|
+
interface ChatCompletionRequest {
|
|
96
|
+
messages: ChatMessage[];
|
|
97
|
+
options?: ChatCompletionOptions;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 聊天完成响应消息
|
|
101
|
+
*/
|
|
102
|
+
interface ChatCompletionResponseMessage {
|
|
103
|
+
role: MessageRole;
|
|
104
|
+
content: string;
|
|
105
|
+
[x: string]: unknown;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 聊天完成响应选择
|
|
109
|
+
*/
|
|
110
|
+
interface ChatCompletionResponseChoice {
|
|
50
111
|
index: number;
|
|
112
|
+
message: ChatCompletionResponseMessage;
|
|
113
|
+
finish_reason: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 聊天完成响应使用情况
|
|
117
|
+
*/
|
|
118
|
+
interface ChatCompletionResponseUsage {
|
|
119
|
+
prompt_tokens: number;
|
|
120
|
+
completion_tokens: number;
|
|
121
|
+
total_tokens: number;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 聊天完成响应
|
|
125
|
+
*/
|
|
126
|
+
interface ChatCompletionResponse {
|
|
127
|
+
id: string;
|
|
128
|
+
object: string;
|
|
129
|
+
created: number;
|
|
130
|
+
model: string;
|
|
131
|
+
choices: ChatCompletionResponseChoice[];
|
|
132
|
+
usage: ChatCompletionResponseUsage;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 流式聊天完成响应增量
|
|
136
|
+
*/
|
|
137
|
+
interface ChatCompletionStreamResponseDelta {
|
|
138
|
+
content?: string;
|
|
139
|
+
role?: MessageRole;
|
|
140
|
+
[x: string]: unknown;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 流式聊天完成响应选择
|
|
144
|
+
*/
|
|
145
|
+
interface ChatCompletionStreamResponseChoice {
|
|
146
|
+
index: number;
|
|
147
|
+
delta: ChatCompletionStreamResponseDelta;
|
|
148
|
+
finish_reason: string | null;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* 流式聊天完成响应
|
|
152
|
+
*/
|
|
153
|
+
interface ChatCompletionStreamResponse {
|
|
51
154
|
id: string;
|
|
155
|
+
object: string;
|
|
156
|
+
created: number;
|
|
157
|
+
model: string;
|
|
158
|
+
choices: ChatCompletionStreamResponseChoice[];
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* AI模型提供商类型
|
|
162
|
+
*/
|
|
163
|
+
type AIProvider = 'openai' | 'deepseek' | 'custom';
|
|
164
|
+
/**
|
|
165
|
+
* AI模型配置接口
|
|
166
|
+
*/
|
|
167
|
+
interface AIModelConfig {
|
|
168
|
+
provider: AIProvider;
|
|
169
|
+
providerImplementation?: BaseModelProvider;
|
|
170
|
+
apiKey?: string;
|
|
171
|
+
apiUrl?: string;
|
|
172
|
+
apiVersion?: string;
|
|
173
|
+
defaultModel?: string;
|
|
174
|
+
defaultOptions?: ChatCompletionOptions;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 错误类型
|
|
178
|
+
*/
|
|
179
|
+
declare enum ErrorType {
|
|
180
|
+
NETWORK_ERROR = "network_error",
|
|
181
|
+
AUTHENTICATION_ERROR = "authentication_error",
|
|
182
|
+
RATE_LIMIT_ERROR = "rate_limit_error",
|
|
183
|
+
SERVER_ERROR = "server_error",
|
|
184
|
+
MODEL_ERROR = "model_error",
|
|
185
|
+
TIMEOUT_ERROR = "timeout_error",
|
|
186
|
+
UNKNOWN_ERROR = "unknown_error"
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* AI适配器错误
|
|
190
|
+
*/
|
|
191
|
+
interface AIAdapterError {
|
|
192
|
+
type: ErrorType;
|
|
193
|
+
message: string;
|
|
194
|
+
statusCode?: number;
|
|
195
|
+
originalError?: object;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 流式响应事件类型
|
|
199
|
+
*/
|
|
200
|
+
declare enum StreamEventType {
|
|
201
|
+
DATA = "data",
|
|
202
|
+
ERROR = "error",
|
|
203
|
+
DONE = "done"
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 流式响应处理器
|
|
207
|
+
*/
|
|
208
|
+
interface StreamHandler {
|
|
209
|
+
onData: (data: ChatCompletionStreamResponse) => void;
|
|
210
|
+
onError: (error: AIAdapterError) => void;
|
|
211
|
+
onDone: (finishReason?: string) => void;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* AI客户端类
|
|
216
|
+
* 负责根据配置选择合适的提供商并处理请求
|
|
217
|
+
*/
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @deprecated
|
|
221
|
+
* AI客户端类
|
|
222
|
+
*/
|
|
223
|
+
declare class AIClient {
|
|
224
|
+
private provider;
|
|
225
|
+
private config;
|
|
226
|
+
/**
|
|
227
|
+
* 构造函数
|
|
228
|
+
* @param config AI模型配置
|
|
229
|
+
*/
|
|
230
|
+
constructor(config: AIModelConfig);
|
|
231
|
+
/**
|
|
232
|
+
* 创建提供商实例
|
|
233
|
+
* @param config AI模型配置
|
|
234
|
+
* @returns 提供商实例
|
|
235
|
+
*/
|
|
236
|
+
private createProvider;
|
|
237
|
+
/**
|
|
238
|
+
* 发送聊天请求并获取响应
|
|
239
|
+
* @param request 聊天请求参数
|
|
240
|
+
* @returns 聊天响应
|
|
241
|
+
*/
|
|
242
|
+
chat(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
243
|
+
/**
|
|
244
|
+
* 发送流式聊天请求并通过处理器处理响应
|
|
245
|
+
* @param request 聊天请求参数
|
|
246
|
+
* @param handler 流式响应处理器
|
|
247
|
+
*/
|
|
248
|
+
chatStream(request: ChatCompletionRequest, handler: StreamHandler): Promise<void>;
|
|
249
|
+
/**
|
|
250
|
+
* 获取当前配置
|
|
251
|
+
* @returns AI模型配置
|
|
252
|
+
*/
|
|
253
|
+
getConfig(): AIModelConfig;
|
|
254
|
+
/**
|
|
255
|
+
* 更新配置
|
|
256
|
+
* @param config 新的AI模型配置
|
|
257
|
+
*/
|
|
258
|
+
updateConfig(config: Partial<AIModelConfig>): void;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
interface Tool {
|
|
52
262
|
type: 'function';
|
|
53
263
|
function: {
|
|
54
264
|
name: string;
|
|
55
|
-
|
|
56
|
-
|
|
265
|
+
description: string;
|
|
266
|
+
/**
|
|
267
|
+
* function 的输入参数,以 JSON Schema 对象描述
|
|
268
|
+
*/
|
|
269
|
+
parameters: any;
|
|
57
270
|
};
|
|
271
|
+
[key: string]: any;
|
|
272
|
+
}
|
|
273
|
+
interface MessageRequestBody {
|
|
274
|
+
messages: Partial<ChatMessage>[];
|
|
275
|
+
[key: string]: any;
|
|
58
276
|
}
|
|
277
|
+
type RequestState = 'idle' | 'processing' | 'completed' | 'aborted' | 'error';
|
|
278
|
+
type RequestProcessingState = 'requesting' | 'completing' | string;
|
|
59
279
|
interface Usage {
|
|
60
280
|
prompt_tokens: number;
|
|
61
281
|
completion_tokens: number;
|
|
@@ -226,28 +446,6 @@ interface Conversation extends ConversationInfo {
|
|
|
226
446
|
*/
|
|
227
447
|
engine: UseMessageReturn;
|
|
228
448
|
}
|
|
229
|
-
interface ConversationStorageStrategy {
|
|
230
|
-
/**
|
|
231
|
-
* Load all conversations (id and title only).
|
|
232
|
-
*/
|
|
233
|
-
loadConversations?: () => MaybePromise<ConversationInfo[]>;
|
|
234
|
-
/**
|
|
235
|
-
* Load all messages for a given conversation.
|
|
236
|
-
*/
|
|
237
|
-
loadMessages?: (conversationId: string) => MaybePromise<ChatMessage[]>;
|
|
238
|
-
/**
|
|
239
|
-
* Persist conversation metadata (create or update).
|
|
240
|
-
*/
|
|
241
|
-
saveConversation?: (conversation: ConversationInfo) => MaybePromise<void>;
|
|
242
|
-
/**
|
|
243
|
-
* Persist messages for a given conversation.
|
|
244
|
-
*/
|
|
245
|
-
saveMessages?: (conversationId: string, messages: ChatMessage[]) => MaybePromise<void>;
|
|
246
|
-
/**
|
|
247
|
-
* Optional method to delete a conversation and its messages.
|
|
248
|
-
*/
|
|
249
|
-
deleteConversation?: (conversationId: string) => MaybePromise<void>;
|
|
250
|
-
}
|
|
251
449
|
interface UseConversationOptions {
|
|
252
450
|
/**
|
|
253
451
|
* Base useMessage options for all conversations.
|
|
@@ -288,12 +486,125 @@ interface UseConversationReturn {
|
|
|
288
486
|
}) => Conversation;
|
|
289
487
|
switchConversation: (id: string) => Promise<Conversation | null>;
|
|
290
488
|
deleteConversation: (id: string) => Promise<void>;
|
|
489
|
+
clear: () => void;
|
|
291
490
|
updateConversationTitle: (id: string, title?: string) => void;
|
|
292
491
|
saveMessages: (id?: string) => void;
|
|
293
492
|
sendMessage: (content: string) => void;
|
|
294
493
|
abortActiveRequest: () => Promise<void>;
|
|
295
494
|
}
|
|
296
495
|
|
|
496
|
+
/**
|
|
497
|
+
* 存储策略接口
|
|
498
|
+
*/
|
|
499
|
+
interface ConversationStorageStrategy {
|
|
500
|
+
/**
|
|
501
|
+
* Load all conversations (id and title only).
|
|
502
|
+
*/
|
|
503
|
+
loadConversations: () => MaybePromise<ConversationInfo[]>;
|
|
504
|
+
/**
|
|
505
|
+
* Load all messages for a given conversation.
|
|
506
|
+
*/
|
|
507
|
+
loadMessages: (conversationId: string) => MaybePromise<ChatMessage[]>;
|
|
508
|
+
/**
|
|
509
|
+
* Persist conversation metadata (create or update).
|
|
510
|
+
*/
|
|
511
|
+
saveConversation: (conversation: ConversationInfo) => MaybePromise<void>;
|
|
512
|
+
/**
|
|
513
|
+
* Persist messages for a given conversation.
|
|
514
|
+
*/
|
|
515
|
+
saveMessages: (conversationId: string, messages: ChatMessage[]) => MaybePromise<void>;
|
|
516
|
+
/**
|
|
517
|
+
* Optional method to delete a conversation and its messages.
|
|
518
|
+
*/
|
|
519
|
+
deleteConversation?: (conversationId: string) => MaybePromise<void>;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* 本地存储策略
|
|
524
|
+
*/
|
|
525
|
+
declare class LocalStorageStrategy implements ConversationStorageStrategy {
|
|
526
|
+
private storageKey;
|
|
527
|
+
constructor(storageKey?: string);
|
|
528
|
+
saveConversation(conversation: ConversationInfo): void;
|
|
529
|
+
loadConversations(): ConversationInfo[];
|
|
530
|
+
saveMessages(conversationId: string, messages: ChatMessage[]): void;
|
|
531
|
+
loadMessages(conversationId: string): ChatMessage[];
|
|
532
|
+
deleteConversation(conversationId: string): void;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* IndexedDB 存储策略
|
|
537
|
+
*/
|
|
538
|
+
declare class IndexedDBStrategy implements ConversationStorageStrategy {
|
|
539
|
+
private dbName;
|
|
540
|
+
private dbVersion;
|
|
541
|
+
private db;
|
|
542
|
+
constructor(dbName?: string, dbVersion?: number);
|
|
543
|
+
/**
|
|
544
|
+
* 获取或初始化数据库连接
|
|
545
|
+
*/
|
|
546
|
+
private getDB;
|
|
547
|
+
/**
|
|
548
|
+
* 加载所有会话(只包含元数据)
|
|
549
|
+
*/
|
|
550
|
+
loadConversations(): Promise<ConversationInfo[]>;
|
|
551
|
+
/**
|
|
552
|
+
* 加载指定会话的所有消息
|
|
553
|
+
*/
|
|
554
|
+
loadMessages(conversationId: string): Promise<ChatMessage[]>;
|
|
555
|
+
/**
|
|
556
|
+
* 保存或更新会话元数据
|
|
557
|
+
*/
|
|
558
|
+
saveConversation(conversation: ConversationInfo): Promise<void>;
|
|
559
|
+
/**
|
|
560
|
+
* 保存指定会话的消息
|
|
561
|
+
*/
|
|
562
|
+
saveMessages(conversationId: string, messages: ChatMessage[]): Promise<void>;
|
|
563
|
+
/**
|
|
564
|
+
* 删除会话及其所有消息
|
|
565
|
+
*/
|
|
566
|
+
deleteConversation(conversationId: string): Promise<void>;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
interface LocalStorageConfig {
|
|
570
|
+
/** 存储键名 (default: 'tiny-robot-ai-conversations') */
|
|
571
|
+
key?: string;
|
|
572
|
+
}
|
|
573
|
+
interface IndexedDBConfig {
|
|
574
|
+
/** 数据库名称 (default: 'tiny-robot-ai-db') */
|
|
575
|
+
dbName?: string;
|
|
576
|
+
/** 数据库版本 (default: 1) */
|
|
577
|
+
dbVersion?: number;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* LocalStorage 策略工厂函数
|
|
581
|
+
*/
|
|
582
|
+
declare function localStorageStrategyFactory(config?: LocalStorageConfig): ConversationStorageStrategy;
|
|
583
|
+
/**
|
|
584
|
+
* IndexedDB 策略工厂函数
|
|
585
|
+
*/
|
|
586
|
+
declare function indexedDBStorageStrategyFactory(config?: IndexedDBConfig): ConversationStorageStrategy;
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* 工具函数模块
|
|
590
|
+
* 提供一些实用的辅助函数
|
|
591
|
+
*/
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* 将 SSE 流转换为异步生成器。
|
|
595
|
+
* 将服务器发送事件(SSE)流式响应转换为异步生成器,逐个产出解析后的数据
|
|
596
|
+
*
|
|
597
|
+
* 当取消信号被触发时,会抛出 name 为 'AbortError' 的错误
|
|
598
|
+
* @param response fetch 响应对象
|
|
599
|
+
* @param options 配置选项
|
|
600
|
+
* @param options.signal 可选的取消信号,用于中断流处理
|
|
601
|
+
* @returns 异步生成器,产出类型为 T 的数据
|
|
602
|
+
* @template T 生成器产出的数据类型,默认为 any
|
|
603
|
+
*/
|
|
604
|
+
declare function sseStreamToGenerator<T = any>(response: Response, options?: {
|
|
605
|
+
signal?: AbortSignal;
|
|
606
|
+
}): AsyncGenerator<T, void, unknown>;
|
|
607
|
+
|
|
297
608
|
declare const useConversation: (options: UseConversationOptions) => UseConversationReturn;
|
|
298
609
|
|
|
299
610
|
declare const fallbackRolePlugin: (options?: UseMessagePlugin & {
|
|
@@ -377,4 +688,4 @@ declare const toolPlugin: (options: UseMessagePlugin & {
|
|
|
377
688
|
|
|
378
689
|
declare const useMessage: (options: UseMessageOptions) => UseMessageReturn;
|
|
379
690
|
|
|
380
|
-
export { type BasePluginContext, type ChatCompletion, type ChatMessage, type Choice, type CompletionChoice, type Conversation, type ConversationInfo, type ConversationStorageStrategy, type DeltaChoice, EXCLUDE_MODE_REMOVE, type MessageMetadata, type MessageRequestBody, type RequestProcessingState, type RequestState, type Tool, type ToolCall, type Usage, type UseConversationOptions, type UseConversationReturn, type UseMessageOptions, type UseMessagePlugin, type UseMessageReturn,
|
|
691
|
+
export { type AIAdapterError, AIClient, type AIModelConfig, type AIProvider, type BasePluginContext, type ChatCompletion, type ChatCompletionOptions, type ChatCompletionRequest, type ChatCompletionResponse, type ChatCompletionResponseChoice, type ChatCompletionResponseMessage, type ChatCompletionResponseUsage, type ChatCompletionStreamResponse, type ChatCompletionStreamResponseChoice, type ChatCompletionStreamResponseDelta, type ChatHistory, type ChatMessage, type Choice, type CompletionChoice, type Conversation, type ConversationInfo, type ConversationStorageStrategy, type DeltaChoice, EXCLUDE_MODE_REMOVE, ErrorType, type IndexedDBConfig, IndexedDBStrategy, type LocalStorageConfig, LocalStorageStrategy, type MaybePromise, type MessageMetadata, type MessageRequestBody, type MessageRole, type RequestProcessingState, type RequestState, StreamEventType, type StreamHandler, type Tool, type ToolCall, type Usage, type UseConversationOptions, type UseConversationReturn, type UseMessageOptions, type UseMessagePlugin, type UseMessageReturn, fallbackRolePlugin, indexedDBStorageStrategyFactory, lengthPlugin, localStorageStrategyFactory, sseStreamToGenerator, thinkingPlugin, toolPlugin, useConversation, useMessage };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,27 +1,57 @@
|
|
|
1
1
|
import { Ref, ComputedRef } from 'vue';
|
|
2
2
|
|
|
3
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
|
-
*
|
|
7
|
-
* 提供一些实用的辅助函数
|
|
4
|
+
* 模型Provider基类
|
|
8
5
|
*/
|
|
6
|
+
declare abstract class BaseModelProvider {
|
|
7
|
+
protected config: AIModelConfig;
|
|
8
|
+
/**
|
|
9
|
+
* @param config AI模型配置
|
|
10
|
+
*/
|
|
11
|
+
constructor(config: AIModelConfig);
|
|
12
|
+
/**
|
|
13
|
+
* 发送聊天请求并获取响应
|
|
14
|
+
* @param request 聊天请求参数
|
|
15
|
+
* @returns 聊天响应
|
|
16
|
+
*/
|
|
17
|
+
abstract chat(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
18
|
+
/**
|
|
19
|
+
* 发送流式聊天请求并通过处理器处理响应
|
|
20
|
+
* @param request 聊天请求参数
|
|
21
|
+
* @param handler 流式响应处理器
|
|
22
|
+
*/
|
|
23
|
+
abstract chatStream(request: ChatCompletionRequest, handler: StreamHandler): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* 更新配置
|
|
26
|
+
* @param config 新的AI模型配置
|
|
27
|
+
*/
|
|
28
|
+
updateConfig(config: AIModelConfig): void;
|
|
29
|
+
/**
|
|
30
|
+
* 获取当前配置
|
|
31
|
+
* @returns AI模型配置
|
|
32
|
+
*/
|
|
33
|
+
getConfig(): AIModelConfig;
|
|
34
|
+
/**
|
|
35
|
+
* 验证请求参数
|
|
36
|
+
* @param request 聊天请求参数
|
|
37
|
+
*/
|
|
38
|
+
protected validateRequest(request: ChatCompletionRequest): void;
|
|
39
|
+
}
|
|
9
40
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
41
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
42
|
+
/**
|
|
43
|
+
* 消息角色类型
|
|
44
|
+
*/
|
|
45
|
+
type MessageRole = 'system' | 'user' | 'assistant';
|
|
46
|
+
interface ToolCall {
|
|
47
|
+
index: number;
|
|
48
|
+
id: string;
|
|
15
49
|
type: 'function';
|
|
16
50
|
function: {
|
|
17
51
|
name: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* function 的输入参数,以 JSON Schema 对象描述
|
|
21
|
-
*/
|
|
22
|
-
parameters: any;
|
|
52
|
+
arguments: string;
|
|
53
|
+
result?: string;
|
|
23
54
|
};
|
|
24
|
-
[key: string]: any;
|
|
25
55
|
}
|
|
26
56
|
interface MessageMetadata {
|
|
27
57
|
createdAt?: number;
|
|
@@ -30,6 +60,9 @@ interface MessageMetadata {
|
|
|
30
60
|
model?: string;
|
|
31
61
|
[key: string]: any;
|
|
32
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* 聊天消息接口
|
|
65
|
+
*/
|
|
33
66
|
interface ChatMessage {
|
|
34
67
|
role: string;
|
|
35
68
|
content: string;
|
|
@@ -40,22 +73,209 @@ interface ChatMessage {
|
|
|
40
73
|
[key: string]: any;
|
|
41
74
|
[key: symbol]: any;
|
|
42
75
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
76
|
+
/**
|
|
77
|
+
* 聊天历史记录
|
|
78
|
+
*/
|
|
79
|
+
type ChatHistory = ChatMessage[];
|
|
80
|
+
/**
|
|
81
|
+
* 聊天完成请求选项
|
|
82
|
+
*/
|
|
83
|
+
interface ChatCompletionOptions {
|
|
84
|
+
model?: string;
|
|
85
|
+
temperature?: number;
|
|
86
|
+
top_p?: number;
|
|
87
|
+
n?: number;
|
|
88
|
+
stream?: boolean;
|
|
89
|
+
max_tokens?: number;
|
|
90
|
+
signal?: AbortSignal;
|
|
46
91
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
92
|
+
/**
|
|
93
|
+
* 聊天完成请求参数
|
|
94
|
+
*/
|
|
95
|
+
interface ChatCompletionRequest {
|
|
96
|
+
messages: ChatMessage[];
|
|
97
|
+
options?: ChatCompletionOptions;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 聊天完成响应消息
|
|
101
|
+
*/
|
|
102
|
+
interface ChatCompletionResponseMessage {
|
|
103
|
+
role: MessageRole;
|
|
104
|
+
content: string;
|
|
105
|
+
[x: string]: unknown;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 聊天完成响应选择
|
|
109
|
+
*/
|
|
110
|
+
interface ChatCompletionResponseChoice {
|
|
50
111
|
index: number;
|
|
112
|
+
message: ChatCompletionResponseMessage;
|
|
113
|
+
finish_reason: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 聊天完成响应使用情况
|
|
117
|
+
*/
|
|
118
|
+
interface ChatCompletionResponseUsage {
|
|
119
|
+
prompt_tokens: number;
|
|
120
|
+
completion_tokens: number;
|
|
121
|
+
total_tokens: number;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 聊天完成响应
|
|
125
|
+
*/
|
|
126
|
+
interface ChatCompletionResponse {
|
|
127
|
+
id: string;
|
|
128
|
+
object: string;
|
|
129
|
+
created: number;
|
|
130
|
+
model: string;
|
|
131
|
+
choices: ChatCompletionResponseChoice[];
|
|
132
|
+
usage: ChatCompletionResponseUsage;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 流式聊天完成响应增量
|
|
136
|
+
*/
|
|
137
|
+
interface ChatCompletionStreamResponseDelta {
|
|
138
|
+
content?: string;
|
|
139
|
+
role?: MessageRole;
|
|
140
|
+
[x: string]: unknown;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 流式聊天完成响应选择
|
|
144
|
+
*/
|
|
145
|
+
interface ChatCompletionStreamResponseChoice {
|
|
146
|
+
index: number;
|
|
147
|
+
delta: ChatCompletionStreamResponseDelta;
|
|
148
|
+
finish_reason: string | null;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* 流式聊天完成响应
|
|
152
|
+
*/
|
|
153
|
+
interface ChatCompletionStreamResponse {
|
|
51
154
|
id: string;
|
|
155
|
+
object: string;
|
|
156
|
+
created: number;
|
|
157
|
+
model: string;
|
|
158
|
+
choices: ChatCompletionStreamResponseChoice[];
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* AI模型提供商类型
|
|
162
|
+
*/
|
|
163
|
+
type AIProvider = 'openai' | 'deepseek' | 'custom';
|
|
164
|
+
/**
|
|
165
|
+
* AI模型配置接口
|
|
166
|
+
*/
|
|
167
|
+
interface AIModelConfig {
|
|
168
|
+
provider: AIProvider;
|
|
169
|
+
providerImplementation?: BaseModelProvider;
|
|
170
|
+
apiKey?: string;
|
|
171
|
+
apiUrl?: string;
|
|
172
|
+
apiVersion?: string;
|
|
173
|
+
defaultModel?: string;
|
|
174
|
+
defaultOptions?: ChatCompletionOptions;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 错误类型
|
|
178
|
+
*/
|
|
179
|
+
declare enum ErrorType {
|
|
180
|
+
NETWORK_ERROR = "network_error",
|
|
181
|
+
AUTHENTICATION_ERROR = "authentication_error",
|
|
182
|
+
RATE_LIMIT_ERROR = "rate_limit_error",
|
|
183
|
+
SERVER_ERROR = "server_error",
|
|
184
|
+
MODEL_ERROR = "model_error",
|
|
185
|
+
TIMEOUT_ERROR = "timeout_error",
|
|
186
|
+
UNKNOWN_ERROR = "unknown_error"
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* AI适配器错误
|
|
190
|
+
*/
|
|
191
|
+
interface AIAdapterError {
|
|
192
|
+
type: ErrorType;
|
|
193
|
+
message: string;
|
|
194
|
+
statusCode?: number;
|
|
195
|
+
originalError?: object;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 流式响应事件类型
|
|
199
|
+
*/
|
|
200
|
+
declare enum StreamEventType {
|
|
201
|
+
DATA = "data",
|
|
202
|
+
ERROR = "error",
|
|
203
|
+
DONE = "done"
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 流式响应处理器
|
|
207
|
+
*/
|
|
208
|
+
interface StreamHandler {
|
|
209
|
+
onData: (data: ChatCompletionStreamResponse) => void;
|
|
210
|
+
onError: (error: AIAdapterError) => void;
|
|
211
|
+
onDone: (finishReason?: string) => void;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* AI客户端类
|
|
216
|
+
* 负责根据配置选择合适的提供商并处理请求
|
|
217
|
+
*/
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @deprecated
|
|
221
|
+
* AI客户端类
|
|
222
|
+
*/
|
|
223
|
+
declare class AIClient {
|
|
224
|
+
private provider;
|
|
225
|
+
private config;
|
|
226
|
+
/**
|
|
227
|
+
* 构造函数
|
|
228
|
+
* @param config AI模型配置
|
|
229
|
+
*/
|
|
230
|
+
constructor(config: AIModelConfig);
|
|
231
|
+
/**
|
|
232
|
+
* 创建提供商实例
|
|
233
|
+
* @param config AI模型配置
|
|
234
|
+
* @returns 提供商实例
|
|
235
|
+
*/
|
|
236
|
+
private createProvider;
|
|
237
|
+
/**
|
|
238
|
+
* 发送聊天请求并获取响应
|
|
239
|
+
* @param request 聊天请求参数
|
|
240
|
+
* @returns 聊天响应
|
|
241
|
+
*/
|
|
242
|
+
chat(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
243
|
+
/**
|
|
244
|
+
* 发送流式聊天请求并通过处理器处理响应
|
|
245
|
+
* @param request 聊天请求参数
|
|
246
|
+
* @param handler 流式响应处理器
|
|
247
|
+
*/
|
|
248
|
+
chatStream(request: ChatCompletionRequest, handler: StreamHandler): Promise<void>;
|
|
249
|
+
/**
|
|
250
|
+
* 获取当前配置
|
|
251
|
+
* @returns AI模型配置
|
|
252
|
+
*/
|
|
253
|
+
getConfig(): AIModelConfig;
|
|
254
|
+
/**
|
|
255
|
+
* 更新配置
|
|
256
|
+
* @param config 新的AI模型配置
|
|
257
|
+
*/
|
|
258
|
+
updateConfig(config: Partial<AIModelConfig>): void;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
interface Tool {
|
|
52
262
|
type: 'function';
|
|
53
263
|
function: {
|
|
54
264
|
name: string;
|
|
55
|
-
|
|
56
|
-
|
|
265
|
+
description: string;
|
|
266
|
+
/**
|
|
267
|
+
* function 的输入参数,以 JSON Schema 对象描述
|
|
268
|
+
*/
|
|
269
|
+
parameters: any;
|
|
57
270
|
};
|
|
271
|
+
[key: string]: any;
|
|
272
|
+
}
|
|
273
|
+
interface MessageRequestBody {
|
|
274
|
+
messages: Partial<ChatMessage>[];
|
|
275
|
+
[key: string]: any;
|
|
58
276
|
}
|
|
277
|
+
type RequestState = 'idle' | 'processing' | 'completed' | 'aborted' | 'error';
|
|
278
|
+
type RequestProcessingState = 'requesting' | 'completing' | string;
|
|
59
279
|
interface Usage {
|
|
60
280
|
prompt_tokens: number;
|
|
61
281
|
completion_tokens: number;
|
|
@@ -226,28 +446,6 @@ interface Conversation extends ConversationInfo {
|
|
|
226
446
|
*/
|
|
227
447
|
engine: UseMessageReturn;
|
|
228
448
|
}
|
|
229
|
-
interface ConversationStorageStrategy {
|
|
230
|
-
/**
|
|
231
|
-
* Load all conversations (id and title only).
|
|
232
|
-
*/
|
|
233
|
-
loadConversations?: () => MaybePromise<ConversationInfo[]>;
|
|
234
|
-
/**
|
|
235
|
-
* Load all messages for a given conversation.
|
|
236
|
-
*/
|
|
237
|
-
loadMessages?: (conversationId: string) => MaybePromise<ChatMessage[]>;
|
|
238
|
-
/**
|
|
239
|
-
* Persist conversation metadata (create or update).
|
|
240
|
-
*/
|
|
241
|
-
saveConversation?: (conversation: ConversationInfo) => MaybePromise<void>;
|
|
242
|
-
/**
|
|
243
|
-
* Persist messages for a given conversation.
|
|
244
|
-
*/
|
|
245
|
-
saveMessages?: (conversationId: string, messages: ChatMessage[]) => MaybePromise<void>;
|
|
246
|
-
/**
|
|
247
|
-
* Optional method to delete a conversation and its messages.
|
|
248
|
-
*/
|
|
249
|
-
deleteConversation?: (conversationId: string) => MaybePromise<void>;
|
|
250
|
-
}
|
|
251
449
|
interface UseConversationOptions {
|
|
252
450
|
/**
|
|
253
451
|
* Base useMessage options for all conversations.
|
|
@@ -288,12 +486,125 @@ interface UseConversationReturn {
|
|
|
288
486
|
}) => Conversation;
|
|
289
487
|
switchConversation: (id: string) => Promise<Conversation | null>;
|
|
290
488
|
deleteConversation: (id: string) => Promise<void>;
|
|
489
|
+
clear: () => void;
|
|
291
490
|
updateConversationTitle: (id: string, title?: string) => void;
|
|
292
491
|
saveMessages: (id?: string) => void;
|
|
293
492
|
sendMessage: (content: string) => void;
|
|
294
493
|
abortActiveRequest: () => Promise<void>;
|
|
295
494
|
}
|
|
296
495
|
|
|
496
|
+
/**
|
|
497
|
+
* 存储策略接口
|
|
498
|
+
*/
|
|
499
|
+
interface ConversationStorageStrategy {
|
|
500
|
+
/**
|
|
501
|
+
* Load all conversations (id and title only).
|
|
502
|
+
*/
|
|
503
|
+
loadConversations: () => MaybePromise<ConversationInfo[]>;
|
|
504
|
+
/**
|
|
505
|
+
* Load all messages for a given conversation.
|
|
506
|
+
*/
|
|
507
|
+
loadMessages: (conversationId: string) => MaybePromise<ChatMessage[]>;
|
|
508
|
+
/**
|
|
509
|
+
* Persist conversation metadata (create or update).
|
|
510
|
+
*/
|
|
511
|
+
saveConversation: (conversation: ConversationInfo) => MaybePromise<void>;
|
|
512
|
+
/**
|
|
513
|
+
* Persist messages for a given conversation.
|
|
514
|
+
*/
|
|
515
|
+
saveMessages: (conversationId: string, messages: ChatMessage[]) => MaybePromise<void>;
|
|
516
|
+
/**
|
|
517
|
+
* Optional method to delete a conversation and its messages.
|
|
518
|
+
*/
|
|
519
|
+
deleteConversation?: (conversationId: string) => MaybePromise<void>;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* 本地存储策略
|
|
524
|
+
*/
|
|
525
|
+
declare class LocalStorageStrategy implements ConversationStorageStrategy {
|
|
526
|
+
private storageKey;
|
|
527
|
+
constructor(storageKey?: string);
|
|
528
|
+
saveConversation(conversation: ConversationInfo): void;
|
|
529
|
+
loadConversations(): ConversationInfo[];
|
|
530
|
+
saveMessages(conversationId: string, messages: ChatMessage[]): void;
|
|
531
|
+
loadMessages(conversationId: string): ChatMessage[];
|
|
532
|
+
deleteConversation(conversationId: string): void;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* IndexedDB 存储策略
|
|
537
|
+
*/
|
|
538
|
+
declare class IndexedDBStrategy implements ConversationStorageStrategy {
|
|
539
|
+
private dbName;
|
|
540
|
+
private dbVersion;
|
|
541
|
+
private db;
|
|
542
|
+
constructor(dbName?: string, dbVersion?: number);
|
|
543
|
+
/**
|
|
544
|
+
* 获取或初始化数据库连接
|
|
545
|
+
*/
|
|
546
|
+
private getDB;
|
|
547
|
+
/**
|
|
548
|
+
* 加载所有会话(只包含元数据)
|
|
549
|
+
*/
|
|
550
|
+
loadConversations(): Promise<ConversationInfo[]>;
|
|
551
|
+
/**
|
|
552
|
+
* 加载指定会话的所有消息
|
|
553
|
+
*/
|
|
554
|
+
loadMessages(conversationId: string): Promise<ChatMessage[]>;
|
|
555
|
+
/**
|
|
556
|
+
* 保存或更新会话元数据
|
|
557
|
+
*/
|
|
558
|
+
saveConversation(conversation: ConversationInfo): Promise<void>;
|
|
559
|
+
/**
|
|
560
|
+
* 保存指定会话的消息
|
|
561
|
+
*/
|
|
562
|
+
saveMessages(conversationId: string, messages: ChatMessage[]): Promise<void>;
|
|
563
|
+
/**
|
|
564
|
+
* 删除会话及其所有消息
|
|
565
|
+
*/
|
|
566
|
+
deleteConversation(conversationId: string): Promise<void>;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
interface LocalStorageConfig {
|
|
570
|
+
/** 存储键名 (default: 'tiny-robot-ai-conversations') */
|
|
571
|
+
key?: string;
|
|
572
|
+
}
|
|
573
|
+
interface IndexedDBConfig {
|
|
574
|
+
/** 数据库名称 (default: 'tiny-robot-ai-db') */
|
|
575
|
+
dbName?: string;
|
|
576
|
+
/** 数据库版本 (default: 1) */
|
|
577
|
+
dbVersion?: number;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* LocalStorage 策略工厂函数
|
|
581
|
+
*/
|
|
582
|
+
declare function localStorageStrategyFactory(config?: LocalStorageConfig): ConversationStorageStrategy;
|
|
583
|
+
/**
|
|
584
|
+
* IndexedDB 策略工厂函数
|
|
585
|
+
*/
|
|
586
|
+
declare function indexedDBStorageStrategyFactory(config?: IndexedDBConfig): ConversationStorageStrategy;
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* 工具函数模块
|
|
590
|
+
* 提供一些实用的辅助函数
|
|
591
|
+
*/
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* 将 SSE 流转换为异步生成器。
|
|
595
|
+
* 将服务器发送事件(SSE)流式响应转换为异步生成器,逐个产出解析后的数据
|
|
596
|
+
*
|
|
597
|
+
* 当取消信号被触发时,会抛出 name 为 'AbortError' 的错误
|
|
598
|
+
* @param response fetch 响应对象
|
|
599
|
+
* @param options 配置选项
|
|
600
|
+
* @param options.signal 可选的取消信号,用于中断流处理
|
|
601
|
+
* @returns 异步生成器,产出类型为 T 的数据
|
|
602
|
+
* @template T 生成器产出的数据类型,默认为 any
|
|
603
|
+
*/
|
|
604
|
+
declare function sseStreamToGenerator<T = any>(response: Response, options?: {
|
|
605
|
+
signal?: AbortSignal;
|
|
606
|
+
}): AsyncGenerator<T, void, unknown>;
|
|
607
|
+
|
|
297
608
|
declare const useConversation: (options: UseConversationOptions) => UseConversationReturn;
|
|
298
609
|
|
|
299
610
|
declare const fallbackRolePlugin: (options?: UseMessagePlugin & {
|
|
@@ -377,4 +688,4 @@ declare const toolPlugin: (options: UseMessagePlugin & {
|
|
|
377
688
|
|
|
378
689
|
declare const useMessage: (options: UseMessageOptions) => UseMessageReturn;
|
|
379
690
|
|
|
380
|
-
export { type BasePluginContext, type ChatCompletion, type ChatMessage, type Choice, type CompletionChoice, type Conversation, type ConversationInfo, type ConversationStorageStrategy, type DeltaChoice, EXCLUDE_MODE_REMOVE, type MessageMetadata, type MessageRequestBody, type RequestProcessingState, type RequestState, type Tool, type ToolCall, type Usage, type UseConversationOptions, type UseConversationReturn, type UseMessageOptions, type UseMessagePlugin, type UseMessageReturn,
|
|
691
|
+
export { type AIAdapterError, AIClient, type AIModelConfig, type AIProvider, type BasePluginContext, type ChatCompletion, type ChatCompletionOptions, type ChatCompletionRequest, type ChatCompletionResponse, type ChatCompletionResponseChoice, type ChatCompletionResponseMessage, type ChatCompletionResponseUsage, type ChatCompletionStreamResponse, type ChatCompletionStreamResponseChoice, type ChatCompletionStreamResponseDelta, type ChatHistory, type ChatMessage, type Choice, type CompletionChoice, type Conversation, type ConversationInfo, type ConversationStorageStrategy, type DeltaChoice, EXCLUDE_MODE_REMOVE, ErrorType, type IndexedDBConfig, IndexedDBStrategy, type LocalStorageConfig, LocalStorageStrategy, type MaybePromise, type MessageMetadata, type MessageRequestBody, type MessageRole, type RequestProcessingState, type RequestState, StreamEventType, type StreamHandler, type Tool, type ToolCall, type Usage, type UseConversationOptions, type UseConversationReturn, type UseMessageOptions, type UseMessagePlugin, type UseMessageReturn, fallbackRolePlugin, indexedDBStorageStrategyFactory, lengthPlugin, localStorageStrategyFactory, sseStreamToGenerator, thinkingPlugin, toolPlugin, useConversation, useMessage };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`);a=w.pop()||"";for(let x of w)if(x.trim()!==""&&x.startsWith("data: ")){let S=x.slice(6);if(S==="[DONE]")return;try{yield JSON.parse(S)}catch(b){console.warn("Failed to parse SSE data:",S,b)}}}}finally{s?.removeEventListener("abort",i),n.releaseLock()}}var N=require("vue");var q=require("vue");var $=(e={})=>{let{fallbackRole:t="assistant",...n}=e;return{name:"fallbackRole",...n,onBeforeRequest(s){let{requestBody:o,messages:a}=s;return o.messages=a.map(i=>({...i,role:i.role||t})),n.onBeforeRequest?.(s)}}};var Q=(e={})=>{let{continueContent:t="Please continue with your previous answer.",...n}=e;return{name:"length",...n,onAfterRequest:async s=>{let{lastChoice:o,appendMessage:a,requestNext:i}=s;return o?.finish_reason==="length"&&(a({role:"user",content:t}),i()),n.onAfterRequest?.(s)}}};var Y=(e={})=>({name:"thinking",...e,onCompletionChunk(t){let{choice:n,currentMessage:s}=t,a=typeof(n?.message?.reasoning_content||n?.delta?.reasoning_content)=="string";return s.state?s.state.thinking=a:s.state={thinking:a},e.onCompletionChunk?.(t)},onTurnEnd(t){let n=t.currentTurn.slice(-1)[0];return n?.state&&(n.state.thinking=void 0),e.onTurnEnd?.(t)}});var ae=require("vue");var j=class extends Error{constructor(t){super(t),this.name="AbortError"}};function ye(e){if(e.aborted)return{promise:Promise.reject(new j(String(e.reason??"Aborted"))),cleanup:()=>{}};let t=null;return{promise:new Promise((o,a)=>{t=()=>{a(new j(String(e.reason??"Aborted")))},e.addEventListener("abort",t,{once:!0})}),cleanup:()=>{t&&(e.removeEventListener("abort",t),t=null)}}}function se(e,t){let{promise:n,cleanup:s}=ye(t);return Promise.race([e,n]).finally(s)}function oe(e,t){let n={};for(let s in e)t.includes(s)&&(n[s]=e[s]);return n}function re(e,t){let n={};for(let s in e)t.includes(s)||(n[s]=e[s]);return n}async function*L(e){if(te(e)){yield*e;return}let t=await e;if(te(t)){yield*t;return}yield t}function te(e){return e&&typeof e=="object"&&typeof e[Symbol.asyncIterator]=="function"}var Z=e=>typeof e=="object"&&e!==null,ne=e=>Z(e)&&typeof e.index=="number",W=(e,t)=>{for(let[n,s]of Object.entries(t)){let o=e[n];if(o)if(typeof o=="string"&&typeof s=="string")n==="type"&&o||(e[n]=o+s);else if(Array.isArray(o)&&Array.isArray(s))if(o.every(a=>ne(a))&&s.every(a=>ne(a))){let a=new Map(o.map(f=>[f.index,f])),i=new Map(s.map(f=>[f.index,f]));for(let[f,v]of i)if(a.has(f)){let w=a.get(f);a.set(f,W(w,v))}else a.set(f,v);let p=Math.max(...Array.from(a.keys()),-1)+1,g=p>o.length?Array.from({length:p}):o;for(let[f,v]of a)g[f]=v;e[n]=g}else e[n]=[...o,...s];else Z(o)&&Z(s)&&(e[n]=W(o,s));else e[n]=s}return e};function Ce(e,t){let n=[];for(let s=0;s<e.length;s++){let o=e[s];if(o.role==="assistant"&&o.tool_calls&&o.tool_calls.length>0){let a=new Set(o.tool_calls.map(g=>g.id)),i=new Set;for(let g=s+1;g<e.length;g++){let f=e[g];f.role==="tool"&&f.tool_call_id&&a.has(f.tool_call_id)&&i.add(f.tool_call_id)}let p=o.tool_calls.map(g=>g.id).filter(g=>!i.has(g));p.length>0&&n.push({insertAfterIndex:s,missingToolCallIds:p})}}for(let s=n.length-1;s>=0;s--){let{insertAfterIndex:o,missingToolCallIds:a}=n[s],i=a.map(p=>({role:"tool",tool_call_id:p,content:t}));e.splice(o+1,0,...i)}}var ie="remove";function Me(e,t,n,s){let o=e.filter(i=>i[n]),a=new Set(o.flatMap(i=>i.tool_calls?.map(p=>p.id)??[]));if(t===ie)for(let i=e.length-1;i>=0;i--){let p=e[i];(p[n]||p[s]||p.tool_call_id&&a.has(p.tool_call_id))&&e.splice(i,1)}else if(t===!0)for(let i of e)(i[n]||i.tool_call_id&&a.has(i.tool_call_id))&&(i[s]=!0,delete i[n])}var ve=e=>{let{getTools:t,beforeCallTools:n,callTool:s,onToolCallStart:o,onToolCallEnd:a,toolCallCancelledContent:i="Tool call cancelled.",toolCallFailedContent:p="Tool call failed.",autoFillMissingToolMessages:g=!1,excludeToolMessagesNextTurn:f=!1,...v}=e,w=Symbol("doNotSendNextTurn"),x=Symbol("doNotSend"),S=(...m)=>{let[h,{primaryMessage:A}]=m;A.state.toolCall[h.id].status="running",o?.(...m)},b=(...m)=>{let[h,{status:A,primaryMessage:r}]=m;r.state.toolCall[h.id].status=A,a?.(...m)};return{name:"tool",...v,onTurnStart:m=>{let{messages:h}=m;return g&&Ce(h,i),f&&Me(h,f,w,x),v.onTurnStart?.(m)},onBeforeRequest:async m=>{let{messages:h,requestBody:A}=m;f===!0&&(A.messages=h.filter(c=>!c[x]));let r=await t?.();return r&&r.length>0&&(A.tools=r),v.onBeforeRequest?.(m)},onAfterRequest:async m=>{let{currentMessage:h,lastChoice:A,appendMessage:r,abortSignal:c,setRequestState:d,requestNext:y}=m;if(A?.finish_reason!=="tool_calls"||!h.tool_calls?.length)return;f&&(h[w]=!0),d("processing","calling-tools"),await n?.(h.tool_calls,{...m,currentMessage:h});let T=h.tool_calls.map(async R=>{let D=Math.floor(Date.now()/1e3),E=(0,ae.reactive)({role:"tool",tool_call_id:R.id,content:"",metadata:{createdAt:D,updatedAt:D}});r(E);let I={...m,primaryMessage:h,toolMessage:E};S(R,I);try{let F=s(R,I),l=L(F);for await(let u of l){if(typeof u=="string")E.content+=u;else{let M={};try{M=JSON.parse(E.content||"{}")}catch(P){console.warn(P)}E.content=JSON.stringify(W(M,u))}E.metadata.updatedAt=Math.floor(Date.now()/1e3)}b(R,{...I,status:"success"})}catch(F){let l=F instanceof Error?F:new Error(String(F));if(c.aborted){b(R,{...I,status:"cancelled",error:l});return}console.error(F),E.content.length===0&&(E.content=p),b(R,{...I,status:"failed",error:l})}});return await Promise.all(T),y(),v.onAfterRequest?.(m)},onCompletionChunk:m=>{var A,r,c;let{currentMessage:h}=m;if(Array.isArray(h.tool_calls))for(let d of h.tool_calls)h.state?.toolCall?.[d.id]?.status||(h.state??(h.state={}),(A=h.state).toolCall??(A.toolCall={}),(r=h.state.toolCall)[c=d.id]??(r[c]={}));return v.onCompletionChunk?.(m)}}};var Te=e=>{let t=[];for(let n of e){if(n.name){let s=t.findIndex(o=>o.name===n.name);s!==-1&&t.splice(s,1)}t.push(n)}return t},H=e=>{let{initialMessages:t=[],requestMessageFields:n=[],requestMessageFieldsExclude:s=["state","metadata","loading"],plugins:o=[],onCompletionChunk:a}=e,i=(0,q.ref)("idle"),p=(0,q.ref)(void 0),g=(0,q.ref)(t),f=(0,q.ref)(e.responseProvider),v=null,w=[],x={},S=[$(),Y(),Q()],b=Te(S.concat(o)),m=(0,q.computed)(()=>i.value==="processing"),h=async l=>{if(!l||!l.trim()){console.warn("Cannot send empty message");return}if(m.value){console.warn("Cannot send message while processing is in progress");return}let u=Math.floor(Date.now()/1e3);g.value.push({role:"user",content:l.trim(),metadata:{createdAt:u,updatedAt:u}}),w.push(g.value[g.value.length-1]),await D()},A=async(...l)=>{if(m.value){console.warn("Cannot send message while processing is in progress");return}g.value.push(...l),w.push(...l),await D()},r=l=>{let u=l;return n.length&&(u=u.map(M=>oe(M,n))),s.length&&(u=u.map(M=>re(M,s))),u},c=(l,u)=>{i.value=l,l==="processing"?p.value=u||"requesting":p.value=void 0},d=l=>{Object.assign(x,l)},y=l=>({messages:g.value,currentTurn:w,requestState:i.value,processingState:p.value,plugins:b,abortSignal:l,setRequestState:c,customContext:x,setCustomContext:d}),T=(l,u)=>typeof l.disabled=="function"?l.disabled(u):!!l.disabled,R=async(l,u)=>{c("processing","requesting");let M=new Proxy({messages:r(g.value)},{set(_,O,B){return O==="messages"?(_.messages=r(B),!0):(_[O]=B,!0)}}),P=y(u);for(let _ of b.filter(O=>!T(O,P)))await _.onBeforeRequest?.({...P,requestBody:M});let C=(0,q.reactive)({role:"",content:"",loading:!0});F(C);let k,z=l(M,u),U=L(z);for await(let _ of U){c("processing","completing"),C.loading&&(C.loading=void 0);let O=_.choices?.find(G=>G.index===0);if(O){k=O;let G=()=>{C.metadata||(C.metadata={});let{created:K,...ue}=_;C.metadata.createdAt=K,C.metadata.updatedAt=Math.floor(Date.now()/1e3),Object.assign(C.metadata,ue),W(C,O.message||O.delta)};if(a){let K=y(u);a({...K,chunk:_,choice:O,currentMessage:C},G)}else G()}let B=y(u);for(let G of b.filter(K=>!T(K,B)))G.onCompletionChunk?.({...B,abortSignal:u,chunk:_,choice:O,currentMessage:C})}await I(C,l,u,k)},D=async()=>{let l=new AbortController;v=l,x={};try{c("processing","requesting");let u=y(l.signal);for(let C of b.filter(k=>!T(k,u)))await C.onTurnStart?.(u);let M=f.value;try{await R(M,l.signal),c("completed")}catch(C){if(l.signal.aborted||C instanceof j||C instanceof Error&&C.name==="AbortError")c("aborted");else throw C}let P=y(l.signal);for(let C of b.filter(k=>!T(k,P)))await C.onTurnEnd?.(P)}catch(u){c("error");let M=!1,P=y(l.signal);for(let C of b.filter(k=>!T(k,P)))M=!0,C.onError?.({...P,error:u});if(!M)throw u}finally{let u=y(l.signal);for(let M of b.filter(P=>!T(P,u)))try{M.onFinally?.(u)}catch(P){console.error(`Error in onFinally hook for plugin ${M.name||"Anonymous"}:`,P)}v=null,w.slice(-1)[0]&&(w.slice(-1)[0].loading=void 0),w=[]}},E=async()=>{v?.abort(),m.value&&await new Promise(l=>{let u=(0,q.watch)(m,M=>{M||(u(),l())},{immediate:!0})})},I=async(l,u,M,P)=>{let C=!1,k=y(M),z=b.filter(U=>!T(U,k)).map(U=>{if(!U.onAfterRequest)return null;let _=B=>{F(B)},O=()=>{C=!0};return U.onAfterRequest({...k,currentMessage:l,lastChoice:P,appendMessage:_,requestNext:O})}).filter(U=>U!==null);await se(Promise.all(z),M),C&&await R(u,M)},F=l=>{let u=Array.isArray(l)?l:[l];g.value.push(...u),w.push(...u)};return{requestState:i,processingState:p,messages:g,responseProvider:f,isProcessing:m,sendMessage:h,send:A,abortRequest:E}};var V=require("vue");function ce(e,t=200,n=!1,s=!0,o=!1){return be(we(t,n,s,o),e)}function be(e,t){function n(...s){return new Promise((o,a)=>{Promise.resolve(e(()=>t.apply(this,s),{fn:t,thisArg:this,args:s})).then(o).catch(a)})}return n}var le=()=>{};function we(...e){let t=0,n,s=!0,o=le,a,i,p,g,f;!(0,V.isRef)(e[0])&&typeof e[0]=="object"?{delay:i,trailing:p=!0,leading:g=!0,rejectOnCancel:f=!1}=e[0]:[i,p=!0,g=!0,f=!1]=e;let v=()=>{n&&(clearTimeout(n),n=void 0,o(),o=le)};return x=>{let S=(0,V.toValue)(i),b=Date.now()-t,m=()=>a=x();return v(),S<=0?(t=Date.now(),m()):(b>S?(t=Date.now(),(g||!s)&&m()):p&&(a=new Promise((h,A)=>{o=f?A:h,n=setTimeout(()=>{t=Date.now(),s=!0,h(m()),v()},Math.max(0,S-b))})),!g&&!n&&(n=setTimeout(()=>s=!0,S)),s=!1,a)}}var xe=e=>{let t=(0,N.ref)([]),n=new Map,s=new Map,o=(0,N.ref)(null),a=(0,N.computed)(()=>{let r=o.value;if(!r)return null;let c=t.value.find(y=>y.id===r);if(!c)return null;let d=n.get(r);return d?{...c,engine:d}:null}),i=r=>{if(!e.storage?.saveMessages)return;let c=r||o.value,d=t.value.find(T=>T.id===c);if(!d)return;d.updatedAt=Date.now(),e.storage?.saveConversation?.(d);let y=n.get(d.id);y&&e.storage.saveMessages(d.id,y.messages.value)},p=(r,c)=>{if(!e.autoSaveMessages||!e.storage?.saveMessages)return;let d=s.get(r);d&&d();let y=e.autoSaveThrottle??1e3,T=ce(()=>{i(r)},y,!0,!0),R=(0,N.watch)(c.messages,T,{deep:!0});s.set(r,R)},g=r=>{let c=s.get(r);c&&(c(),s.delete(r))};e.storage?.loadConversations&&Promise.resolve(e.storage.loadConversations()).then(r=>{t.value=r}).catch(r=>{console.error("[useConversation] loadConversations failed:",r)});let f=async(r,c)=>{let d=n.get(r);if(d)return d;let y=c?.initialMessages??e.useMessageOptions.initialMessages??[];if(e.storage?.loadMessages)try{y=await e.storage.loadMessages(r)}catch(R){console.error("[useConversation] loadMessages failed:",R)}let T=H({...e.useMessageOptions,...c,initialMessages:y});return n.set(r,T),p(r,T),T};function v(){return Date.now().toString(36)+Math.random().toString(36).substring(2,9)}let w=r=>{let{id:c=v(),title:d,metadata:y,useMessageOptions:T}=r||{},R=Date.now(),D={id:c,title:d,createdAt:R,updatedAt:R,metadata:y};t.value.unshift(D);let E=H({...e.useMessageOptions,...T});return n.set(c,E),p(c,E),e.storage?.saveConversation?.(D),e.storage?.saveMessages?.(c,E.messages.value),o.value=c,a.value},x=r=>{let{excludeId:c}=r||{};n.forEach((d,y)=>{if(c&&y===c)return;d.isProcessing?.value||(g(y),n.delete(y))})};return{conversations:t,activeConversationId:o,activeConversation:a,createConversation:w,switchConversation:async r=>r?o.value===r?a.value:t.value.find(d=>d.id===r)?(await f(r),x({excludeId:r}),o.value=r,a.value):null:null,deleteConversation:async r=>{let c=t.value.findIndex(y=>y.id===r);if(c===-1)return;await n.get(r)?.abortRequest(),g(r),n.delete(r),t.value.splice(c,1),e.storage?.deleteConversation?.(r),o.value===r&&(o.value=null,x())},updateConversationTitle:(r,c)=>{let d=t.value.find(y=>y.id===r);d&&(d.title=c,d.updatedAt=Date.now(),e.storage?.saveConversation?.(d))},saveMessages:i,sendMessage:r=>{a.value?.engine.sendMessage(r)},abortActiveRequest:async()=>{await a.value?.engine.abortRequest()}}};0&&(module.exports={EXCLUDE_MODE_REMOVE,createSSEStreamGenerator,fallbackRolePlugin,lengthPlugin,thinkingPlugin,toolPlugin,useConversation,useMessage});
|
|
1
|
+
"use strict";var ne=Object.defineProperty;var Pe=Object.getOwnPropertyDescriptor;var Ee=Object.getOwnPropertyNames;var Oe=Object.prototype.hasOwnProperty;var Ie=(o,t)=>{for(var e in t)ne(o,e,{get:t[e],enumerable:!0})},ke=(o,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ee(t))!Oe.call(o,n)&&n!==e&&ne(o,n,{get:()=>t[n],enumerable:!(s=Pe(t,n))||s.enumerable});return o};var _e=o=>ke(ne({},"__esModule",{value:!0}),o);var Le={};Ie(Le,{AIClient:()=>Q,EXCLUDE_MODE_REMOVE:()=>Ae,ErrorType:()=>de,IndexedDBStrategy:()=>W,LocalStorageStrategy:()=>L,StreamEventType:()=>ge,fallbackRolePlugin:()=>ie,indexedDBStorageStrategyFactory:()=>ye,lengthPlugin:()=>le,localStorageStrategyFactory:()=>Z,sseStreamToGenerator:()=>fe,thinkingPlugin:()=>ce,toolPlugin:()=>Be,useConversation:()=>je,useMessage:()=>te});module.exports=_e(Le);var X=class{constructor(t){this.config=t}updateConfig(t){this.config={...this.config,...t}}getConfig(){return{...this.config}}validateRequest(t){if(!t.messages||!Array.isArray(t.messages)||t.messages.length===0)throw new Error("\u8BF7\u6C42\u5FC5\u987B\u5305\u542B\u81F3\u5C11\u4E00\u6761\u6D88\u606F");for(let e of t.messages)if(!e.role||!e.content)throw new Error("\u6BCF\u6761\u6D88\u606F\u5FC5\u987B\u5305\u542B\u89D2\u8272\u548C\u5185\u5BB9")}};var de=(l=>(l.NETWORK_ERROR="network_error",l.AUTHENTICATION_ERROR="authentication_error",l.RATE_LIMIT_ERROR="rate_limit_error",l.SERVER_ERROR="server_error",l.MODEL_ERROR="model_error",l.TIMEOUT_ERROR="timeout_error",l.UNKNOWN_ERROR="unknown_error",l))(de||{}),ge=(s=>(s.DATA="data",s.ERROR="error",s.DONE="done",s))(ge||{});function F(o){return{type:o.type||"unknown_error",message:o.message||"\u672A\u77E5\u9519\u8BEF",statusCode:o.statusCode,originalError:o.originalError}}function re(o){if(!o.response)return F({type:"network_error",message:"\u7F51\u7EDC\u8FDE\u63A5\u9519\u8BEF\uFF0C\u8BF7\u68C0\u67E5\u60A8\u7684\u7F51\u7EDC\u8FDE\u63A5",originalError:o});if(o.response){let{status:t,data:e}=o.response;return t===401||t===403?F({type:"authentication_error",message:"\u8EAB\u4EFD\u9A8C\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u60A8\u7684API\u5BC6\u94A5",statusCode:t,originalError:o}):t===429?F({type:"rate_limit_error",message:"\u8D85\u51FAAPI\u8C03\u7528\u9650\u5236\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",statusCode:t,originalError:o}):t>=500?F({type:"server_error",message:"\u670D\u52A1\u5668\u9519\u8BEF\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",statusCode:t,originalError:o}):F({type:"unknown_error",message:e?.error?.message||`\u8BF7\u6C42\u5931\u8D25\uFF0C\u72B6\u6001\u7801: ${t}`,statusCode:t,originalError:o})}return o.code==="ECONNABORTED"?F({type:"timeout_error",message:"\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",originalError:o}):F({type:"unknown_error",message:o.message||"\u53D1\u751F\u672A\u77E5\u9519\u8BEF",originalError:o})}async function pe(o,t,e){let s=o.body?.getReader();if(!s)throw new Error("Response body is null");let n=new TextDecoder,r="",a,l;e&&e.addEventListener("abort",()=>{s.cancel().catch(u=>console.error("Error cancelling reader:",u))},{once:!0});try{for(;;){if(e?.aborted){await s.cancel();break}let{done:u,value:p}=await s.read();if(u)break;let v=n.decode(p,{stream:!0});r+=v;let R=r.split(`
|
|
2
|
+
|
|
3
|
+
`);r=R.pop()||"";for(let x of R)if(x.trim()!==""){if(x.trim()==="data: [DONE]"){l&&(a=l),t.onDone(a);continue}try{let A=x.match(/^data: (.+)$/m);if(!A)continue;let b=JSON.parse(A[1]);t.onData(b),l=b.choices?.[0]?.finish_reason||void 0}catch(A){console.error("Error parsing SSE message:",A)}}}(r.trim()==="data: [DONE]"||e?.aborted)&&(e?.aborted&&(a="aborted"),t.onDone(a))}catch(u){if(e?.aborted)return;throw u}}function ae(o="The operation was aborted"){let t=new Error(o);return t.name="AbortError",t}async function*fe(o,t={}){let e=o.body?.getReader();if(!e)throw new Error("ReadableStream not supported");let{signal:s}=t,n=new TextDecoder,r="",a=()=>{e.cancel()};s?.addEventListener("abort",a);try{for(;;){if(s?.aborted)throw ae();let l;try{l=await e.read()}catch(x){throw s?.aborted?ae():x}let{done:u,value:p}=l;if(u){if(s?.aborted)throw ae();return}let v=n.decode(p,{stream:!0});r+=v;let R=r.split(`
|
|
4
|
+
`);r=R.pop()||"";for(let x of R)if(x.trim()!==""&&x.startsWith("data: ")){let A=x.slice(6);if(A==="[DONE]")return;try{yield JSON.parse(A)}catch(b){console.warn("Failed to parse SSE data:",A,b)}}}}finally{s?.removeEventListener("abort",a),e.releaseLock()}}var $=class extends X{constructor(e){super(e);this.defaultModel="gpt-3.5-turbo";this.baseURL=e.apiUrl||"https://api.openai.com/v1",this.apiKey=e.apiKey||"",e.defaultModel&&(this.defaultModel=e.defaultModel),this.apiKey||console.warn("API key is not provided. Authentication will likely fail.")}async chat(e){try{this.validateRequest(e);let s={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...e.options,stream:!1},n={method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)};this.apiKey&&Object.assign(n.headers,{Authorization:`Bearer ${this.apiKey}`});let r=await fetch(`${this.baseURL}/chat/completions`,n);if(!r.ok){let a=await r.text();throw new Error(`HTTP error! status: ${r.status}, details: ${a}`)}return await r.json()}catch(s){throw re(s)}}async chatStream(e,s){let{signal:n,...r}=e.options||{};try{this.validateRequest(e);let a={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...r,stream:!0},l={method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,Accept:"text/event-stream"},body:JSON.stringify(a),signal:n};this.apiKey&&Object.assign(l.headers,{Authorization:`Bearer ${this.apiKey}`});let u=await fetch(`${this.baseURL}/chat/completions`,l);if(!u.ok){let p=await u.text();throw new Error(`HTTP error! status: ${u.status}, details: ${p}`)}await pe(u,s,n)}catch(a){if(n?.aborted)return;s.onError(re(a))}}updateConfig(e){super.updateConfig(e),e.apiUrl&&(this.baseURL=e.apiUrl),e.apiKey&&(this.apiKey=e.apiKey),e.defaultModel&&(this.defaultModel=e.defaultModel)}};var Q=class{constructor(t){this.config=t,this.provider=this.createProvider(t)}createProvider(t){if(t.provider==="custom"&&"providerImplementation"in t)return t.providerImplementation;if(t.provider==="deepseek"){let e={defaultModel:"deepseek-chat",apiUrl:"https://api.deepseek.com/v1"};return new $({...e,...t})}else return new $(t)}async chat(t){return this.provider.chat(t)}async chatStream(t,e){let s={...t,options:{...t.options,stream:!0}};return this.provider.chatStream(s,e)}getConfig(){return{...this.config}}updateConfig(t){this.config={...this.config,...t},t.provider&&t.provider!==this.config.provider?this.provider=this.createProvider(this.config):this.provider.updateConfig(this.config)}};var me=require("vue");function J(o,t=new WeakSet){if(o==null||typeof o!="object")return o;if(t.has(o))return Array.isArray(o)?[]:{};t.add(o);try{let e=(0,me.toRaw)(o);if(Array.isArray(e))return e.map(n=>J(n,t));if(e instanceof Date||e instanceof RegExp||e instanceof ArrayBuffer||e instanceof Blob)return e;let s={};for(let n in e){if(typeof n=="symbol")continue;let r=Object.getOwnPropertyDescriptor(e,n);if(!r||r.get||r.set)continue;let a=e[n];typeof a!="function"&&typeof a!="symbol"&&(s[n]=J(a,t))}return s}catch(e){return console.warn("unwrapProxy error:",e),Array.isArray(o)?[]:{}}}var Y=o=>o.map(t=>{let{renderContent:e,...s}=t;if(!Array.isArray(e))return t;let n=e.filter(a=>a.type==="collapsible-text"),r=e.filter(a=>a.type==="markdown"||a.type==="text");return n.length>0&&(s.reasoning_content=n.map(a=>a.content).join("")),r.length>0&&(s.content=r.map(a=>a.content).join("")),s});var z=o=>{let t=localStorage.getItem(o);return t?JSON.parse(t):[]},L=class{constructor(t="tiny-robot-ai-conversations"){this.storageKey=t}saveConversation(t){try{let e=z(this.storageKey),s=e.findIndex(n=>n.id===t.id);s!==-1?Object.assign(e[s],t):e.unshift({...t,messages:[]}),localStorage.setItem(this.storageKey,JSON.stringify(e))}catch(e){console.error("\u4FDD\u5B58\u4F1A\u8BDD\u5931\u8D25:",e)}}loadConversations(){try{return z(this.storageKey).map(e=>({id:e.id,title:e.title,createdAt:e.createdAt,updatedAt:e.updatedAt,metadata:e.metadata}))}catch(t){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u5931\u8D25:",t),[]}}saveMessages(t,e){try{let s=z(this.storageKey),n=s.findIndex(r=>r.id===t);n!==-1&&(s[n].messages=e),localStorage.setItem(this.storageKey,JSON.stringify(s))}catch(s){console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",s)}}loadMessages(t){try{let s=z(this.storageKey).find(r=>r.id===t);return Y(s?.messages||[])}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}deleteConversation(t){let e=z(this.storageKey),s=e.findIndex(n=>n.id===t);s!==-1&&e.splice(s,1),localStorage.setItem(this.storageKey,JSON.stringify(e))}};var he=require("idb");var W=class{constructor(t="tiny-robot-ai-db",e=3){this.db=null;this.dbName=t,this.dbVersion=e}async getDB(){return this.db||(this.db=await(0,he.openDB)(this.dbName,this.dbVersion,{upgrade(t){t.objectStoreNames.contains("conversations")||t.createObjectStore("conversations",{keyPath:"id"}).createIndex("by-updated","updatedAt"),t.objectStoreNames.contains("messages")||t.createObjectStore("messages",{keyPath:"conversationId"})}})),this.db}async loadConversations(){try{return(await(await this.getDB()).getAllFromIndex("conversations","by-updated")).reverse()}catch(t){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u5931\u8D25:",t),[]}}async loadMessages(t){try{let s=await(await this.getDB()).get("messages",t);return s?Y(s.messages):[]}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}async saveConversation(t){try{let e=await this.getDB(),s=J(t);await e.put("conversations",s)}catch(e){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u5931\u8D25:",e),e}}async saveMessages(t,e){try{let s=await this.getDB(),n=J(e);await s.put("messages",{conversationId:t,messages:n})}catch(s){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",s),s}}async deleteConversation(t){try{let e=await this.getDB();await e.delete("conversations",t),await e.delete("messages",t)}catch(e){throw console.error("\u5220\u9664\u4F1A\u8BDD\u5931\u8D25:",e),e}}};function Z(o={}){return new L(o.key||"tiny-robot-ai-conversations")}function ye(o={}){return new W(o.dbName||"tiny-robot-ai-db",o.dbVersion||1)}var K=require("vue");var U=require("vue");var ie=(o={})=>{let{fallbackRole:t="assistant",...e}=o;return{name:"fallbackRole",...e,onBeforeRequest(s){let{requestBody:n,messages:r}=s;return n.messages=r.map(a=>({...a,role:a.role||t})),e.onBeforeRequest?.(s)}}};var le=(o={})=>{let{continueContent:t="Please continue with your previous answer.",...e}=o;return{name:"length",...e,onAfterRequest:async s=>{let{lastChoice:n,appendMessage:r,requestNext:a}=s;return n?.finish_reason==="length"&&(r({role:"user",content:t}),a()),e.onAfterRequest?.(s)}}};var ce=(o={})=>({name:"thinking",...o,onCompletionChunk(t){let{choice:e,currentMessage:s}=t,r=typeof(e?.message?.reasoning_content||e?.delta?.reasoning_content)=="string";return s.state?s.state.thinking=r:s.state={thinking:r},o.onCompletionChunk?.(t)},onTurnEnd(t){let e=t.currentTurn.slice(-1)[0];return e?.state&&(e.state.thinking=void 0),o.onTurnEnd?.(t)}});var xe=require("vue");var H=class extends Error{constructor(t){super(t),this.name="AbortError"}};function De(o){if(o.aborted)return{promise:Promise.reject(new H(String(o.reason??"Aborted"))),cleanup:()=>{}};let t=null;return{promise:new Promise((n,r)=>{t=()=>{r(new H(String(o.reason??"Aborted")))},o.addEventListener("abort",t,{once:!0})}),cleanup:()=>{t&&(o.removeEventListener("abort",t),t=null)}}}function Me(o,t){let{promise:e,cleanup:s}=De(t);return Promise.race([o,e]).finally(s)}function be(o,t){let e={};for(let s in o)t.includes(s)&&(e[s]=o[s]);return e}function Re(o,t){let e={};for(let s in o)t.includes(s)||(e[s]=o[s]);return e}async function*ee(o){if(Ce(o)){yield*o;return}let t=await o;if(Ce(t)){yield*t;return}yield t}function Ce(o){return o&&typeof o=="object"&&typeof o[Symbol.asyncIterator]=="function"}var ue=o=>typeof o=="object"&&o!==null,ve=o=>ue(o)&&typeof o.index=="number",G=(o,t)=>{for(let[e,s]of Object.entries(t)){let n=o[e];if(n)if(typeof n=="string"&&typeof s=="string")e==="type"&&n||(o[e]=n+s);else if(Array.isArray(n)&&Array.isArray(s))if(n.every(r=>ve(r))&&s.every(r=>ve(r))){let r=new Map(n.map(p=>[p.index,p])),a=new Map(s.map(p=>[p.index,p]));for(let[p,v]of a)if(r.has(p)){let R=r.get(p);r.set(p,G(R,v))}else r.set(p,v);let l=Math.max(...Array.from(r.keys()),-1)+1,u=l>n.length?Array.from({length:l}):n;for(let[p,v]of r)u[p]=v;o[e]=u}else o[e]=[...n,...s];else ue(n)&&ue(s)&&(o[e]=G(n,s));else o[e]=s}return o};function Ue(o,t){let e=[];for(let s=0;s<o.length;s++){let n=o[s];if(n.role==="assistant"&&n.tool_calls&&n.tool_calls.length>0){let r=new Set(n.tool_calls.map(u=>u.id)),a=new Set;for(let u=s+1;u<o.length;u++){let p=o[u];p.role==="tool"&&p.tool_call_id&&r.has(p.tool_call_id)&&a.add(p.tool_call_id)}let l=n.tool_calls.map(u=>u.id).filter(u=>!a.has(u));l.length>0&&e.push({insertAfterIndex:s,missingToolCallIds:l})}}for(let s=e.length-1;s>=0;s--){let{insertAfterIndex:n,missingToolCallIds:r}=e[s],a=r.map(l=>({role:"tool",tool_call_id:l,content:t}));o.splice(n+1,0,...a)}}var Ae="remove";function qe(o,t,e,s){let n=o.filter(a=>a[e]),r=new Set(n.flatMap(a=>a.tool_calls?.map(l=>l.id)??[]));if(t===Ae)for(let a=o.length-1;a>=0;a--){let l=o[a];(l[e]||l[s]||l.tool_call_id&&r.has(l.tool_call_id))&&o.splice(a,1)}else if(t===!0)for(let a of o)(a[e]||a.tool_call_id&&r.has(a.tool_call_id))&&(a[s]=!0,delete a[e])}var Be=o=>{let{getTools:t,beforeCallTools:e,callTool:s,onToolCallStart:n,onToolCallEnd:r,toolCallCancelledContent:a="Tool call cancelled.",toolCallFailedContent:l="Tool call failed.",autoFillMissingToolMessages:u=!1,excludeToolMessagesNextTurn:p=!1,...v}=o,R=Symbol("doNotSendNextTurn"),x=Symbol("doNotSend"),A=(...m)=>{let[h,{primaryMessage:T}]=m;T.state.toolCall[h.id].status="running",n?.(...m)},b=(...m)=>{let[h,{status:T,primaryMessage:O}]=m;O.state.toolCall[h.id].status=T,r?.(...m)};return{name:"tool",...v,onTurnStart:m=>{let{messages:h}=m;return u&&Ue(h,a),p&&qe(h,p,R,x),v.onTurnStart?.(m)},onBeforeRequest:async m=>{let{messages:h,requestBody:T}=m;p===!0&&(T.messages=h.filter(E=>!E[x]));let O=await t?.();return O&&O.length>0&&(T.tools=O),v.onBeforeRequest?.(m)},onAfterRequest:async m=>{let{currentMessage:h,lastChoice:T,appendMessage:O,abortSignal:E,setRequestState:i,requestNext:c}=m;if(T?.finish_reason!=="tool_calls"||!h.tool_calls?.length)return;p&&(h[R]=!0),i("processing","calling-tools"),await e?.(h.tool_calls,{...m,currentMessage:h});let f=h.tool_calls.map(async y=>{let S=Math.floor(Date.now()/1e3),w=(0,xe.reactive)({role:"tool",tool_call_id:y.id,content:"",metadata:{createdAt:S,updatedAt:S}});O(w);let q={...m,primaryMessage:h,toolMessage:w};A(y,q);try{let I=s(y,q),d=ee(I);for await(let g of d){if(typeof g=="string")w.content+=g;else{let M={};try{M=JSON.parse(w.content||"{}")}catch(P){console.warn(P)}w.content=JSON.stringify(G(M,g))}w.metadata.updatedAt=Math.floor(Date.now()/1e3)}b(y,{...q,status:"success"})}catch(I){let d=I instanceof Error?I:new Error(String(I));if(E.aborted){b(y,{...q,status:"cancelled",error:d});return}console.error(I),w.content.length===0&&(w.content=l),b(y,{...q,status:"failed",error:d})}});return await Promise.all(f),c(),v.onAfterRequest?.(m)},onCompletionChunk:m=>{var T,O,E;let{currentMessage:h}=m;if(Array.isArray(h.tool_calls))for(let i of h.tool_calls)h.state?.toolCall?.[i.id]?.status||(h.state??(h.state={}),(T=h.state).toolCall??(T.toolCall={}),(O=h.state.toolCall)[E=i.id]??(O[E]={}));return v.onCompletionChunk?.(m)}}};var Ne=o=>{let t=[];for(let e of o){if(e.name){let s=t.findIndex(n=>n.name===e.name);s!==-1&&t.splice(s,1)}t.push(e)}return t},te=o=>{let{initialMessages:t=[],requestMessageFields:e=[],requestMessageFieldsExclude:s=["state","metadata","loading"],plugins:n=[],onCompletionChunk:r}=o,a=(0,U.ref)("idle"),l=(0,U.ref)(void 0),u=(0,U.ref)(t),p=(0,U.ref)(o.responseProvider),v=null,R=[],x={},A=[ie(),ce(),le()],b=Ne(A.concat(n)),m=(0,U.computed)(()=>a.value==="processing"),h=async d=>{if(!d||!d.trim()){console.warn("Cannot send empty message");return}if(m.value){console.warn("Cannot send message while processing is in progress");return}let g=Math.floor(Date.now()/1e3);u.value.push({role:"user",content:d.trim(),metadata:{createdAt:g,updatedAt:g}}),R.push(u.value[u.value.length-1]),await S()},T=async(...d)=>{if(m.value){console.warn("Cannot send message while processing is in progress");return}u.value.push(...d),R.push(...d),await S()},O=d=>{let g=d;return e.length&&(g=g.map(M=>be(M,e))),s.length&&(g=g.map(M=>Re(M,s))),g},E=(d,g)=>{a.value=d,d==="processing"?l.value=g||"requesting":l.value=void 0},i=d=>{Object.assign(x,d)},c=d=>({messages:u.value,currentTurn:R,requestState:a.value,processingState:l.value,plugins:b,abortSignal:d,setRequestState:E,customContext:x,setCustomContext:i}),f=(d,g)=>typeof d.disabled=="function"?d.disabled(g):!!d.disabled,y=async(d,g)=>{E("processing","requesting");let M=new Proxy({messages:O(u.value)},{set(D,k,N){return k==="messages"?(D.messages=O(N),!0):(D[k]=N,!0)}}),P=c(g);for(let D of b.filter(k=>!f(k,P)))await D.onBeforeRequest?.({...P,requestBody:M});let C=(0,U.reactive)({role:"",content:"",loading:!0});I(C);let _,se=d(M,g),B=ee(se);for await(let D of B){E("processing","completing"),C.loading&&(C.loading=void 0);let k=D.choices?.find(j=>j.index===0);if(k){_=k;let j=()=>{C.metadata||(C.metadata={});let{created:V,...Se}=D;C.metadata.createdAt=V,C.metadata.updatedAt=Math.floor(Date.now()/1e3),Object.assign(C.metadata,Se),G(C,k.message||k.delta)};if(r){let V=c(g);r({...V,chunk:D,choice:k,currentMessage:C},j)}else j()}let N=c(g);for(let j of b.filter(V=>!f(V,N)))j.onCompletionChunk?.({...N,abortSignal:g,chunk:D,choice:k,currentMessage:C})}await q(C,d,g,_)},S=async()=>{let d=new AbortController;v=d,x={};try{E("processing","requesting");let g=c(d.signal);for(let C of b.filter(_=>!f(_,g)))await C.onTurnStart?.(g);let M=p.value;try{await y(M,d.signal),E("completed")}catch(C){if(d.signal.aborted||C instanceof H||C instanceof Error&&C.name==="AbortError")E("aborted");else throw C}let P=c(d.signal);for(let C of b.filter(_=>!f(_,P)))await C.onTurnEnd?.(P)}catch(g){E("error");let M=!1,P=c(d.signal);for(let C of b.filter(_=>!f(_,P)))M=!0,C.onError?.({...P,error:g});if(!M)throw g}finally{let g=c(d.signal);for(let M of b.filter(P=>!f(P,g)))try{M.onFinally?.(g)}catch(P){console.error(`Error in onFinally hook for plugin ${M.name||"Anonymous"}:`,P)}v=null,R.slice(-1)[0]&&(R.slice(-1)[0].loading=void 0),R=[]}},w=async()=>{v?.abort(),m.value&&await new Promise(d=>{let g=(0,U.watch)(m,M=>{M||(g(),d())},{immediate:!0})})},q=async(d,g,M,P)=>{let C=!1,_=c(M),se=b.filter(B=>!f(B,_)).map(B=>{if(!B.onAfterRequest)return null;let D=N=>{I(N)},k=()=>{C=!0};return B.onAfterRequest({..._,currentMessage:d,lastChoice:P,appendMessage:D,requestNext:k})}).filter(B=>B!==null);await Me(Promise.all(se),M),C&&await y(g,M)},I=d=>{let g=Array.isArray(d)?d:[d];u.value.push(...g),R.push(...g)};return{requestState:a,processingState:l,messages:u,responseProvider:p,isProcessing:m,sendMessage:h,send:T,abortRequest:w}};var oe=require("vue");function Te(o,t=200,e=!1,s=!0,n=!1){return Fe(Ke(t,e,s,n),o)}function Fe(o,t){function e(...s){return new Promise((n,r)=>{Promise.resolve(o(()=>t.apply(this,s),{fn:t,thisArg:this,args:s})).then(n).catch(r)})}return e}var we=()=>{};function Ke(...o){let t=0,e,s=!0,n=we,r,a,l,u,p;!(0,oe.isRef)(o[0])&&typeof o[0]=="object"?{delay:a,trailing:l=!0,leading:u=!0,rejectOnCancel:p=!1}=o[0]:[a,l=!0,u=!0,p=!1]=o;let v=()=>{e&&(clearTimeout(e),e=void 0,n(),n=we)};return x=>{let A=(0,oe.toValue)(a),b=Date.now()-t,m=()=>r=x();return v(),A<=0?(t=Date.now(),m()):(b>A?(t=Date.now(),(u||!s)&&m()):l&&(r=new Promise((h,T)=>{n=p?T:h,e=setTimeout(()=>{t=Date.now(),s=!0,h(m()),v()},Math.max(0,A-b))})),!u&&!e&&(e=setTimeout(()=>s=!0,A)),s=!1,r)}}var je=o=>{let t=o.storage||Z(),e=(0,K.ref)([]),s=new Map,n=new Map,r=(0,K.ref)(null),a=(0,K.computed)(()=>{let i=r.value;if(!i)return null;let c=e.value.find(y=>y.id===i);if(!c)return null;let f=s.get(i);return f?{...c,engine:f}:null}),l=i=>{if(!t?.saveMessages)return;let c=i||r.value,f=e.value.find(S=>S.id===c);if(!f)return;f.updatedAt=Date.now(),t?.saveConversation?.(f);let y=s.get(f.id);y&&t.saveMessages(f.id,y.messages.value)},u=(i,c)=>{if(!o.autoSaveMessages||!t?.saveMessages)return;let f=n.get(i);f&&f();let y=o.autoSaveThrottle??1e3,S=Te(()=>{l(i)},y,!0,!0),w=(0,K.watch)(c.messages,S,{deep:!0});n.set(i,w)},p=i=>{let c=n.get(i);c&&(c(),n.delete(i))};t?.loadConversations&&Promise.resolve(t.loadConversations()).then(i=>{e.value=i}).catch(i=>{console.error("[useConversation] loadConversations failed:",i)});let v=async(i,c)=>{let f=s.get(i);if(f)return f;let y=c?.initialMessages??o.useMessageOptions.initialMessages??[];if(t?.loadMessages)try{y=await t.loadMessages(i)}catch(w){console.error("[useConversation] loadMessages failed:",w)}let S=te({...o.useMessageOptions,...c,initialMessages:y});return s.set(i,S),u(i,S),S};function R(){return Date.now().toString(36)+Math.random().toString(36).substring(2,9)}let x=i=>{let{id:c=R(),title:f,metadata:y,useMessageOptions:S}=i||{},w=Date.now(),q={id:c,title:f,createdAt:w,updatedAt:w,metadata:y};e.value.unshift(q);let I=te({...o.useMessageOptions,...S});return s.set(c,I),u(c,I),t?.saveConversation?.(q),t?.saveMessages?.(c,I.messages.value),r.value=c,a.value},A=i=>{let{excludeId:c}=i||{};s.forEach((f,y)=>{if(c&&y===c)return;f.isProcessing?.value||(p(y),s.delete(y))})};return{conversations:e,activeConversationId:r,activeConversation:a,createConversation:x,switchConversation:async i=>i?r.value===i?a.value:e.value.find(f=>f.id===i)?(await v(i),A({excludeId:i}),r.value=i,a.value):null:null,deleteConversation:async i=>{let c=e.value.findIndex(y=>y.id===i);if(c===-1)return;await s.get(i)?.abortRequest(),p(i),s.delete(i),e.value.splice(c,1),t?.deleteConversation?.(i),r.value===i&&(r.value=null,A())},clear:()=>{e.value.map(c=>c.id).forEach(c=>{t?.deleteConversation?.(c)}),s.forEach(c=>{c.abortRequest()}),n.forEach(c=>{c()}),e.value=[],s.clear(),n.clear(),r.value=null},updateConversationTitle:(i,c)=>{let f=e.value.find(y=>y.id===i);f&&(f.title=c,f.updatedAt=Date.now(),t?.saveConversation?.(f))},saveMessages:l,sendMessage:i=>{a.value?.engine.sendMessage(i)},abortActiveRequest:async()=>{await a.value?.engine.abortRequest()}}};0&&(module.exports={AIClient,EXCLUDE_MODE_REMOVE,ErrorType,IndexedDBStrategy,LocalStorageStrategy,StreamEventType,fallbackRolePlugin,indexedDBStorageStrategyFactory,lengthPlugin,localStorageStrategyFactory,sseStreamToGenerator,thinkingPlugin,toolPlugin,useConversation,useMessage});
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
`);a=w.pop()||"";for(let x of w)if(x.trim()!==""&&x.startsWith("data: ")){let S=x.slice(6);if(S==="[DONE]")return;try{yield JSON.parse(S)}catch(b){console.warn("Failed to parse SSE data:",S,b)}}}}finally{n?.removeEventListener("abort",i),t.releaseLock()}}import{computed as ve,ref as oe,watch as Te}from"vue";import{computed as fe,reactive as de,ref as K,watch as pe}from"vue";var J=(e={})=>{let{fallbackRole:s="assistant",...t}=e;return{name:"fallbackRole",...t,onBeforeRequest(n){let{requestBody:r,messages:a}=n;return r.messages=a.map(i=>({...i,role:i.role||s})),t.onBeforeRequest?.(n)}}};var X=(e={})=>{let{continueContent:s="Please continue with your previous answer.",...t}=e;return{name:"length",...t,onAfterRequest:async n=>{let{lastChoice:r,appendMessage:a,requestNext:i}=n;return r?.finish_reason==="length"&&(a({role:"user",content:s}),i()),t.onAfterRequest?.(n)}}};var $=(e={})=>({name:"thinking",...e,onCompletionChunk(s){let{choice:t,currentMessage:n}=s,a=typeof(t?.message?.reasoning_content||t?.delta?.reasoning_content)=="string";return n.state?n.state.thinking=a:n.state={thinking:a},e.onCompletionChunk?.(s)},onTurnEnd(s){let t=s.currentTurn.slice(-1)[0];return t?.state&&(t.state.thinking=void 0),e.onTurnEnd?.(s)}});import{reactive as le}from"vue";var N=class extends Error{constructor(s){super(s),this.name="AbortError"}};function ie(e){if(e.aborted)return{promise:Promise.reject(new N(String(e.reason??"Aborted"))),cleanup:()=>{}};let s=null;return{promise:new Promise((r,a)=>{s=()=>{a(new N(String(e.reason??"Aborted")))},e.addEventListener("abort",s,{once:!0})}),cleanup:()=>{s&&(e.removeEventListener("abort",s),s=null)}}}function Z(e,s){let{promise:t,cleanup:n}=ie(s);return Promise.race([e,t]).finally(n)}function ee(e,s){let t={};for(let n in e)s.includes(n)&&(t[n]=e[n]);return t}function te(e,s){let t={};for(let n in e)s.includes(n)||(t[n]=e[n]);return t}async function*W(e){if(Q(e)){yield*e;return}let s=await e;if(Q(s)){yield*s;return}yield s}function Q(e){return e&&typeof e=="object"&&typeof e[Symbol.asyncIterator]=="function"}var V=e=>typeof e=="object"&&e!==null,Y=e=>V(e)&&typeof e.index=="number",G=(e,s)=>{for(let[t,n]of Object.entries(s)){let r=e[t];if(r)if(typeof r=="string"&&typeof n=="string")t==="type"&&r||(e[t]=r+n);else if(Array.isArray(r)&&Array.isArray(n))if(r.every(a=>Y(a))&&n.every(a=>Y(a))){let a=new Map(r.map(f=>[f.index,f])),i=new Map(n.map(f=>[f.index,f]));for(let[f,v]of i)if(a.has(f)){let w=a.get(f);a.set(f,G(w,v))}else a.set(f,v);let p=Math.max(...Array.from(a.keys()),-1)+1,g=p>r.length?Array.from({length:p}):r;for(let[f,v]of a)g[f]=v;e[t]=g}else e[t]=[...r,...n];else V(r)&&V(n)&&(e[t]=G(r,n));else e[t]=n}return e};function ce(e,s){let t=[];for(let n=0;n<e.length;n++){let r=e[n];if(r.role==="assistant"&&r.tool_calls&&r.tool_calls.length>0){let a=new Set(r.tool_calls.map(g=>g.id)),i=new Set;for(let g=n+1;g<e.length;g++){let f=e[g];f.role==="tool"&&f.tool_call_id&&a.has(f.tool_call_id)&&i.add(f.tool_call_id)}let p=r.tool_calls.map(g=>g.id).filter(g=>!i.has(g));p.length>0&&t.push({insertAfterIndex:n,missingToolCallIds:p})}}for(let n=t.length-1;n>=0;n--){let{insertAfterIndex:r,missingToolCallIds:a}=t[n],i=a.map(p=>({role:"tool",tool_call_id:p,content:s}));e.splice(r+1,0,...i)}}var ue="remove";function ge(e,s,t,n){let r=e.filter(i=>i[t]),a=new Set(r.flatMap(i=>i.tool_calls?.map(p=>p.id)??[]));if(s===ue)for(let i=e.length-1;i>=0;i--){let p=e[i];(p[t]||p[n]||p.tool_call_id&&a.has(p.tool_call_id))&&e.splice(i,1)}else if(s===!0)for(let i of e)(i[t]||i.tool_call_id&&a.has(i.tool_call_id))&&(i[n]=!0,delete i[t])}var Se=e=>{let{getTools:s,beforeCallTools:t,callTool:n,onToolCallStart:r,onToolCallEnd:a,toolCallCancelledContent:i="Tool call cancelled.",toolCallFailedContent:p="Tool call failed.",autoFillMissingToolMessages:g=!1,excludeToolMessagesNextTurn:f=!1,...v}=e,w=Symbol("doNotSendNextTurn"),x=Symbol("doNotSend"),S=(...m)=>{let[h,{primaryMessage:A}]=m;A.state.toolCall[h.id].status="running",r?.(...m)},b=(...m)=>{let[h,{status:A,primaryMessage:o}]=m;o.state.toolCall[h.id].status=A,a?.(...m)};return{name:"tool",...v,onTurnStart:m=>{let{messages:h}=m;return g&&ce(h,i),f&&ge(h,f,w,x),v.onTurnStart?.(m)},onBeforeRequest:async m=>{let{messages:h,requestBody:A}=m;f===!0&&(A.messages=h.filter(c=>!c[x]));let o=await s?.();return o&&o.length>0&&(A.tools=o),v.onBeforeRequest?.(m)},onAfterRequest:async m=>{let{currentMessage:h,lastChoice:A,appendMessage:o,abortSignal:c,setRequestState:d,requestNext:y}=m;if(A?.finish_reason!=="tool_calls"||!h.tool_calls?.length)return;f&&(h[w]=!0),d("processing","calling-tools"),await t?.(h.tool_calls,{...m,currentMessage:h});let T=h.tool_calls.map(async R=>{let F=Math.floor(Date.now()/1e3),E=le({role:"tool",tool_call_id:R.id,content:"",metadata:{createdAt:F,updatedAt:F}});o(E);let U={...m,primaryMessage:h,toolMessage:E};S(R,U);try{let q=n(R,U),l=W(q);for await(let u of l){if(typeof u=="string")E.content+=u;else{let M={};try{M=JSON.parse(E.content||"{}")}catch(P){console.warn(P)}E.content=JSON.stringify(G(M,u))}E.metadata.updatedAt=Math.floor(Date.now()/1e3)}b(R,{...U,status:"success"})}catch(q){let l=q instanceof Error?q:new Error(String(q));if(c.aborted){b(R,{...U,status:"cancelled",error:l});return}console.error(q),E.content.length===0&&(E.content=p),b(R,{...U,status:"failed",error:l})}});return await Promise.all(T),y(),v.onAfterRequest?.(m)},onCompletionChunk:m=>{var A,o,c;let{currentMessage:h}=m;if(Array.isArray(h.tool_calls))for(let d of h.tool_calls)h.state?.toolCall?.[d.id]?.status||(h.state??(h.state={}),(A=h.state).toolCall??(A.toolCall={}),(o=h.state.toolCall)[c=d.id]??(o[c]={}));return v.onCompletionChunk?.(m)}}};var me=e=>{let s=[];for(let t of e){if(t.name){let n=s.findIndex(r=>r.name===t.name);n!==-1&&s.splice(n,1)}s.push(t)}return s},z=e=>{let{initialMessages:s=[],requestMessageFields:t=[],requestMessageFieldsExclude:n=["state","metadata","loading"],plugins:r=[],onCompletionChunk:a}=e,i=K("idle"),p=K(void 0),g=K(s),f=K(e.responseProvider),v=null,w=[],x={},S=[J(),$(),X()],b=me(S.concat(r)),m=fe(()=>i.value==="processing"),h=async l=>{if(!l||!l.trim()){console.warn("Cannot send empty message");return}if(m.value){console.warn("Cannot send message while processing is in progress");return}let u=Math.floor(Date.now()/1e3);g.value.push({role:"user",content:l.trim(),metadata:{createdAt:u,updatedAt:u}}),w.push(g.value[g.value.length-1]),await F()},A=async(...l)=>{if(m.value){console.warn("Cannot send message while processing is in progress");return}g.value.push(...l),w.push(...l),await F()},o=l=>{let u=l;return t.length&&(u=u.map(M=>ee(M,t))),n.length&&(u=u.map(M=>te(M,n))),u},c=(l,u)=>{i.value=l,l==="processing"?p.value=u||"requesting":p.value=void 0},d=l=>{Object.assign(x,l)},y=l=>({messages:g.value,currentTurn:w,requestState:i.value,processingState:p.value,plugins:b,abortSignal:l,setRequestState:c,customContext:x,setCustomContext:d}),T=(l,u)=>typeof l.disabled=="function"?l.disabled(u):!!l.disabled,R=async(l,u)=>{c("processing","requesting");let M=new Proxy({messages:o(g.value)},{set(_,O,I){return O==="messages"?(_.messages=o(I),!0):(_[O]=I,!0)}}),P=y(u);for(let _ of b.filter(O=>!T(O,P)))await _.onBeforeRequest?.({...P,requestBody:M});let C=de({role:"",content:"",loading:!0});q(C);let k,L=l(M,u),D=W(L);for await(let _ of D){c("processing","completing"),C.loading&&(C.loading=void 0);let O=_.choices?.find(B=>B.index===0);if(O){k=O;let B=()=>{C.metadata||(C.metadata={});let{created:j,...re}=_;C.metadata.createdAt=j,C.metadata.updatedAt=Math.floor(Date.now()/1e3),Object.assign(C.metadata,re),G(C,O.message||O.delta)};if(a){let j=y(u);a({...j,chunk:_,choice:O,currentMessage:C},B)}else B()}let I=y(u);for(let B of b.filter(j=>!T(j,I)))B.onCompletionChunk?.({...I,abortSignal:u,chunk:_,choice:O,currentMessage:C})}await U(C,l,u,k)},F=async()=>{let l=new AbortController;v=l,x={};try{c("processing","requesting");let u=y(l.signal);for(let C of b.filter(k=>!T(k,u)))await C.onTurnStart?.(u);let M=f.value;try{await R(M,l.signal),c("completed")}catch(C){if(l.signal.aborted||C instanceof N||C instanceof Error&&C.name==="AbortError")c("aborted");else throw C}let P=y(l.signal);for(let C of b.filter(k=>!T(k,P)))await C.onTurnEnd?.(P)}catch(u){c("error");let M=!1,P=y(l.signal);for(let C of b.filter(k=>!T(k,P)))M=!0,C.onError?.({...P,error:u});if(!M)throw u}finally{let u=y(l.signal);for(let M of b.filter(P=>!T(P,u)))try{M.onFinally?.(u)}catch(P){console.error(`Error in onFinally hook for plugin ${M.name||"Anonymous"}:`,P)}v=null,w.slice(-1)[0]&&(w.slice(-1)[0].loading=void 0),w=[]}},E=async()=>{v?.abort(),m.value&&await new Promise(l=>{let u=pe(m,M=>{M||(u(),l())},{immediate:!0})})},U=async(l,u,M,P)=>{let C=!1,k=y(M),L=b.filter(D=>!T(D,k)).map(D=>{if(!D.onAfterRequest)return null;let _=I=>{q(I)},O=()=>{C=!0};return D.onAfterRequest({...k,currentMessage:l,lastChoice:P,appendMessage:_,requestNext:O})}).filter(D=>D!==null);await Z(Promise.all(L),M),C&&await R(u,M)},q=l=>{let u=Array.isArray(l)?l:[l];g.value.push(...u),w.push(...u)};return{requestState:i,processingState:p,messages:g,responseProvider:f,isProcessing:m,sendMessage:h,send:A,abortRequest:E}};import{isRef as he,toValue as ye}from"vue";function se(e,s=200,t=!1,n=!0,r=!1){return Ce(Me(s,t,n,r),e)}function Ce(e,s){function t(...n){return new Promise((r,a)=>{Promise.resolve(e(()=>s.apply(this,n),{fn:s,thisArg:this,args:n})).then(r).catch(a)})}return t}var ne=()=>{};function Me(...e){let s=0,t,n=!0,r=ne,a,i,p,g,f;!he(e[0])&&typeof e[0]=="object"?{delay:i,trailing:p=!0,leading:g=!0,rejectOnCancel:f=!1}=e[0]:[i,p=!0,g=!0,f=!1]=e;let v=()=>{t&&(clearTimeout(t),t=void 0,r(),r=ne)};return x=>{let S=ye(i),b=Date.now()-s,m=()=>a=x();return v(),S<=0?(s=Date.now(),m()):(b>S?(s=Date.now(),(g||!n)&&m()):p&&(a=new Promise((h,A)=>{r=f?A:h,t=setTimeout(()=>{s=Date.now(),n=!0,h(m()),v()},Math.max(0,S-b))})),!g&&!t&&(t=setTimeout(()=>n=!0,S)),n=!1,a)}}var We=e=>{let s=oe([]),t=new Map,n=new Map,r=oe(null),a=ve(()=>{let o=r.value;if(!o)return null;let c=s.value.find(y=>y.id===o);if(!c)return null;let d=t.get(o);return d?{...c,engine:d}:null}),i=o=>{if(!e.storage?.saveMessages)return;let c=o||r.value,d=s.value.find(T=>T.id===c);if(!d)return;d.updatedAt=Date.now(),e.storage?.saveConversation?.(d);let y=t.get(d.id);y&&e.storage.saveMessages(d.id,y.messages.value)},p=(o,c)=>{if(!e.autoSaveMessages||!e.storage?.saveMessages)return;let d=n.get(o);d&&d();let y=e.autoSaveThrottle??1e3,T=se(()=>{i(o)},y,!0,!0),R=Te(c.messages,T,{deep:!0});n.set(o,R)},g=o=>{let c=n.get(o);c&&(c(),n.delete(o))};e.storage?.loadConversations&&Promise.resolve(e.storage.loadConversations()).then(o=>{s.value=o}).catch(o=>{console.error("[useConversation] loadConversations failed:",o)});let f=async(o,c)=>{let d=t.get(o);if(d)return d;let y=c?.initialMessages??e.useMessageOptions.initialMessages??[];if(e.storage?.loadMessages)try{y=await e.storage.loadMessages(o)}catch(R){console.error("[useConversation] loadMessages failed:",R)}let T=z({...e.useMessageOptions,...c,initialMessages:y});return t.set(o,T),p(o,T),T};function v(){return Date.now().toString(36)+Math.random().toString(36).substring(2,9)}let w=o=>{let{id:c=v(),title:d,metadata:y,useMessageOptions:T}=o||{},R=Date.now(),F={id:c,title:d,createdAt:R,updatedAt:R,metadata:y};s.value.unshift(F);let E=z({...e.useMessageOptions,...T});return t.set(c,E),p(c,E),e.storage?.saveConversation?.(F),e.storage?.saveMessages?.(c,E.messages.value),r.value=c,a.value},x=o=>{let{excludeId:c}=o||{};t.forEach((d,y)=>{if(c&&y===c)return;d.isProcessing?.value||(g(y),t.delete(y))})};return{conversations:s,activeConversationId:r,activeConversation:a,createConversation:w,switchConversation:async o=>o?r.value===o?a.value:s.value.find(d=>d.id===o)?(await f(o),x({excludeId:o}),r.value=o,a.value):null:null,deleteConversation:async o=>{let c=s.value.findIndex(y=>y.id===o);if(c===-1)return;await t.get(o)?.abortRequest(),g(o),t.delete(o),s.value.splice(c,1),e.storage?.deleteConversation?.(o),r.value===o&&(r.value=null,x())},updateConversationTitle:(o,c)=>{let d=s.value.find(y=>y.id===o);d&&(d.title=c,d.updatedAt=Date.now(),e.storage?.saveConversation?.(d))},saveMessages:i,sendMessage:o=>{a.value?.engine.sendMessage(o)},abortActiveRequest:async()=>{await a.value?.engine.abortRequest()}}};export{ue as EXCLUDE_MODE_REMOVE,ae as createSSEStreamGenerator,J as fallbackRolePlugin,X as lengthPlugin,$ as thinkingPlugin,Se as toolPlugin,We as useConversation,z as useMessage};
|
|
1
|
+
var J=class{constructor(t){this.config=t}updateConfig(t){this.config={...this.config,...t}}getConfig(){return{...this.config}}validateRequest(t){if(!t.messages||!Array.isArray(t.messages)||t.messages.length===0)throw new Error("\u8BF7\u6C42\u5FC5\u987B\u5305\u542B\u81F3\u5C11\u4E00\u6761\u6D88\u606F");for(let e of t.messages)if(!e.role||!e.content)throw new Error("\u6BCF\u6761\u6D88\u606F\u5FC5\u987B\u5305\u542B\u89D2\u8272\u548C\u5185\u5BB9")}};var Ce=(l=>(l.NETWORK_ERROR="network_error",l.AUTHENTICATION_ERROR="authentication_error",l.RATE_LIMIT_ERROR="rate_limit_error",l.SERVER_ERROR="server_error",l.MODEL_ERROR="model_error",l.TIMEOUT_ERROR="timeout_error",l.UNKNOWN_ERROR="unknown_error",l))(Ce||{}),ve=(s=>(s.DATA="data",s.ERROR="error",s.DONE="done",s))(ve||{});function N(o){return{type:o.type||"unknown_error",message:o.message||"\u672A\u77E5\u9519\u8BEF",statusCode:o.statusCode,originalError:o.originalError}}function Z(o){if(!o.response)return N({type:"network_error",message:"\u7F51\u7EDC\u8FDE\u63A5\u9519\u8BEF\uFF0C\u8BF7\u68C0\u67E5\u60A8\u7684\u7F51\u7EDC\u8FDE\u63A5",originalError:o});if(o.response){let{status:t,data:e}=o.response;return t===401||t===403?N({type:"authentication_error",message:"\u8EAB\u4EFD\u9A8C\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u60A8\u7684API\u5BC6\u94A5",statusCode:t,originalError:o}):t===429?N({type:"rate_limit_error",message:"\u8D85\u51FAAPI\u8C03\u7528\u9650\u5236\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",statusCode:t,originalError:o}):t>=500?N({type:"server_error",message:"\u670D\u52A1\u5668\u9519\u8BEF\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",statusCode:t,originalError:o}):N({type:"unknown_error",message:e?.error?.message||`\u8BF7\u6C42\u5931\u8D25\uFF0C\u72B6\u6001\u7801: ${t}`,statusCode:t,originalError:o})}return o.code==="ECONNABORTED"?N({type:"timeout_error",message:"\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",originalError:o}):N({type:"unknown_error",message:o.message||"\u53D1\u751F\u672A\u77E5\u9519\u8BEF",originalError:o})}async function re(o,t,e){let s=o.body?.getReader();if(!s)throw new Error("Response body is null");let n=new TextDecoder,r="",a,l;e&&e.addEventListener("abort",()=>{s.cancel().catch(u=>console.error("Error cancelling reader:",u))},{once:!0});try{for(;;){if(e?.aborted){await s.cancel();break}let{done:u,value:p}=await s.read();if(u)break;let v=n.decode(p,{stream:!0});r+=v;let R=r.split(`
|
|
2
|
+
|
|
3
|
+
`);r=R.pop()||"";for(let x of R)if(x.trim()!==""){if(x.trim()==="data: [DONE]"){l&&(a=l),t.onDone(a);continue}try{let A=x.match(/^data: (.+)$/m);if(!A)continue;let b=JSON.parse(A[1]);t.onData(b),l=b.choices?.[0]?.finish_reason||void 0}catch(A){console.error("Error parsing SSE message:",A)}}}(r.trim()==="data: [DONE]"||e?.aborted)&&(e?.aborted&&(a="aborted"),t.onDone(a))}catch(u){if(e?.aborted)return;throw u}}function ee(o="The operation was aborted"){let t=new Error(o);return t.name="AbortError",t}async function*Me(o,t={}){let e=o.body?.getReader();if(!e)throw new Error("ReadableStream not supported");let{signal:s}=t,n=new TextDecoder,r="",a=()=>{e.cancel()};s?.addEventListener("abort",a);try{for(;;){if(s?.aborted)throw ee();let l;try{l=await e.read()}catch(x){throw s?.aborted?ee():x}let{done:u,value:p}=l;if(u){if(s?.aborted)throw ee();return}let v=n.decode(p,{stream:!0});r+=v;let R=r.split(`
|
|
4
|
+
`);r=R.pop()||"";for(let x of R)if(x.trim()!==""&&x.startsWith("data: ")){let A=x.slice(6);if(A==="[DONE]")return;try{yield JSON.parse(A)}catch(b){console.warn("Failed to parse SSE data:",A,b)}}}}finally{s?.removeEventListener("abort",a),e.releaseLock()}}var W=class extends J{constructor(e){super(e);this.defaultModel="gpt-3.5-turbo";this.baseURL=e.apiUrl||"https://api.openai.com/v1",this.apiKey=e.apiKey||"",e.defaultModel&&(this.defaultModel=e.defaultModel),this.apiKey||console.warn("API key is not provided. Authentication will likely fail.")}async chat(e){try{this.validateRequest(e);let s={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...e.options,stream:!1},n={method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)};this.apiKey&&Object.assign(n.headers,{Authorization:`Bearer ${this.apiKey}`});let r=await fetch(`${this.baseURL}/chat/completions`,n);if(!r.ok){let a=await r.text();throw new Error(`HTTP error! status: ${r.status}, details: ${a}`)}return await r.json()}catch(s){throw Z(s)}}async chatStream(e,s){let{signal:n,...r}=e.options||{};try{this.validateRequest(e);let a={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...r,stream:!0},l={method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,Accept:"text/event-stream"},body:JSON.stringify(a),signal:n};this.apiKey&&Object.assign(l.headers,{Authorization:`Bearer ${this.apiKey}`});let u=await fetch(`${this.baseURL}/chat/completions`,l);if(!u.ok){let p=await u.text();throw new Error(`HTTP error! status: ${u.status}, details: ${p}`)}await re(u,s,n)}catch(a){if(n?.aborted)return;s.onError(Z(a))}}updateConfig(e){super.updateConfig(e),e.apiUrl&&(this.baseURL=e.apiUrl),e.apiKey&&(this.apiKey=e.apiKey),e.defaultModel&&(this.defaultModel=e.defaultModel)}};var te=class{constructor(t){this.config=t,this.provider=this.createProvider(t)}createProvider(t){if(t.provider==="custom"&&"providerImplementation"in t)return t.providerImplementation;if(t.provider==="deepseek"){let e={defaultModel:"deepseek-chat",apiUrl:"https://api.deepseek.com/v1"};return new W({...e,...t})}else return new W(t)}async chat(t){return this.provider.chat(t)}async chatStream(t,e){let s={...t,options:{...t.options,stream:!0}};return this.provider.chatStream(s,e)}getConfig(){return{...this.config}}updateConfig(t){this.config={...this.config,...t},t.provider&&t.provider!==this.config.provider?this.provider=this.createProvider(this.config):this.provider.updateConfig(this.config)}};import{toRaw as be}from"vue";function H(o,t=new WeakSet){if(o==null||typeof o!="object")return o;if(t.has(o))return Array.isArray(o)?[]:{};t.add(o);try{let e=be(o);if(Array.isArray(e))return e.map(n=>H(n,t));if(e instanceof Date||e instanceof RegExp||e instanceof ArrayBuffer||e instanceof Blob)return e;let s={};for(let n in e){if(typeof n=="symbol")continue;let r=Object.getOwnPropertyDescriptor(e,n);if(!r||r.get||r.set)continue;let a=e[n];typeof a!="function"&&typeof a!="symbol"&&(s[n]=H(a,t))}return s}catch(e){return console.warn("unwrapProxy error:",e),Array.isArray(o)?[]:{}}}var z=o=>o.map(t=>{let{renderContent:e,...s}=t;if(!Array.isArray(e))return t;let n=e.filter(a=>a.type==="collapsible-text"),r=e.filter(a=>a.type==="markdown"||a.type==="text");return n.length>0&&(s.reasoning_content=n.map(a=>a.content).join("")),r.length>0&&(s.content=r.map(a=>a.content).join("")),s});var G=o=>{let t=localStorage.getItem(o);return t?JSON.parse(t):[]},V=class{constructor(t="tiny-robot-ai-conversations"){this.storageKey=t}saveConversation(t){try{let e=G(this.storageKey),s=e.findIndex(n=>n.id===t.id);s!==-1?Object.assign(e[s],t):e.unshift({...t,messages:[]}),localStorage.setItem(this.storageKey,JSON.stringify(e))}catch(e){console.error("\u4FDD\u5B58\u4F1A\u8BDD\u5931\u8D25:",e)}}loadConversations(){try{return G(this.storageKey).map(e=>({id:e.id,title:e.title,createdAt:e.createdAt,updatedAt:e.updatedAt,metadata:e.metadata}))}catch(t){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u5931\u8D25:",t),[]}}saveMessages(t,e){try{let s=G(this.storageKey),n=s.findIndex(r=>r.id===t);n!==-1&&(s[n].messages=e),localStorage.setItem(this.storageKey,JSON.stringify(s))}catch(s){console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",s)}}loadMessages(t){try{let s=G(this.storageKey).find(r=>r.id===t);return z(s?.messages||[])}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}deleteConversation(t){let e=G(this.storageKey),s=e.findIndex(n=>n.id===t);s!==-1&&e.splice(s,1),localStorage.setItem(this.storageKey,JSON.stringify(e))}};import{openDB as Re}from"idb";var $=class{constructor(t="tiny-robot-ai-db",e=3){this.db=null;this.dbName=t,this.dbVersion=e}async getDB(){return this.db||(this.db=await Re(this.dbName,this.dbVersion,{upgrade(t){t.objectStoreNames.contains("conversations")||t.createObjectStore("conversations",{keyPath:"id"}).createIndex("by-updated","updatedAt"),t.objectStoreNames.contains("messages")||t.createObjectStore("messages",{keyPath:"conversationId"})}})),this.db}async loadConversations(){try{return(await(await this.getDB()).getAllFromIndex("conversations","by-updated")).reverse()}catch(t){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u5931\u8D25:",t),[]}}async loadMessages(t){try{let s=await(await this.getDB()).get("messages",t);return s?z(s.messages):[]}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}async saveConversation(t){try{let e=await this.getDB(),s=H(t);await e.put("conversations",s)}catch(e){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u5931\u8D25:",e),e}}async saveMessages(t,e){try{let s=await this.getDB(),n=H(e);await s.put("messages",{conversationId:t,messages:n})}catch(s){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",s),s}}async deleteConversation(t){try{let e=await this.getDB();await e.delete("conversations",t),await e.delete("messages",t)}catch(e){throw console.error("\u5220\u9664\u4F1A\u8BDD\u5931\u8D25:",e),e}}};function oe(o={}){return new V(o.key||"tiny-robot-ai-conversations")}function xe(o={}){return new $(o.dbName||"tiny-robot-ai-db",o.dbVersion||1)}import{computed as Be,ref as he,watch as Ne}from"vue";import{computed as Ee,reactive as Oe,ref as Q,watch as Ie}from"vue";var ae=(o={})=>{let{fallbackRole:t="assistant",...e}=o;return{name:"fallbackRole",...e,onBeforeRequest(s){let{requestBody:n,messages:r}=s;return n.messages=r.map(a=>({...a,role:a.role||t})),e.onBeforeRequest?.(s)}}};var ie=(o={})=>{let{continueContent:t="Please continue with your previous answer.",...e}=o;return{name:"length",...e,onAfterRequest:async s=>{let{lastChoice:n,appendMessage:r,requestNext:a}=s;return n?.finish_reason==="length"&&(r({role:"user",content:t}),a()),e.onAfterRequest?.(s)}}};var le=(o={})=>({name:"thinking",...o,onCompletionChunk(t){let{choice:e,currentMessage:s}=t,r=typeof(e?.message?.reasoning_content||e?.delta?.reasoning_content)=="string";return s.state?s.state.thinking=r:s.state={thinking:r},o.onCompletionChunk?.(t)},onTurnEnd(t){let e=t.currentTurn.slice(-1)[0];return e?.state&&(e.state.thinking=void 0),o.onTurnEnd?.(t)}});import{reactive as we}from"vue";var K=class extends Error{constructor(t){super(t),this.name="AbortError"}};function Ae(o){if(o.aborted)return{promise:Promise.reject(new K(String(o.reason??"Aborted"))),cleanup:()=>{}};let t=null;return{promise:new Promise((n,r)=>{t=()=>{r(new K(String(o.reason??"Aborted")))},o.addEventListener("abort",t,{once:!0})}),cleanup:()=>{t&&(o.removeEventListener("abort",t),t=null)}}}function de(o,t){let{promise:e,cleanup:s}=Ae(t);return Promise.race([o,e]).finally(s)}function ge(o,t){let e={};for(let s in o)t.includes(s)&&(e[s]=o[s]);return e}function pe(o,t){let e={};for(let s in o)t.includes(s)||(e[s]=o[s]);return e}async function*X(o){if(ce(o)){yield*o;return}let t=await o;if(ce(t)){yield*t;return}yield t}function ce(o){return o&&typeof o=="object"&&typeof o[Symbol.asyncIterator]=="function"}var se=o=>typeof o=="object"&&o!==null,ue=o=>se(o)&&typeof o.index=="number",j=(o,t)=>{for(let[e,s]of Object.entries(t)){let n=o[e];if(n)if(typeof n=="string"&&typeof s=="string")e==="type"&&n||(o[e]=n+s);else if(Array.isArray(n)&&Array.isArray(s))if(n.every(r=>ue(r))&&s.every(r=>ue(r))){let r=new Map(n.map(p=>[p.index,p])),a=new Map(s.map(p=>[p.index,p]));for(let[p,v]of a)if(r.has(p)){let R=r.get(p);r.set(p,j(R,v))}else r.set(p,v);let l=Math.max(...Array.from(r.keys()),-1)+1,u=l>n.length?Array.from({length:l}):n;for(let[p,v]of r)u[p]=v;o[e]=u}else o[e]=[...n,...s];else se(n)&&se(s)&&(o[e]=j(n,s));else o[e]=s}return o};function Te(o,t){let e=[];for(let s=0;s<o.length;s++){let n=o[s];if(n.role==="assistant"&&n.tool_calls&&n.tool_calls.length>0){let r=new Set(n.tool_calls.map(u=>u.id)),a=new Set;for(let u=s+1;u<o.length;u++){let p=o[u];p.role==="tool"&&p.tool_call_id&&r.has(p.tool_call_id)&&a.add(p.tool_call_id)}let l=n.tool_calls.map(u=>u.id).filter(u=>!a.has(u));l.length>0&&e.push({insertAfterIndex:s,missingToolCallIds:l})}}for(let s=e.length-1;s>=0;s--){let{insertAfterIndex:n,missingToolCallIds:r}=e[s],a=r.map(l=>({role:"tool",tool_call_id:l,content:t}));o.splice(n+1,0,...a)}}var Se="remove";function Pe(o,t,e,s){let n=o.filter(a=>a[e]),r=new Set(n.flatMap(a=>a.tool_calls?.map(l=>l.id)??[]));if(t===Se)for(let a=o.length-1;a>=0;a--){let l=o[a];(l[e]||l[s]||l.tool_call_id&&r.has(l.tool_call_id))&&o.splice(a,1)}else if(t===!0)for(let a of o)(a[e]||a.tool_call_id&&r.has(a.tool_call_id))&&(a[s]=!0,delete a[e])}var mt=o=>{let{getTools:t,beforeCallTools:e,callTool:s,onToolCallStart:n,onToolCallEnd:r,toolCallCancelledContent:a="Tool call cancelled.",toolCallFailedContent:l="Tool call failed.",autoFillMissingToolMessages:u=!1,excludeToolMessagesNextTurn:p=!1,...v}=o,R=Symbol("doNotSendNextTurn"),x=Symbol("doNotSend"),A=(...m)=>{let[h,{primaryMessage:T}]=m;T.state.toolCall[h.id].status="running",n?.(...m)},b=(...m)=>{let[h,{status:T,primaryMessage:O}]=m;O.state.toolCall[h.id].status=T,r?.(...m)};return{name:"tool",...v,onTurnStart:m=>{let{messages:h}=m;return u&&Te(h,a),p&&Pe(h,p,R,x),v.onTurnStart?.(m)},onBeforeRequest:async m=>{let{messages:h,requestBody:T}=m;p===!0&&(T.messages=h.filter(E=>!E[x]));let O=await t?.();return O&&O.length>0&&(T.tools=O),v.onBeforeRequest?.(m)},onAfterRequest:async m=>{let{currentMessage:h,lastChoice:T,appendMessage:O,abortSignal:E,setRequestState:i,requestNext:c}=m;if(T?.finish_reason!=="tool_calls"||!h.tool_calls?.length)return;p&&(h[R]=!0),i("processing","calling-tools"),await e?.(h.tool_calls,{...m,currentMessage:h});let f=h.tool_calls.map(async y=>{let S=Math.floor(Date.now()/1e3),w=we({role:"tool",tool_call_id:y.id,content:"",metadata:{createdAt:S,updatedAt:S}});O(w);let U={...m,primaryMessage:h,toolMessage:w};A(y,U);try{let I=s(y,U),d=X(I);for await(let g of d){if(typeof g=="string")w.content+=g;else{let M={};try{M=JSON.parse(w.content||"{}")}catch(P){console.warn(P)}w.content=JSON.stringify(j(M,g))}w.metadata.updatedAt=Math.floor(Date.now()/1e3)}b(y,{...U,status:"success"})}catch(I){let d=I instanceof Error?I:new Error(String(I));if(E.aborted){b(y,{...U,status:"cancelled",error:d});return}console.error(I),w.content.length===0&&(w.content=l),b(y,{...U,status:"failed",error:d})}});return await Promise.all(f),c(),v.onAfterRequest?.(m)},onCompletionChunk:m=>{var T,O,E;let{currentMessage:h}=m;if(Array.isArray(h.tool_calls))for(let i of h.tool_calls)h.state?.toolCall?.[i.id]?.status||(h.state??(h.state={}),(T=h.state).toolCall??(T.toolCall={}),(O=h.state.toolCall)[E=i.id]??(O[E]={}));return v.onCompletionChunk?.(m)}}};var ke=o=>{let t=[];for(let e of o){if(e.name){let s=t.findIndex(n=>n.name===e.name);s!==-1&&t.splice(s,1)}t.push(e)}return t},ne=o=>{let{initialMessages:t=[],requestMessageFields:e=[],requestMessageFieldsExclude:s=["state","metadata","loading"],plugins:n=[],onCompletionChunk:r}=o,a=Q("idle"),l=Q(void 0),u=Q(t),p=Q(o.responseProvider),v=null,R=[],x={},A=[ae(),le(),ie()],b=ke(A.concat(n)),m=Ee(()=>a.value==="processing"),h=async d=>{if(!d||!d.trim()){console.warn("Cannot send empty message");return}if(m.value){console.warn("Cannot send message while processing is in progress");return}let g=Math.floor(Date.now()/1e3);u.value.push({role:"user",content:d.trim(),metadata:{createdAt:g,updatedAt:g}}),R.push(u.value[u.value.length-1]),await S()},T=async(...d)=>{if(m.value){console.warn("Cannot send message while processing is in progress");return}u.value.push(...d),R.push(...d),await S()},O=d=>{let g=d;return e.length&&(g=g.map(M=>ge(M,e))),s.length&&(g=g.map(M=>pe(M,s))),g},E=(d,g)=>{a.value=d,d==="processing"?l.value=g||"requesting":l.value=void 0},i=d=>{Object.assign(x,d)},c=d=>({messages:u.value,currentTurn:R,requestState:a.value,processingState:l.value,plugins:b,abortSignal:d,setRequestState:E,customContext:x,setCustomContext:i}),f=(d,g)=>typeof d.disabled=="function"?d.disabled(g):!!d.disabled,y=async(d,g)=>{E("processing","requesting");let M=new Proxy({messages:O(u.value)},{set(D,k,B){return k==="messages"?(D.messages=O(B),!0):(D[k]=B,!0)}}),P=c(g);for(let D of b.filter(k=>!f(k,P)))await D.onBeforeRequest?.({...P,requestBody:M});let C=Oe({role:"",content:"",loading:!0});I(C);let _,Y=d(M,g),q=X(Y);for await(let D of q){E("processing","completing"),C.loading&&(C.loading=void 0);let k=D.choices?.find(F=>F.index===0);if(k){_=k;let F=()=>{C.metadata||(C.metadata={});let{created:L,...ye}=D;C.metadata.createdAt=L,C.metadata.updatedAt=Math.floor(Date.now()/1e3),Object.assign(C.metadata,ye),j(C,k.message||k.delta)};if(r){let L=c(g);r({...L,chunk:D,choice:k,currentMessage:C},F)}else F()}let B=c(g);for(let F of b.filter(L=>!f(L,B)))F.onCompletionChunk?.({...B,abortSignal:g,chunk:D,choice:k,currentMessage:C})}await U(C,d,g,_)},S=async()=>{let d=new AbortController;v=d,x={};try{E("processing","requesting");let g=c(d.signal);for(let C of b.filter(_=>!f(_,g)))await C.onTurnStart?.(g);let M=p.value;try{await y(M,d.signal),E("completed")}catch(C){if(d.signal.aborted||C instanceof K||C instanceof Error&&C.name==="AbortError")E("aborted");else throw C}let P=c(d.signal);for(let C of b.filter(_=>!f(_,P)))await C.onTurnEnd?.(P)}catch(g){E("error");let M=!1,P=c(d.signal);for(let C of b.filter(_=>!f(_,P)))M=!0,C.onError?.({...P,error:g});if(!M)throw g}finally{let g=c(d.signal);for(let M of b.filter(P=>!f(P,g)))try{M.onFinally?.(g)}catch(P){console.error(`Error in onFinally hook for plugin ${M.name||"Anonymous"}:`,P)}v=null,R.slice(-1)[0]&&(R.slice(-1)[0].loading=void 0),R=[]}},w=async()=>{v?.abort(),m.value&&await new Promise(d=>{let g=Ie(m,M=>{M||(g(),d())},{immediate:!0})})},U=async(d,g,M,P)=>{let C=!1,_=c(M),Y=b.filter(q=>!f(q,_)).map(q=>{if(!q.onAfterRequest)return null;let D=B=>{I(B)},k=()=>{C=!0};return q.onAfterRequest({..._,currentMessage:d,lastChoice:P,appendMessage:D,requestNext:k})}).filter(q=>q!==null);await de(Promise.all(Y),M),C&&await y(g,M)},I=d=>{let g=Array.isArray(d)?d:[d];u.value.push(...g),R.push(...g)};return{requestState:a,processingState:l,messages:u,responseProvider:p,isProcessing:m,sendMessage:h,send:T,abortRequest:w}};import{isRef as _e,toValue as De}from"vue";function me(o,t=200,e=!1,s=!0,n=!1){return Ue(qe(t,e,s,n),o)}function Ue(o,t){function e(...s){return new Promise((n,r)=>{Promise.resolve(o(()=>t.apply(this,s),{fn:t,thisArg:this,args:s})).then(n).catch(r)})}return e}var fe=()=>{};function qe(...o){let t=0,e,s=!0,n=fe,r,a,l,u,p;!_e(o[0])&&typeof o[0]=="object"?{delay:a,trailing:l=!0,leading:u=!0,rejectOnCancel:p=!1}=o[0]:[a,l=!0,u=!0,p=!1]=o;let v=()=>{e&&(clearTimeout(e),e=void 0,n(),n=fe)};return x=>{let A=De(a),b=Date.now()-t,m=()=>r=x();return v(),A<=0?(t=Date.now(),m()):(b>A?(t=Date.now(),(u||!s)&&m()):l&&(r=new Promise((h,T)=>{n=p?T:h,e=setTimeout(()=>{t=Date.now(),s=!0,h(m()),v()},Math.max(0,A-b))})),!u&&!e&&(e=setTimeout(()=>s=!0,A)),s=!1,r)}}var Et=o=>{let t=o.storage||oe(),e=he([]),s=new Map,n=new Map,r=he(null),a=Be(()=>{let i=r.value;if(!i)return null;let c=e.value.find(y=>y.id===i);if(!c)return null;let f=s.get(i);return f?{...c,engine:f}:null}),l=i=>{if(!t?.saveMessages)return;let c=i||r.value,f=e.value.find(S=>S.id===c);if(!f)return;f.updatedAt=Date.now(),t?.saveConversation?.(f);let y=s.get(f.id);y&&t.saveMessages(f.id,y.messages.value)},u=(i,c)=>{if(!o.autoSaveMessages||!t?.saveMessages)return;let f=n.get(i);f&&f();let y=o.autoSaveThrottle??1e3,S=me(()=>{l(i)},y,!0,!0),w=Ne(c.messages,S,{deep:!0});n.set(i,w)},p=i=>{let c=n.get(i);c&&(c(),n.delete(i))};t?.loadConversations&&Promise.resolve(t.loadConversations()).then(i=>{e.value=i}).catch(i=>{console.error("[useConversation] loadConversations failed:",i)});let v=async(i,c)=>{let f=s.get(i);if(f)return f;let y=c?.initialMessages??o.useMessageOptions.initialMessages??[];if(t?.loadMessages)try{y=await t.loadMessages(i)}catch(w){console.error("[useConversation] loadMessages failed:",w)}let S=ne({...o.useMessageOptions,...c,initialMessages:y});return s.set(i,S),u(i,S),S};function R(){return Date.now().toString(36)+Math.random().toString(36).substring(2,9)}let x=i=>{let{id:c=R(),title:f,metadata:y,useMessageOptions:S}=i||{},w=Date.now(),U={id:c,title:f,createdAt:w,updatedAt:w,metadata:y};e.value.unshift(U);let I=ne({...o.useMessageOptions,...S});return s.set(c,I),u(c,I),t?.saveConversation?.(U),t?.saveMessages?.(c,I.messages.value),r.value=c,a.value},A=i=>{let{excludeId:c}=i||{};s.forEach((f,y)=>{if(c&&y===c)return;f.isProcessing?.value||(p(y),s.delete(y))})};return{conversations:e,activeConversationId:r,activeConversation:a,createConversation:x,switchConversation:async i=>i?r.value===i?a.value:e.value.find(f=>f.id===i)?(await v(i),A({excludeId:i}),r.value=i,a.value):null:null,deleteConversation:async i=>{let c=e.value.findIndex(y=>y.id===i);if(c===-1)return;await s.get(i)?.abortRequest(),p(i),s.delete(i),e.value.splice(c,1),t?.deleteConversation?.(i),r.value===i&&(r.value=null,A())},clear:()=>{e.value.map(c=>c.id).forEach(c=>{t?.deleteConversation?.(c)}),s.forEach(c=>{c.abortRequest()}),n.forEach(c=>{c()}),e.value=[],s.clear(),n.clear(),r.value=null},updateConversationTitle:(i,c)=>{let f=e.value.find(y=>y.id===i);f&&(f.title=c,f.updatedAt=Date.now(),t?.saveConversation?.(f))},saveMessages:l,sendMessage:i=>{a.value?.engine.sendMessage(i)},abortActiveRequest:async()=>{await a.value?.engine.abortRequest()}}};export{te as AIClient,Se as EXCLUDE_MODE_REMOVE,Ce as ErrorType,$ as IndexedDBStrategy,V as LocalStorageStrategy,ve as StreamEventType,ae as fallbackRolePlugin,xe as indexedDBStorageStrategyFactory,ie as lengthPlugin,oe as localStorageStrategyFactory,Me as sseStreamToGenerator,le as thinkingPlugin,mt as toolPlugin,Et as useConversation,ne as useMessage};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentiny/tiny-robot-kit",
|
|
3
|
-
"version": "0.4.0-alpha.
|
|
3
|
+
"version": "0.4.0-alpha.6",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"idb": "^8.0.3"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "031899aa132c6c971fb86bf7d8c297dbf5f02c5d"
|
|
33
33
|
}
|