@opentiny/tiny-robot-kit 0.4.0-beta.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -262
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/package.json +28 -2
package/README.md
CHANGED
|
@@ -1,289 +1,86 @@
|
|
|
1
1
|
# tiny-robot-kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
@opentiny/tiny-robot-kit 是 TinyRobot 提供的数据层工具包,用于统一处理 AI 大模型调用、消息状态与多会话管理。
|
|
4
|
+
它帮助你在任意 UI 上快速实现聊天、流式响应和会话持久化等常见 AI 交互能力。
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
+
## 功能概览
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
- **消息管理**:`useMessage` 管理消息状态与模型交互流程。
|
|
9
|
+
- **会话管理**:`useConversation` 在 useMessage 之上提供多会话能力。
|
|
10
|
+
- **工具函数(Utils)**:处理 SSE、消息格式与响应解析。
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
完整 API 请参考文档:
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
- `useMessage`:<https://docs.opentiny.design/tiny-robot/tools/message>
|
|
15
|
+
- `useConversation`:<https://docs.opentiny.design/tiny-robot/tools/conversation>
|
|
16
|
+
- 工具函数:<https://docs.opentiny.design/tiny-robot/tools/utils>
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
new AIClient(config: AIModelConfig)
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
#### 方法
|
|
18
|
-
|
|
19
|
-
- `chat(request: ChatCompletionRequest): Promise<ChatCompletionResponse>`
|
|
20
|
-
发送聊天请求并获取响应。
|
|
21
|
-
|
|
22
|
-
- `chatStream(request: ChatCompletionRequest, handler: StreamHandler): Promise<void>`
|
|
23
|
-
发送流式聊天请求并通过处理器处理响应。
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
## 基本用法
|
|
27
|
-
|
|
28
|
-
### 创建客户端并发送消息
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
import { AIClient } from '@opentiny/tiny-robot-kit';
|
|
32
|
-
|
|
33
|
-
// 创建客户端
|
|
34
|
-
const client = new AIClient({
|
|
35
|
-
provider: 'openai',
|
|
36
|
-
apiKey: 'your-api-key',
|
|
37
|
-
defaultModel: 'gpt-3.5-turbo'
|
|
38
|
-
});
|
|
18
|
+
## 安装
|
|
39
19
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{ role: 'system', content: '你是一个有用的助手。' },
|
|
46
|
-
{ role: 'user', content: '你好,请介绍一下自己。' }
|
|
47
|
-
],
|
|
48
|
-
options: {
|
|
49
|
-
temperature: 0.7
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
console.log(response.choices[0].message.content);
|
|
54
|
-
} catch (error) {
|
|
55
|
-
console.error('聊天出错:', error);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
chat();
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### 使用流式响应
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
import { AIClient } from '@opentiny/tiny-robot-kit';
|
|
66
|
-
|
|
67
|
-
const client = new AIClient({
|
|
68
|
-
provider: 'openai',
|
|
69
|
-
apiKey: 'your-api-key'
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
async function streamChat() {
|
|
73
|
-
try {
|
|
74
|
-
const controller: AbortController = new AbortController()
|
|
75
|
-
await client.chatStream({
|
|
76
|
-
messages: [
|
|
77
|
-
{ role: 'user', content: '写一个简短的故事。' }
|
|
78
|
-
],
|
|
79
|
-
options: {
|
|
80
|
-
stream: true, // 启用流式响应
|
|
81
|
-
signal: controller.signal // 传递 AbortController 的 signal用于中断请求
|
|
82
|
-
}
|
|
83
|
-
}, {
|
|
84
|
-
onData: (data) => {
|
|
85
|
-
// 处理流式数据
|
|
86
|
-
const content = data.choices[0]?.delta?.content || '';
|
|
87
|
-
process.stdout.write(content);
|
|
88
|
-
},
|
|
89
|
-
onError: (error) => {
|
|
90
|
-
console.error('流式响应错误:', error);
|
|
91
|
-
},
|
|
92
|
-
onDone: () => {
|
|
93
|
-
console.log('\n流式响应完成');
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
// controller.abort() // 中断请求
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.error('流式聊天出错:', error);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
streamChat();
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @opentiny/tiny-robot-kit
|
|
22
|
+
# 或
|
|
23
|
+
npm install @opentiny/tiny-robot-kit
|
|
24
|
+
yarn add @opentiny/tiny-robot-kit
|
|
103
25
|
```
|
|
104
26
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
#### 选项
|
|
108
|
-
|
|
109
|
-
`useMessage` 接受以下选项:
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
interface UseMessageOptions {
|
|
113
|
-
/** AI客户端实例 */
|
|
114
|
-
client: AIClient;
|
|
115
|
-
/** 是否默认使用流式响应 */
|
|
116
|
-
useStreamByDefault?: boolean;
|
|
117
|
-
/** 错误消息模板 */
|
|
118
|
-
errorMessage?: string;
|
|
119
|
-
/** 初始消息列表 */
|
|
120
|
-
initialMessages?: ChatMessage[];
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
#### 返回值
|
|
125
|
-
|
|
126
|
-
`useMessage` 返回以下内容:
|
|
127
|
-
```typescript
|
|
128
|
-
interface UseMessageReturn {
|
|
129
|
-
messages: ChatMessage[];
|
|
130
|
-
/** 消息状态 */
|
|
131
|
-
messageState: Reactive<MessageState>;
|
|
132
|
-
/** 输入消息 */
|
|
133
|
-
inputMessage: Ref<string>;
|
|
134
|
-
/** 是否使用流式响应 */
|
|
135
|
-
useStream: Ref<boolean>;
|
|
136
|
-
/** 发送消息 */
|
|
137
|
-
sendMessage: (content?: string, clearInput?: boolean) => Promise<void>
|
|
138
|
-
/** 清空消息 */
|
|
139
|
-
clearMessages: () => void;
|
|
140
|
-
/** 添加消息 */
|
|
141
|
-
addMessage: (message: ChatMessage) => void;
|
|
142
|
-
/** 中止请求 */
|
|
143
|
-
abortRequest: () => void;
|
|
144
|
-
/** 重试请求 */
|
|
145
|
-
retryRequest: () => Promise<void>;
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
#### MessageState 接口
|
|
150
|
-
```typescript
|
|
151
|
-
interface MessageState {
|
|
152
|
-
status: STATUS
|
|
153
|
-
errorMsg: string | null
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
enum STATUS {
|
|
157
|
-
INIT = 'init', // 初始状态
|
|
158
|
-
PROCESSING = 'processing', // AI请求正在处理中, 还未响应,显示加载动画
|
|
159
|
-
STREAMING = 'streaming', // 流式响应中分块数据返回中
|
|
160
|
-
FINISHED = 'finished', // AI请求已完成
|
|
161
|
-
ABORTED = 'aborted', // 用户中止请求
|
|
162
|
-
ERROR = 'error', // AI请求发生错误
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### useConversation
|
|
167
|
-
|
|
168
|
-
对话管理与数据持久化
|
|
169
|
-
|
|
170
|
-
#### 基本用法
|
|
27
|
+
## 基本用法
|
|
171
28
|
|
|
172
|
-
|
|
173
|
-
import { useConversation, AIClient } from '@opentiny/tiny-robot-kit'
|
|
29
|
+
### useMessage —— 管理 AI 消息
|
|
174
30
|
|
|
175
|
-
|
|
176
|
-
provider: 'openai',
|
|
177
|
-
apiKey: 'your-api-key',
|
|
178
|
-
defaultModel: 'gpt-3.5-turbo'
|
|
179
|
-
});
|
|
31
|
+
`useMessage` 管理消息列表、请求/处理状态、流式响应、插件体系以及工具调用逻辑,而真正的 HTTP 请求由你提供的 `responseProvider` 负责。
|
|
180
32
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
messageManager, // 与 useMessage 返回一致,具体查看useMessage
|
|
184
|
-
createConversation,
|
|
185
|
-
switchConversation,
|
|
186
|
-
deleteConversation,
|
|
187
|
-
// ...
|
|
188
|
-
} = useConversation({ client })
|
|
33
|
+
```ts
|
|
34
|
+
import { useMessage } from '@opentiny/tiny-robot-kit'
|
|
189
35
|
|
|
190
|
-
const
|
|
36
|
+
const message = useMessage({
|
|
37
|
+
// responseProvider 负责调用你的后端 / 模型接口
|
|
38
|
+
async responseProvider(requestBody, abortSignal) {
|
|
39
|
+
const res = await fetch('/api/chat', {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
body: JSON.stringify(requestBody),
|
|
42
|
+
signal: abortSignal,
|
|
43
|
+
headers: { 'Content-Type': 'application/json' },
|
|
44
|
+
})
|
|
191
45
|
|
|
192
|
-
|
|
46
|
+
return await res.json()
|
|
47
|
+
},
|
|
48
|
+
})
|
|
193
49
|
```
|
|
194
50
|
|
|
195
|
-
|
|
51
|
+
更多进阶用法(流式响应、插件、自定义分块(chunk)处理、工具调用等),请查看 <https://docs.opentiny.design/tiny-robot/tools/message>。
|
|
196
52
|
|
|
197
|
-
|
|
53
|
+
### useConversation —— 管理多会话
|
|
198
54
|
|
|
199
|
-
|
|
200
|
-
interface UseConversationReturn {
|
|
201
|
-
/** 会话状态 */
|
|
202
|
-
state: ConversationState;
|
|
203
|
-
/** 消息管理 */
|
|
204
|
-
messageManager: UseMessageReturn;
|
|
205
|
-
/** 创建新会话 */
|
|
206
|
-
createConversation: (title?: string, metadata?: Record<string, any>) => string;
|
|
207
|
-
/** 切换会话 */
|
|
208
|
-
switchConversation: (id: string) => void;
|
|
209
|
-
/** 删除会话 */
|
|
210
|
-
deleteConversation: (id: string) => void;
|
|
211
|
-
/** 更新会话标题 */
|
|
212
|
-
updateTitle: (id: string, title: string) => void;
|
|
213
|
-
/** 更新会话元数据 */
|
|
214
|
-
updateMetadata: (id: string, metadata: Record<string, any>) => void;
|
|
215
|
-
/** 保存会话 */
|
|
216
|
-
saveConversations: () => Promise<void>;
|
|
217
|
-
/** 加载会话 */
|
|
218
|
-
loadConversations: () => Promise<void>;
|
|
219
|
-
/** 生成会话标题 */
|
|
220
|
-
generateTitle: (id: string) => Promise<string>;
|
|
221
|
-
/** 获取当前会话 */
|
|
222
|
-
getCurrentConversation: () => Conversation | null;
|
|
223
|
-
}
|
|
224
|
-
```
|
|
55
|
+
`useConversation` 基于 `useMessage` 之上,提供多会话管理能力,并支持多种存储策略(LocalStorage、IndexedDB、自定义等)。
|
|
225
56
|
|
|
226
|
-
|
|
57
|
+
```ts
|
|
58
|
+
import { useConversation } from '@opentiny/tiny-robot-kit'
|
|
227
59
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
60
|
+
const { conversations, activeConversation, createConversation, switchConversation } = useConversation({
|
|
61
|
+
useMessageOptions: {
|
|
62
|
+
async responseProvider(requestBody, abortSignal) {
|
|
63
|
+
const res = await fetch('/api/chat', {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
body: JSON.stringify(requestBody),
|
|
66
|
+
signal: abortSignal,
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
})
|
|
69
|
+
return await res.json()
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
})
|
|
237
73
|
```
|
|
238
74
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
|
|
243
|
-
interface Conversation {
|
|
244
|
-
/** 会话ID */
|
|
245
|
-
id: string;
|
|
246
|
-
/** 会话标题 */
|
|
247
|
-
title: string;
|
|
248
|
-
/** 创建时间 */
|
|
249
|
-
createdAt: number;
|
|
250
|
-
/** 更新时间 */
|
|
251
|
-
updatedAt: number;
|
|
252
|
-
/** 自定义元数据 */
|
|
253
|
-
metadata?: Record<string, any>;
|
|
254
|
-
/** 消息 */
|
|
255
|
-
messages: ChatMessage[];
|
|
256
|
-
}
|
|
257
|
-
```
|
|
75
|
+
例如 `localStorageStrategyFactory` 和 `indexedDBStorageStrategyFactory` 等存储策略的详细用法,请参考 <https://docs.opentiny.design/tiny-robot/tools/conversation>。
|
|
258
76
|
|
|
259
|
-
|
|
77
|
+
### 工具函数 Utils —— 处理 SSE 与响应
|
|
260
78
|
|
|
261
|
-
|
|
79
|
+
Utils 模块提供了一些与 `useMessage` 搭配使用的常用工具函数:
|
|
262
80
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
/** 加载会话列表 */
|
|
268
|
-
loadConversations: () => Promise<Conversation[]> | Conversation[];
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// 自定义存储策略示例
|
|
272
|
-
class CustomStorageStrategy implements ConversationStorageStrategy {
|
|
273
|
-
async saveConversations(conversations: Conversation[]) {
|
|
274
|
-
// 实现自定义存储逻辑
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
async loadConversations(): Promise<Conversation[]> {
|
|
278
|
-
// 实现自定义加载逻辑
|
|
279
|
-
return [];
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// 使用自定义存储策略
|
|
284
|
-
const conversationManager = useConversation({
|
|
285
|
-
client,
|
|
286
|
-
storage: new CustomStorageStrategy(),
|
|
287
|
-
});
|
|
288
|
-
```
|
|
81
|
+
- `sseStreamToGenerator`:把 SSE `Response` 转换为异步生成器。
|
|
82
|
+
- `formatMessages`:将多种形式的消息统一为 `ChatMessage[]`。
|
|
83
|
+
- `extractTextFromResponse`:从大模型响应中提取纯文本内容。
|
|
84
|
+
- `handleSSEStream`:通过回调方式消费 SSE 流式响应。
|
|
289
85
|
|
|
86
|
+
详细函数签名与行为说明,请查看 <https://docs.opentiny.design/tiny-robot/tools/utils>。
|
package/dist/index.d.mts
CHANGED
|
@@ -501,6 +501,11 @@ interface UseConversationOptions {
|
|
|
501
501
|
* When provided, conversation list and messages can be loaded and persisted.
|
|
502
502
|
*/
|
|
503
503
|
storage?: ConversationStorageStrategy;
|
|
504
|
+
/**
|
|
505
|
+
* Called when the initial conversation list has been loaded from storage.
|
|
506
|
+
* Only fired when storage.loadConversations is available and has completed.
|
|
507
|
+
*/
|
|
508
|
+
onLoad?: (conversations: ConversationInfo[]) => void;
|
|
504
509
|
}
|
|
505
510
|
interface UseConversationReturn {
|
|
506
511
|
conversations: Ref<ConversationInfo[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -501,6 +501,11 @@ interface UseConversationOptions {
|
|
|
501
501
|
* When provided, conversation list and messages can be loaded and persisted.
|
|
502
502
|
*/
|
|
503
503
|
storage?: ConversationStorageStrategy;
|
|
504
|
+
/**
|
|
505
|
+
* Called when the initial conversation list has been loaded from storage.
|
|
506
|
+
* Only fired when storage.loadConversations is available and has completed.
|
|
507
|
+
*/
|
|
508
|
+
onLoad?: (conversations: ConversationInfo[]) => void;
|
|
504
509
|
}
|
|
505
510
|
interface UseConversationReturn {
|
|
506
511
|
conversations: Ref<ConversationInfo[]>;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var ie=Object.defineProperty;var ke=Object.getOwnPropertyDescriptor;var _e=Object.getOwnPropertyNames;var De=Object.prototype.hasOwnProperty;var Ue=(o,t)=>{for(var e in t)ie(o,e,{get:t[e],enumerable:!0})},qe=(o,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of _e(t))!De.call(o,s)&&s!==e&&ie(o,s,{get:()=>t[s],enumerable:!(n=ke(t,s))||n.enumerable});return o};var Be=o=>qe(ie({},"__esModule",{value:!0}),o);var Ve={};Ue(Ve,{AIClient:()=>Z,BaseModelProvider:()=>H,EXCLUDE_MODE_REMOVE:()=>Pe,ErrorType:()=>fe,IndexedDBStrategy:()=>V,LocalStorageStrategy:()=>G,OpenAIProvider:()=>j,StreamEventType:()=>me,extractTextFromResponse:()=>ye,fallbackRolePlugin:()=>ue,formatMessages:()=>he,handleSSEStream:()=>Y,indexedDBStorageStrategyFactory:()=>be,lengthPlugin:()=>de,localStorageStrategyFactory:()=>te,sseStreamToGenerator:()=>Ce,thinkingPlugin:()=>ge,toolPlugin:()=>je,useConversation:()=>Ge,useMessage:()=>ne});module.exports=Be(Ve);var H=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 fe=(c=>(c.NETWORK_ERROR="network_error",c.AUTHENTICATION_ERROR="authentication_error",c.RATE_LIMIT_ERROR="rate_limit_error",c.SERVER_ERROR="server_error",c.MODEL_ERROR="model_error",c.TIMEOUT_ERROR="timeout_error",c.UNKNOWN_ERROR="unknown_error",c))(fe||{}),me=(n=>(n.DATA="data",n.ERROR="error",n.DONE="done",n))(me||{});function K(o){return{type:o.type||"unknown_error",message:o.message||"\u672A\u77E5\u9519\u8BEF",statusCode:o.statusCode,originalError:o.originalError}}function le(o){if(!o.response)return K({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?K({type:"authentication_error",message:"\u8EAB\u4EFD\u9A8C\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u60A8\u7684API\u5BC6\u94A5",statusCode:t,originalError:o}):t===429?K({type:"rate_limit_error",message:"\u8D85\u51FAAPI\u8C03\u7528\u9650\u5236\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",statusCode:t,originalError:o}):t>=500?K({type:"server_error",message:"\u670D\u52A1\u5668\u9519\u8BEF\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",statusCode:t,originalError:o}):K({type:"unknown_error",message:e?.error?.message||`\u8BF7\u6C42\u5931\u8D25\uFF0C\u72B6\u6001\u7801: ${t}`,statusCode:t,originalError:o})}return o.code==="ECONNABORTED"?K({type:"timeout_error",message:"\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5",originalError:o}):K({type:"unknown_error",message:o.message||"\u53D1\u751F\u672A\u77E5\u9519\u8BEF",originalError:o})}async function Y(o,t,e){let n=o.body?.getReader();if(!n)throw new Error("Response body is null");let s=new TextDecoder,r="",a,c;e&&e.addEventListener("abort",()=>{n.cancel().catch(d=>console.error("Error cancelling reader:",d))},{once:!0});try{for(;;){if(e?.aborted){await n.cancel();break}let{done:d,value:f}=await n.read();if(d)break;let v=s.decode(f,{stream:!0});r+=v;let R=r.split(`
|
|
2
2
|
|
|
3
|
-
`);r=R.pop()||"";for(let x of R)if(x.trim()!==""){if(x.trim()==="data: [DONE]"){c&&(a=c),t.onDone(a);continue}try{let A=x.match(/^data: (.+)$/m);if(!A)continue;let
|
|
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{n?.removeEventListener("abort",a),e.releaseLock()}}var K=class extends W{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 n={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...e.options,stream:!1},s={method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)};this.apiKey&&Object.assign(s.headers,{Authorization:`Bearer ${this.apiKey}`});let r=await fetch(`${this.baseURL}/chat/completions`,s);if(!r.ok){let a=await r.text();throw new Error(`HTTP error! status: ${r.status}, details: ${a}`)}return await r.json()}catch(n){throw ae(n)}}async chatStream(e,n){let{signal:s,...r}=e.options||{};try{this.validateRequest(e);let a={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...r,stream:!0},c={method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,Accept:"text/event-stream"},body:JSON.stringify(a),signal:s};this.apiKey&&Object.assign(c.headers,{Authorization:`Bearer ${this.apiKey}`});let u=await fetch(`${this.baseURL}/chat/completions`,c);if(!u.ok){let f=await u.text();throw new Error(`HTTP error! status: ${u.status}, details: ${f}`)}await Q(u,n,s)}catch(a){if(s?.aborted)return;n.onError(ae(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 Y=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 K({...e,...t})}else return new K(t)}async chat(t){return this.provider.chat(t)}async chatStream(t,e){let n={...t,options:{...t.options,stream:!0}};return this.provider.chatStream(n,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 ye=require("vue");function z(o,t=new WeakMap){if(o==null||typeof o!="object")return o;try{let e=(0,ye.toRaw)(o);if(t.has(e))return t.get(e);if(Array.isArray(e)){let s=[];return t.set(e,s),s.push(...e.map(r=>z(r,t))),s}if(e instanceof Date||e instanceof RegExp||e instanceof ArrayBuffer||e instanceof Blob)return e;let n={};t.set(e,n);for(let s of Object.keys(e)){let r=Object.getOwnPropertyDescriptor(e,s);if(!r||r.get||r.set)continue;let a=e[s];typeof a!="function"&&typeof a!="symbol"&&(n[s]=z(a,t))}return n}catch(e){return console.warn("unwrapProxy error:",e),Array.isArray(o)?[]:{}}}var Z=o=>o.map(t=>{let{renderContent:e,...n}=t;if(!Array.isArray(e))return t;let s=e.filter(a=>a.type==="collapsible-text"),r=e.filter(a=>a.type==="markdown"||a.type==="text");return s.length>0&&(n.reasoning_content=s.map(a=>a.content).join("")),r.length>0&&(n.content=r.map(a=>a.content).join("")),n});var X=o=>{let t=localStorage.getItem(o);return t?JSON.parse(t):[]},H=class{constructor(t="tiny-robot-ai-conversations"){this.storageKey=t}saveConversation(t){try{let e=X(this.storageKey),n=e.findIndex(s=>s.id===t.id);n!==-1?Object.assign(e[n],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 X(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 n=X(this.storageKey),s=n.findIndex(r=>r.id===t);s!==-1&&(n[s].messages=e),localStorage.setItem(this.storageKey,JSON.stringify(n))}catch(n){console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",n)}}loadMessages(t){try{let n=X(this.storageKey).find(r=>r.id===t);return Z(n?.messages||[])}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}deleteConversation(t){let e=X(this.storageKey),n=e.findIndex(s=>s.id===t);n!==-1&&e.splice(n,1),localStorage.setItem(this.storageKey,JSON.stringify(e))}};var Ce=require("idb");var G=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,Ce.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 n=await(await this.getDB()).get("messages",t);return n?Z(n.messages):[]}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}async saveConversation(t){try{let e=await this.getDB(),n=z(t);await e.put("conversations",n)}catch(e){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u5931\u8D25:",e),e}}async saveMessages(t,e){try{let n=await this.getDB(),s=z(e);await n.put("messages",{conversationId:t,messages:s})}catch(n){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",n),n}}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 ee(o={}){return new H(o.key||"tiny-robot-ai-conversations")}function ve(o={}){return new G(o.dbName||"tiny-robot-ai-db",o.dbVersion||1)}var j=require("vue");var U=require("vue");var le=(o={})=>{let{fallbackRole:t="assistant",...e}=o;return{name:"fallbackRole",...e,onBeforeRequest(n){let{requestBody:s}=n;return s.messages=s.messages.map(r=>({...r,role:r.role||t})),e.onBeforeRequest?.(n)}}};var ce=(o={})=>{let{continueContent:t="Please continue with your previous answer.",...e}=o;return{name:"length",...e,onAfterRequest:async n=>{let{lastChoice:s,appendMessage:r,requestNext:a}=n;return s?.finish_reason==="length"&&(r({role:"user",content:t}),a()),e.onAfterRequest?.(n)}}};var ue=(o={})=>({name:"thinking",...o,onCompletionChunk(t){let{choice:e,currentMessage:n}=t,r=typeof(e?.message?.reasoning_content||e?.delta?.reasoning_content)=="string";return n.state?n.state.thinking=r:n.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 we=require("vue");var V=class extends Error{constructor(t){super(t),this.name="AbortError"}};function qe(o){if(o.aborted)return{promise:Promise.reject(new V(String(o.reason??"Aborted"))),cleanup:()=>{}};let t=null;return{promise:new Promise((s,r)=>{t=()=>{r(new V(String(o.reason??"Aborted")))},o.addEventListener("abort",t,{once:!0})}),cleanup:()=>{t&&(o.removeEventListener("abort",t),t=null)}}}function Re(o,t){let{promise:e,cleanup:n}=qe(t);return Promise.race([o,e]).finally(n)}function xe(o,t){let e={};for(let n in o)t.includes(n)&&(e[n]=o[n]);return e}function Ae(o,t){let e={};for(let n in o)t.includes(n)||(e[n]=o[n]);return e}async function*te(o){if(Me(o)){yield*o;return}let t=await o;if(Me(t)){yield*t;return}yield t}function Me(o){return o&&typeof o=="object"&&typeof o[Symbol.asyncIterator]=="function"}var de=o=>typeof o=="object"&&o!==null,be=o=>de(o)&&typeof o.index=="number",$=(o,t)=>{for(let[e,n]of Object.entries(t)){let s=o[e];if(s)if(typeof s=="string"&&typeof n=="string")e==="type"&&s||(o[e]=s+n);else if(Array.isArray(s)&&Array.isArray(n))if(s.every(r=>be(r))&&n.every(r=>be(r))){let r=new Map(s.map(f=>[f.index,f])),a=new Map(n.map(f=>[f.index,f]));for(let[f,v]of a)if(r.has(f)){let R=r.get(f);r.set(f,$(R,v))}else r.set(f,v);let c=Math.max(...Array.from(r.keys()),-1)+1,u=c>s.length?Array.from({length:c}):s;for(let[f,v]of r)u[f]=v;o[e]=u}else o[e]=[...s,...n];else de(s)&&de(n)&&(o[e]=$(s,n));else o[e]=n}return o};function Be(o,t){let e=[];for(let n=0;n<o.length;n++){let s=o[n];if(s.role==="assistant"&&s.tool_calls&&s.tool_calls.length>0){let r=new Set(s.tool_calls.map(u=>u.id)),a=new Set;for(let u=n+1;u<o.length;u++){let f=o[u];f.role==="tool"&&f.tool_call_id&&r.has(f.tool_call_id)&&a.add(f.tool_call_id)}let c=s.tool_calls.map(u=>u.id).filter(u=>!a.has(u));c.length>0&&e.push({insertAfterIndex:n,missingToolCallIds:c})}}for(let n=e.length-1;n>=0;n--){let{insertAfterIndex:s,missingToolCallIds:r}=e[n],a=r.map(c=>({role:"tool",tool_call_id:c,content:t}));o.splice(s+1,0,...a)}}var Te="remove";function Ne(o,t,e,n){let s=o.filter(a=>a[e]),r=new Set(s.flatMap(a=>a.tool_calls?.map(c=>c.id)??[]));if(t===Te)for(let a=o.length-1;a>=0;a--){let c=o[a];(c[e]||c[n]||c.tool_call_id&&r.has(c.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[n]=!0,delete a[e])}var Fe=o=>{let{getTools:t,beforeCallTools:e,callTool:n,onToolCallStart:s,onToolCallEnd:r,toolCallCancelledContent:a="Tool call cancelled.",toolCallFailedContent:c="Tool call failed.",autoFillMissingToolMessages:u=!1,excludeToolMessagesNextTurn:f=!1,...v}=o,R=Symbol("doNotSendNextTurn"),x=Symbol("doNotSend"),A=(...m)=>{let[h,{primaryMessage:T}]=m;T.state.toolCall[h.id].status="running",s?.(...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&&Be(h,a),f&&Ne(h,f,R,x),v.onTurnStart?.(m)},onBeforeRequest:async m=>{let{messages:h,requestBody:T}=m;f===!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:l}=m;if(T?.finish_reason!=="tool_calls"||!h.tool_calls?.length)return;f&&(h[R]=!0),i("processing","calling-tools"),await e?.(h.tool_calls,{...m,currentMessage:h});let d=h.tool_calls.map(async y=>{let S=Math.floor(Date.now()/1e3),w=(0,we.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=n(y,q),g=te(I);for await(let p of g){if(typeof p=="string")w.content+=p;else{let M={};try{M=JSON.parse(w.content||"{}")}catch(P){console.warn(P)}w.content=JSON.stringify($(M,p))}w.metadata.updatedAt=Math.floor(Date.now()/1e3)}b(y,{...q,status:"success"})}catch(I){let g=I instanceof Error?I:new Error(String(I));if(E.aborted){b(y,{...q,status:"cancelled",error:g});return}console.error(I),w.content.length===0&&(w.content=c),b(y,{...q,status:"failed",error:g})}});return await Promise.all(d),l(),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 n=t.findIndex(s=>s.name===e.name);n!==-1&&t.splice(n,1)}t.push(e)}return t},oe=o=>{let{initialMessages:t=[],requestMessageFields:e=[],requestMessageFieldsExclude:n=["state","metadata","loading"],plugins:s=[],onCompletionChunk:r}=o,a=(0,U.ref)("idle"),c=(0,U.ref)(void 0),u=(0,U.ref)(t),f=(0,U.ref)(o.responseProvider),v=null,R=[],x={},A=[le(),ue(),ce()],b=Ke(A.concat(s)),m=(0,U.computed)(()=>a.value==="processing"),h=async g=>{if(!g||!g.trim()){console.warn("Cannot send empty message");return}if(m.value){console.warn("Cannot send message while processing is in progress");return}let p=Math.floor(Date.now()/1e3);u.value.push({role:"user",content:g.trim(),metadata:{createdAt:p,updatedAt:p}}),R.push(u.value[u.value.length-1]),await S()},T=async(...g)=>{if(m.value){console.warn("Cannot send message while processing is in progress");return}u.value.push(...g),R.push(...g),await S()},O=g=>{let p=g;return e.length&&(p=p.map(M=>xe(M,e))),n.length&&(p=p.map(M=>Ae(M,n))),p},E=(g,p)=>{a.value=g,g==="processing"?c.value=p||"requesting":c.value=void 0},i=g=>{Object.assign(x,g)},l=g=>({messages:u.value,currentTurn:R,requestState:a.value,processingState:c.value,plugins:b,abortSignal:g,setRequestState:E,customContext:x,setCustomContext:i}),d=(g,p)=>typeof g.disabled=="function"?g.disabled(p):!!g.disabled,y=async(g,p)=>{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=l(p);for(let D of b.filter(k=>!d(k,P)))await D.onBeforeRequest?.({...P,requestBody:M});let C=(0,U.reactive)({role:"",content:"",loading:!0});I(C);let _,se=g(M,p),B=te(se);for await(let D of B){E("processing","completing"),C.loading&&(C.loading=void 0);let k=D.choices?.find(L=>L.index===0);if(k){_=k;let L=()=>{C.metadata||(C.metadata={});let{created:J,...Ee}=D;C.metadata.createdAt=J,C.metadata.updatedAt=Math.floor(Date.now()/1e3),Object.assign(C.metadata,Ee),$(C,k.message||k.delta)};if(r){let J=l(p);r({...J,chunk:D,choice:k,currentMessage:C},L)}else L()}let N=l(p);for(let L of b.filter(J=>!d(J,N)))L.onCompletionChunk?.({...N,abortSignal:p,chunk:D,choice:k,currentMessage:C})}await q(C,g,p,_)},S=async()=>{let g=new AbortController;v=g,x={};try{E("processing","requesting");let p=l(g.signal);for(let C of b.filter(_=>!d(_,p)))await C.onTurnStart?.(p);let M=f.value;try{await y(M,g.signal),E("completed")}catch(C){if(g.signal.aborted||C instanceof V||C instanceof Error&&C.name==="AbortError")E("aborted");else throw C}let P=l(g.signal);for(let C of b.filter(_=>!d(_,P)))await C.onTurnEnd?.(P)}catch(p){E("error");let M=!1,P=l(g.signal);for(let C of b.filter(_=>!d(_,P)))C.onError&&(M=!0,C.onError({...P,error:p}));if(!M)throw p}finally{let p=l(g.signal);for(let M of b.filter(P=>!d(P,p)))try{M.onFinally?.(p)}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(g=>{let p=(0,U.watch)(m,M=>{M||(p(),g())},{immediate:!0})})},q=async(g,p,M,P)=>{let C=!1,_=l(M),se=b.filter(B=>!d(B,_)).map(B=>{if(!B.onAfterRequest)return null;let D=N=>{I(N)},k=()=>{C=!0};return B.onAfterRequest({..._,currentMessage:g,lastChoice:P,appendMessage:D,requestNext:k})}).filter(B=>B!==null);await Re(Promise.all(se),M),C&&await y(p,M)},I=g=>{let p=Array.isArray(g)?g:[g];u.value.push(...p),R.push(...p)};return{requestState:a,processingState:c,messages:u,responseProvider:f,isProcessing:m,sendMessage:h,send:T,abortRequest:w}};var ne=require("vue");function Pe(o,t=200,e=!1,n=!0,s=!1){return je(Le(t,e,n,s),o)}function je(o,t){function e(...n){return new Promise((s,r)=>{Promise.resolve(o(()=>t.apply(this,n),{fn:t,thisArg:this,args:n})).then(s).catch(r)})}return e}var Se=()=>{};function Le(...o){let t=0,e,n=!0,s=Se,r,a,c,u,f;!(0,ne.isRef)(o[0])&&typeof o[0]=="object"?{delay:a,trailing:c=!0,leading:u=!0,rejectOnCancel:f=!1}=o[0]:[a,c=!0,u=!0,f=!1]=o;let v=()=>{e&&(clearTimeout(e),e=void 0,s(),s=Se)};return x=>{let A=(0,ne.toValue)(a),b=Date.now()-t,m=()=>r=x();return v(),A<=0?(t=Date.now(),m()):(b>A?(t=Date.now(),(u||!n)&&m()):c&&(r=new Promise((h,T)=>{s=f?T:h,e=setTimeout(()=>{t=Date.now(),n=!0,h(m()),v()},Math.max(0,A-b))})),!u&&!e&&(e=setTimeout(()=>n=!0,A)),n=!1,r)}}var We=o=>{let t=o.storage||ee(),e=(0,j.ref)([]),n=new Map,s=new Map,r=(0,j.ref)(null),a=(0,j.computed)(()=>{let i=r.value;if(!i)return null;let l=e.value.find(y=>y.id===i);if(!l)return null;let d=n.get(i);return d?{...l,engine:d}:null}),c=i=>{if(!t?.saveMessages)return;let l=i||r.value,d=e.value.find(S=>S.id===l);if(!d)return;d.updatedAt=Date.now(),t?.saveConversation?.(d);let y=n.get(d.id);y&&t.saveMessages(d.id,y.messages.value)},u=(i,l)=>{if(!o.autoSaveMessages||!t?.saveMessages)return;let d=s.get(i);d&&d();let y=o.autoSaveThrottle??1e3,S=Pe(()=>{c(i)},y,!0,!0),w=(0,j.watch)(l.messages,S,{deep:!0});s.set(i,w)},f=i=>{let l=s.get(i);l&&(l(),s.delete(i))};t?.loadConversations&&Promise.resolve(t.loadConversations()).then(i=>{if(!i?.length)return;if(e.value.length===0){e.value=i;return}let l=new Map(e.value.map(d=>[d.id,d]));i.forEach(d=>{l.has(d.id)||l.set(d.id,d)}),e.value=Array.from(l.values())}).catch(i=>{console.error("[useConversation] loadConversations failed:",i)});let v=async(i,l)=>{let d=n.get(i);if(d)return d;let y=l?.initialMessages??o.useMessageOptions.initialMessages??[];if(t?.loadMessages)try{y=await t.loadMessages(i)}catch(w){console.error("[useConversation] loadMessages failed:",w)}let S=oe({...o.useMessageOptions,...l,initialMessages:y});return n.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:l=R(),title:d,metadata:y,useMessageOptions:S}=i||{},w=Date.now(),q={id:l,title:d,createdAt:w,updatedAt:w,metadata:y};e.value.unshift(q);let I=oe({...o.useMessageOptions,...S});return n.set(l,I),u(l,I),t?.saveConversation?.(q),t?.saveMessages?.(l,I.messages.value),r.value=l,a.value},A=i=>{let{excludeId:l}=i||{};n.forEach((d,y)=>{if(l&&y===l)return;d.isProcessing?.value||(f(y),n.delete(y))})};return{conversations:e,activeConversationId:r,activeConversation:a,createConversation:x,switchConversation:async i=>i?r.value===i?a.value:e.value.find(d=>d.id===i)?(await v(i),A({excludeId:i}),r.value=i,a.value):null:null,deleteConversation:async i=>{let l=e.value.findIndex(y=>y.id===i);if(l===-1)return;await n.get(i)?.abortRequest(),f(i),n.delete(i),e.value.splice(l,1),t?.deleteConversation?.(i),r.value===i&&(r.value=null,A())},clear:()=>{e.value.map(l=>l.id).forEach(l=>{t?.deleteConversation?.(l)}),n.forEach(l=>{l.abortRequest()}),s.forEach(l=>{l()}),e.value=[],n.clear(),s.clear(),r.value=null},updateConversationTitle:(i,l)=>{let d=e.value.find(y=>y.id===i);d&&(d.title=l,d.updatedAt=Date.now(),t?.saveConversation?.(d))},saveMessages:c,sendMessage:i=>{a.value?.engine.sendMessage(i)},abortActiveRequest:async()=>{await a.value?.engine.abortRequest()}}};0&&(module.exports={AIClient,BaseModelProvider,EXCLUDE_MODE_REMOVE,ErrorType,IndexedDBStrategy,LocalStorageStrategy,OpenAIProvider,StreamEventType,extractTextFromResponse,fallbackRolePlugin,formatMessages,handleSSEStream,indexedDBStorageStrategyFactory,lengthPlugin,localStorageStrategyFactory,sseStreamToGenerator,thinkingPlugin,toolPlugin,useConversation,useMessage});
|
|
3
|
+
`);r=R.pop()||"";for(let x of R)if(x.trim()!==""){if(x.trim()==="data: [DONE]"){c&&(a=c),t.onDone(a);continue}try{let A=x.match(/^data: (.+)$/m);if(!A)continue;let M=JSON.parse(A[1]);t.onData(M),c=M.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(d){if(e?.aborted)return;throw d}}function he(o){return o.map(t=>typeof t=="object"&&"role"in t&&"content"in t?{role:t.role,content:String(t.content),...t.name?{name:t.name}:{}}:typeof t=="string"?{role:"user",content:t}:{role:"user",content:String(t)})}function ye(o){return!o.choices||!o.choices.length?"":o.choices[0].message?.content||""}function ce(o="The operation was aborted"){let t=new Error(o);return t.name="AbortError",t}async function*Ce(o,t={}){let e=o.body?.getReader();if(!e)throw new Error("ReadableStream not supported");let{signal:n}=t,s=new TextDecoder,r="",a=()=>{e.cancel()};n?.addEventListener("abort",a);try{for(;;){if(n?.aborted)throw ce();let c;try{c=await e.read()}catch(x){throw n?.aborted?ce():x}let{done:d,value:f}=c;if(d){if(n?.aborted)throw ce();return}let v=s.decode(f,{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(M){console.warn("Failed to parse SSE data:",A,M)}}}}finally{n?.removeEventListener("abort",a),e.releaseLock()}}var j=class extends H{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 n={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...e.options,stream:!1},s={method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)};this.apiKey&&Object.assign(s.headers,{Authorization:`Bearer ${this.apiKey}`});let r=await fetch(`${this.baseURL}/chat/completions`,s);if(!r.ok){let a=await r.text();throw new Error(`HTTP error! status: ${r.status}, details: ${a}`)}return await r.json()}catch(n){throw le(n)}}async chatStream(e,n){let{signal:s,...r}=e.options||{};try{this.validateRequest(e);let a={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...r,stream:!0},c={method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,Accept:"text/event-stream"},body:JSON.stringify(a),signal:s};this.apiKey&&Object.assign(c.headers,{Authorization:`Bearer ${this.apiKey}`});let d=await fetch(`${this.baseURL}/chat/completions`,c);if(!d.ok){let f=await d.text();throw new Error(`HTTP error! status: ${d.status}, details: ${f}`)}await Y(d,n,s)}catch(a){if(s?.aborted)return;n.onError(le(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 Z=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 j({...e,...t})}else return new j(t)}async chat(t){return this.provider.chat(t)}async chatStream(t,e){let n={...t,options:{...t.options,stream:!0}};return this.provider.chatStream(n,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 ve=require("vue");function X(o,t=new WeakMap){if(o==null||typeof o!="object")return o;try{let e=(0,ve.toRaw)(o);if(t.has(e))return t.get(e);if(Array.isArray(e)){let s=[];return t.set(e,s),s.push(...e.map(r=>X(r,t))),s}if(e instanceof Date||e instanceof RegExp||e instanceof ArrayBuffer||e instanceof Blob)return e;let n={};t.set(e,n);for(let s of Object.keys(e)){let r=Object.getOwnPropertyDescriptor(e,s);if(!r||r.get||r.set)continue;let a=e[s];typeof a!="function"&&typeof a!="symbol"&&(n[s]=X(a,t))}return n}catch(e){return console.warn("unwrapProxy error:",e),Array.isArray(o)?[]:{}}}var ee=o=>o.map(t=>{let{renderContent:e,...n}=t;if(!Array.isArray(e))return t;let s=e.filter(a=>a.type==="collapsible-text"),r=e.filter(a=>a.type==="markdown"||a.type==="text");return s.length>0&&(n.reasoning_content=s.map(a=>a.content).join("")),r.length>0&&(n.content=r.map(a=>a.content).join("")),n});var Q=o=>{let t=localStorage.getItem(o);return t?JSON.parse(t):[]},G=class{constructor(t="tiny-robot-ai-conversations"){this.storageKey=t}saveConversation(t){try{let e=Q(this.storageKey),n=e.findIndex(s=>s.id===t.id);n!==-1?Object.assign(e[n],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 Q(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 n=Q(this.storageKey),s=n.findIndex(r=>r.id===t);s!==-1&&(n[s].messages=e),localStorage.setItem(this.storageKey,JSON.stringify(n))}catch(n){console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",n)}}loadMessages(t){try{let n=Q(this.storageKey).find(r=>r.id===t);return ee(n?.messages||[])}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}deleteConversation(t){let e=Q(this.storageKey),n=e.findIndex(s=>s.id===t);n!==-1&&e.splice(n,1),localStorage.setItem(this.storageKey,JSON.stringify(e))}};var Me=require("idb");var V=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,Me.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 n=await(await this.getDB()).get("messages",t);return n?ee(n.messages):[]}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}async saveConversation(t){try{let e=await this.getDB(),n=X(t);await e.put("conversations",n)}catch(e){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u5931\u8D25:",e),e}}async saveMessages(t,e){try{let n=await this.getDB(),s=X(e);await n.put("messages",{conversationId:t,messages:s})}catch(n){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",n),n}}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 te(o={}){return new G(o.key||"tiny-robot-ai-conversations")}function be(o={}){return new V(o.dbName||"tiny-robot-ai-db",o.dbVersion||1)}var L=require("vue");var U=require("vue");var ue=(o={})=>{let{fallbackRole:t="assistant",...e}=o;return{name:"fallbackRole",...e,onBeforeRequest(n){let{requestBody:s}=n;return s.messages=s.messages.map(r=>({...r,role:r.role||t})),e.onBeforeRequest?.(n)}}};var de=(o={})=>{let{continueContent:t="Please continue with your previous answer.",...e}=o;return{name:"length",...e,onAfterRequest:async n=>{let{lastChoice:s,appendMessage:r,requestNext:a}=n;return s?.finish_reason==="length"&&(r({role:"user",content:t}),a()),e.onAfterRequest?.(n)}}};var ge=(o={})=>({name:"thinking",...o,onCompletionChunk(t){let{choice:e,currentMessage:n}=t,r=typeof(e?.message?.reasoning_content||e?.delta?.reasoning_content)=="string";return n.state?n.state.thinking=r:n.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 Se=require("vue");var $=class extends Error{constructor(t){super(t),this.name="AbortError"}};function Ne(o){if(o.aborted)return{promise:Promise.reject(new $(String(o.reason??"Aborted"))),cleanup:()=>{}};let t=null;return{promise:new Promise((s,r)=>{t=()=>{r(new $(String(o.reason??"Aborted")))},o.addEventListener("abort",t,{once:!0})}),cleanup:()=>{t&&(o.removeEventListener("abort",t),t=null)}}}function Ae(o,t){let{promise:e,cleanup:n}=Ne(t);return Promise.race([o,e]).finally(n)}function we(o,t){let e={};for(let n in o)t.includes(n)&&(e[n]=o[n]);return e}function Te(o,t){let e={};for(let n in o)t.includes(n)||(e[n]=o[n]);return e}async function*oe(o){if(Re(o)){yield*o;return}let t=await o;if(Re(t)){yield*t;return}yield t}function Re(o){return o&&typeof o=="object"&&typeof o[Symbol.asyncIterator]=="function"}var pe=o=>typeof o=="object"&&o!==null,xe=o=>pe(o)&&typeof o.index=="number",J=(o,t)=>{for(let[e,n]of Object.entries(t)){let s=o[e];if(s)if(typeof s=="string"&&typeof n=="string")e==="type"&&s||(o[e]=s+n);else if(Array.isArray(s)&&Array.isArray(n))if(s.every(r=>xe(r))&&n.every(r=>xe(r))){let r=new Map(s.map(f=>[f.index,f])),a=new Map(n.map(f=>[f.index,f]));for(let[f,v]of a)if(r.has(f)){let R=r.get(f);r.set(f,J(R,v))}else r.set(f,v);let c=Math.max(...Array.from(r.keys()),-1)+1,d=c>s.length?Array.from({length:c}):s;for(let[f,v]of r)d[f]=v;o[e]=d}else o[e]=[...s,...n];else pe(s)&&pe(n)&&(o[e]=J(s,n));else o[e]=n}return o};function Fe(o,t){let e=[];for(let n=0;n<o.length;n++){let s=o[n];if(s.role==="assistant"&&s.tool_calls&&s.tool_calls.length>0){let r=new Set(s.tool_calls.map(d=>d.id)),a=new Set;for(let d=n+1;d<o.length;d++){let f=o[d];f.role==="tool"&&f.tool_call_id&&r.has(f.tool_call_id)&&a.add(f.tool_call_id)}let c=s.tool_calls.map(d=>d.id).filter(d=>!a.has(d));c.length>0&&e.push({insertAfterIndex:n,missingToolCallIds:c})}}for(let n=e.length-1;n>=0;n--){let{insertAfterIndex:s,missingToolCallIds:r}=e[n],a=r.map(c=>({role:"tool",tool_call_id:c,content:t}));o.splice(s+1,0,...a)}}var Pe="remove";function Ke(o,t,e,n){let s=o.filter(a=>a[e]),r=new Set(s.flatMap(a=>a.tool_calls?.map(c=>c.id)??[]));if(t===Pe)for(let a=o.length-1;a>=0;a--){let c=o[a];(c[e]||c[n]||c.tool_call_id&&r.has(c.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[n]=!0,delete a[e])}var je=o=>{let{getTools:t,beforeCallTools:e,callTool:n,onToolCallStart:s,onToolCallEnd:r,toolCallCancelledContent:a="Tool call cancelled.",toolCallFailedContent:c="Tool call failed.",autoFillMissingToolMessages:d=!1,excludeToolMessagesNextTurn:f=!1,...v}=o,R=Symbol("doNotSendNextTurn"),x=Symbol("doNotSend"),A=(...m)=>{let[h,{primaryMessage:T}]=m;T.state.toolCall[h.id].status="running",s?.(...m)},M=(...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 d&&Fe(h,a),f&&Ke(h,f,R,x),v.onTurnStart?.(m)},onBeforeRequest:async m=>{let{messages:h,requestBody:T}=m;f===!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:l}=m;if(T?.finish_reason!=="tool_calls"||!h.tool_calls?.length)return;f&&(h[R]=!0),i("processing","calling-tools"),await e?.(h.tool_calls,{...m,currentMessage:h});let g=h.tool_calls.map(async y=>{let S=Math.floor(Date.now()/1e3),w=(0,Se.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 k=n(y,q),N=oe(k);for await(let u of N){if(typeof u=="string")w.content+=u;else{let p={};try{p=JSON.parse(w.content||"{}")}catch(b){console.warn(b)}w.content=JSON.stringify(J(p,u))}w.metadata.updatedAt=Math.floor(Date.now()/1e3)}M(y,{...q,status:"success"})}catch(k){let N=k instanceof Error?k:new Error(String(k));if(E.aborted){M(y,{...q,status:"cancelled",error:N});return}console.error(k),w.content.length===0&&(w.content=c),M(y,{...q,status:"failed",error:N})}});return await Promise.all(g),l(),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 Le=o=>{let t=[];for(let e of o){if(e.name){let n=t.findIndex(s=>s.name===e.name);n!==-1&&t.splice(n,1)}t.push(e)}return t},ne=o=>{let{initialMessages:t=[],requestMessageFields:e=[],requestMessageFieldsExclude:n=["state","metadata","loading"],plugins:s=[],onCompletionChunk:r}=o,a=(0,U.ref)("idle"),c=(0,U.ref)(void 0),d=(0,U.ref)(t),f=(0,U.ref)(o.responseProvider),v=null,R=[],x={},A=[ue(),ge(),de()],M=Le(A.concat(s)),m=(0,U.computed)(()=>a.value==="processing"),h=async u=>{if(!u||!u.trim()){console.warn("Cannot send empty message");return}if(m.value){console.warn("Cannot send message while processing is in progress");return}let p=Math.floor(Date.now()/1e3);d.value.push({role:"user",content:u.trim(),metadata:{createdAt:p,updatedAt:p}}),R.push(d.value[d.value.length-1]),await w()},T=async(...u)=>{if(m.value){console.warn("Cannot send message while processing is in progress");return}d.value.push(...u),R.push(...u),await w()},O=u=>{let p=u;return e.length&&(p=p.map(b=>we(b,e))),n.length&&(p=p.map(b=>Te(b,n))),p},E=(u,p)=>{a.value=u,u==="processing"?c.value=p||"requesting":c.value=void 0},i=u=>{Object.assign(x,u)},l=u=>({messages:d.value,currentTurn:R,requestState:a.value,processingState:c.value,plugins:M,abortSignal:u,setRequestState:E,customContext:x,setCustomContext:i}),g=(u,p)=>typeof u.disabled=="function"?u.disabled(p):!!u.disabled,y=u=>!u||Object.keys(u).length===0?!1:Object.values(u).some(p=>!!p),S=async(u,p)=>{E("processing","requesting");let b=new Proxy({messages:O(d.value)},{set(D,P,F){return P==="messages"?(D.messages=O(F),!0):(D[P]=F,!0)}}),I=l(p);for(let D of M.filter(P=>!g(P,I)))await D.onBeforeRequest?.({...I,requestBody:b});let C=(0,U.reactive)({role:"",content:"",loading:!0});N(C);let _,re=u(b,p),B=oe(re);for await(let D of B){E("processing","completing"),C.loading&&(C.loading=void 0);let P=D.choices?.find(W=>W.index===0);if(P){_=P;let W=()=>{C.metadata||(C.metadata={});let{created:z,...Ie}=D;C.metadata.createdAt=z,C.metadata.updatedAt=Math.floor(Date.now()/1e3),Object.assign(C.metadata,Ie);let ae=y(P.delta)?P.delta:P.message;ae||(ae=P.delta||P.message||{}),J(C,ae)};if(r){let z=l(p);r({...z,chunk:D,choice:P,currentMessage:C},W)}else W()}let F=l(p);for(let W of M.filter(z=>!g(z,F)))W.onCompletionChunk?.({...F,abortSignal:p,chunk:D,choice:P,currentMessage:C})}await k(C,u,p,_)},w=async()=>{let u=new AbortController;v=u,x={};try{E("processing","requesting");let p=l(u.signal);for(let C of M.filter(_=>!g(_,p)))await C.onTurnStart?.(p);let b=f.value;try{await S(b,u.signal),E("completed")}catch(C){if(u.signal.aborted||C instanceof $||C instanceof Error&&C.name==="AbortError")E("aborted");else throw C}let I=l(u.signal);for(let C of M.filter(_=>!g(_,I)))await C.onTurnEnd?.(I)}catch(p){E("error");let b=!1,I=l(u.signal);for(let C of M.filter(_=>!g(_,I)))C.onError&&(b=!0,C.onError({...I,error:p}));if(!b)throw p}finally{let p=l(u.signal);for(let b of M.filter(I=>!g(I,p)))try{b.onFinally?.(p)}catch(I){console.error(`Error in onFinally hook for plugin ${b.name||"Anonymous"}:`,I)}v=null,R.slice(-1)[0]&&(R.slice(-1)[0].loading=void 0),R=[]}},q=async()=>{v?.abort(),m.value&&await new Promise(u=>{let p=(0,U.watch)(m,b=>{b||(p(),u())},{immediate:!0})})},k=async(u,p,b,I)=>{let C=!1,_=l(b),re=M.filter(B=>!g(B,_)).map(B=>{if(!B.onAfterRequest)return null;let D=F=>{N(F)},P=()=>{C=!0};return B.onAfterRequest({..._,currentMessage:u,lastChoice:I,appendMessage:D,requestNext:P})}).filter(B=>B!==null);await Ae(Promise.all(re),b),C&&await S(p,b)},N=u=>{let p=Array.isArray(u)?u:[u];d.value.push(...p),R.push(...p)};return{requestState:a,processingState:c,messages:d,responseProvider:f,isProcessing:m,sendMessage:h,send:T,abortRequest:q}};var se=require("vue");function Oe(o,t=200,e=!1,n=!0,s=!1){return We(He(t,e,n,s),o)}function We(o,t){function e(...n){return new Promise((s,r)=>{Promise.resolve(o(()=>t.apply(this,n),{fn:t,thisArg:this,args:n})).then(s).catch(r)})}return e}var Ee=()=>{};function He(...o){let t=0,e,n=!0,s=Ee,r,a,c,d,f;!(0,se.isRef)(o[0])&&typeof o[0]=="object"?{delay:a,trailing:c=!0,leading:d=!0,rejectOnCancel:f=!1}=o[0]:[a,c=!0,d=!0,f=!1]=o;let v=()=>{e&&(clearTimeout(e),e=void 0,s(),s=Ee)};return x=>{let A=(0,se.toValue)(a),M=Date.now()-t,m=()=>r=x();return v(),A<=0?(t=Date.now(),m()):(M>A?(t=Date.now(),(d||!n)&&m()):c&&(r=new Promise((h,T)=>{s=f?T:h,e=setTimeout(()=>{t=Date.now(),n=!0,h(m()),v()},Math.max(0,A-M))})),!d&&!e&&(e=setTimeout(()=>n=!0,A)),n=!1,r)}}var Ge=o=>{let t=o.storage||te(),e=(0,L.ref)([]),n=new Map,s=new Map,r=(0,L.ref)(null),a=(0,L.computed)(()=>{let i=r.value;if(!i)return null;let l=e.value.find(y=>y.id===i);if(!l)return null;let g=n.get(i);return g?{...l,engine:g}:null}),c=i=>{if(!t?.saveMessages)return;let l=i||r.value,g=e.value.find(S=>S.id===l);if(!g)return;g.updatedAt=Date.now(),t?.saveConversation?.(g);let y=n.get(g.id);y&&t.saveMessages(g.id,y.messages.value)},d=(i,l)=>{if(!o.autoSaveMessages||!t?.saveMessages)return;let g=s.get(i);g&&g();let y=o.autoSaveThrottle??1e3,S=Oe(()=>{c(i)},y,!0,!0),w=(0,L.watch)(l.messages,S,{deep:!0});s.set(i,w)},f=i=>{let l=s.get(i);l&&(l(),s.delete(i))};t?.loadConversations&&Promise.resolve(t.loadConversations()).then(i=>{if(!i?.length)return[];if(e.value.length===0)return e.value=i,e.value;let l=new Map(e.value.map(g=>[g.id,g]));return i.forEach(g=>{l.has(g.id)||l.set(g.id,g)}),e.value=Array.from(l.values()),e.value}).then(i=>{o.onLoad?.(i)}).catch(i=>{console.error("[useConversation] loadConversations failed:",i)});let v=async(i,l)=>{let g=n.get(i);if(g)return g;let y=l?.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,...l,initialMessages:y});return n.set(i,S),d(i,S),S};function R(){return Date.now().toString(36)+Math.random().toString(36).substring(2,9)}let x=i=>{let{id:l=R(),title:g,metadata:y,useMessageOptions:S}=i||{},w=Date.now(),q={id:l,title:g,createdAt:w,updatedAt:w,metadata:y};e.value.unshift(q);let k=ne({...o.useMessageOptions,...S});return n.set(l,k),d(l,k),t?.saveConversation?.(q),t?.saveMessages?.(l,k.messages.value),r.value=l,a.value},A=i=>{let{excludeId:l}=i||{};n.forEach((g,y)=>{if(l&&y===l)return;g.isProcessing?.value||(f(y),n.delete(y))})};return{conversations:e,activeConversationId:r,activeConversation:a,createConversation:x,switchConversation:async i=>i?r.value===i?a.value:e.value.find(g=>g.id===i)?(await v(i),A({excludeId:i}),r.value=i,a.value):null:null,deleteConversation:async i=>{let l=e.value.findIndex(y=>y.id===i);if(l===-1)return;await n.get(i)?.abortRequest(),f(i),n.delete(i),e.value.splice(l,1),t?.deleteConversation?.(i),r.value===i&&(r.value=null,A())},clear:()=>{e.value.map(l=>l.id).forEach(l=>{t?.deleteConversation?.(l)}),n.forEach(l=>{l.abortRequest()}),s.forEach(l=>{l()}),e.value=[],n.clear(),s.clear(),r.value=null},updateConversationTitle:(i,l)=>{let g=e.value.find(y=>y.id===i);g&&(g.title=l,g.updatedAt=Date.now(),t?.saveConversation?.(g))},saveMessages:c,sendMessage:i=>{a.value?.engine.sendMessage(i)},abortActiveRequest:async()=>{await a.value?.engine.abortRequest()}}};0&&(module.exports={AIClient,BaseModelProvider,EXCLUDE_MODE_REMOVE,ErrorType,IndexedDBStrategy,LocalStorageStrategy,OpenAIProvider,StreamEventType,extractTextFromResponse,fallbackRolePlugin,formatMessages,handleSSEStream,indexedDBStorageStrategyFactory,lengthPlugin,localStorageStrategyFactory,sseStreamToGenerator,thinkingPlugin,toolPlugin,useConversation,useMessage});
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var G=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 Me=(c=>(c.NETWORK_ERROR="network_error",c.AUTHENTICATION_ERROR="authentication_error",c.RATE_LIMIT_ERROR="rate_limit_error",c.SERVER_ERROR="server_error",c.MODEL_ERROR="model_error",c.TIMEOUT_ERROR="timeout_error",c.UNKNOWN_ERROR="unknown_error",c))(Me||{}),be=(n=>(n.DATA="data",n.ERROR="error",n.DONE="done",n))(be||{});function F(o){return{type:o.type||"unknown_error",message:o.message||"\u672A\u77E5\u9519\u8BEF",statusCode:o.statusCode,originalError:o.originalError}}function te(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 ne(o,t,e){let n=o.body?.getReader();if(!n)throw new Error("Response body is null");let s=new TextDecoder,r="",a,c;e&&e.addEventListener("abort",()=>{n.cancel().catch(d=>console.error("Error cancelling reader:",d))},{once:!0});try{for(;;){if(e?.aborted){await n.cancel();break}let{done:d,value:f}=await n.read();if(d)break;let v=s.decode(f,{stream:!0});r+=v;let R=r.split(`
|
|
2
2
|
|
|
3
|
-
`);r=R.pop()||"";for(let x of R)if(x.trim()!==""){if(x.trim()==="data: [DONE]"){c&&(a=c),t.onDone(a);continue}try{let A=x.match(/^data: (.+)$/m);if(!A)continue;let
|
|
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{n?.removeEventListener("abort",a),e.releaseLock()}}var K=class extends H{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 n={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...e.options,stream:!1},s={method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)};this.apiKey&&Object.assign(s.headers,{Authorization:`Bearer ${this.apiKey}`});let r=await fetch(`${this.baseURL}/chat/completions`,s);if(!r.ok){let a=await r.text();throw new Error(`HTTP error! status: ${r.status}, details: ${a}`)}return await r.json()}catch(n){throw Z(n)}}async chatStream(e,n){let{signal:s,...r}=e.options||{};try{this.validateRequest(e);let a={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...r,stream:!0},c={method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,Accept:"text/event-stream"},body:JSON.stringify(a),signal:s};this.apiKey&&Object.assign(c.headers,{Authorization:`Bearer ${this.apiKey}`});let u=await fetch(`${this.baseURL}/chat/completions`,c);if(!u.ok){let f=await u.text();throw new Error(`HTTP error! status: ${u.status}, details: ${f}`)}await te(u,n,s)}catch(a){if(s?.aborted)return;n.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 oe=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 K({...e,...t})}else return new K(t)}async chat(t){return this.provider.chat(t)}async chatStream(t,e){let n={...t,options:{...t.options,stream:!0}};return this.provider.chatStream(n,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 xe}from"vue";function G(o,t=new WeakMap){if(o==null||typeof o!="object")return o;try{let e=xe(o);if(t.has(e))return t.get(e);if(Array.isArray(e)){let s=[];return t.set(e,s),s.push(...e.map(r=>G(r,t))),s}if(e instanceof Date||e instanceof RegExp||e instanceof ArrayBuffer||e instanceof Blob)return e;let n={};t.set(e,n);for(let s of Object.keys(e)){let r=Object.getOwnPropertyDescriptor(e,s);if(!r||r.get||r.set)continue;let a=e[s];typeof a!="function"&&typeof a!="symbol"&&(n[s]=G(a,t))}return n}catch(e){return console.warn("unwrapProxy error:",e),Array.isArray(o)?[]:{}}}var z=o=>o.map(t=>{let{renderContent:e,...n}=t;if(!Array.isArray(e))return t;let s=e.filter(a=>a.type==="collapsible-text"),r=e.filter(a=>a.type==="markdown"||a.type==="text");return s.length>0&&(n.reasoning_content=s.map(a=>a.content).join("")),r.length>0&&(n.content=r.map(a=>a.content).join("")),n});var V=o=>{let t=localStorage.getItem(o);return t?JSON.parse(t):[]},$=class{constructor(t="tiny-robot-ai-conversations"){this.storageKey=t}saveConversation(t){try{let e=V(this.storageKey),n=e.findIndex(s=>s.id===t.id);n!==-1?Object.assign(e[n],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 V(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 n=V(this.storageKey),s=n.findIndex(r=>r.id===t);s!==-1&&(n[s].messages=e),localStorage.setItem(this.storageKey,JSON.stringify(n))}catch(n){console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",n)}}loadMessages(t){try{let n=V(this.storageKey).find(r=>r.id===t);return z(n?.messages||[])}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}deleteConversation(t){let e=V(this.storageKey),n=e.findIndex(s=>s.id===t);n!==-1&&e.splice(n,1),localStorage.setItem(this.storageKey,JSON.stringify(e))}};import{openDB as Ae}from"idb";var J=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 Ae(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 n=await(await this.getDB()).get("messages",t);return n?z(n.messages):[]}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}async saveConversation(t){try{let e=await this.getDB(),n=G(t);await e.put("conversations",n)}catch(e){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u5931\u8D25:",e),e}}async saveMessages(t,e){try{let n=await this.getDB(),s=G(e);await n.put("messages",{conversationId:t,messages:s})}catch(n){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",n),n}}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 ne(o={}){return new $(o.key||"tiny-robot-ai-conversations")}function we(o={}){return new J(o.dbName||"tiny-robot-ai-db",o.dbVersion||1)}import{computed as Fe,ref as he,watch as Ke}from"vue";import{computed as Ie,reactive as ke,ref as Q,watch as _e}from"vue";var ae=(o={})=>{let{fallbackRole:t="assistant",...e}=o;return{name:"fallbackRole",...e,onBeforeRequest(n){let{requestBody:s}=n;return s.messages=s.messages.map(r=>({...r,role:r.role||t})),e.onBeforeRequest?.(n)}}};var ie=(o={})=>{let{continueContent:t="Please continue with your previous answer.",...e}=o;return{name:"length",...e,onAfterRequest:async n=>{let{lastChoice:s,appendMessage:r,requestNext:a}=n;return s?.finish_reason==="length"&&(r({role:"user",content:t}),a()),e.onAfterRequest?.(n)}}};var le=(o={})=>({name:"thinking",...o,onCompletionChunk(t){let{choice:e,currentMessage:n}=t,r=typeof(e?.message?.reasoning_content||e?.delta?.reasoning_content)=="string";return n.state?n.state.thinking=r:n.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 Se}from"vue";var j=class extends Error{constructor(t){super(t),this.name="AbortError"}};function Te(o){if(o.aborted)return{promise:Promise.reject(new j(String(o.reason??"Aborted"))),cleanup:()=>{}};let t=null;return{promise:new Promise((s,r)=>{t=()=>{r(new j(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:n}=Te(t);return Promise.race([o,e]).finally(n)}function ge(o,t){let e={};for(let n in o)t.includes(n)&&(e[n]=o[n]);return e}function pe(o,t){let e={};for(let n in o)t.includes(n)||(e[n]=o[n]);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",L=(o,t)=>{for(let[e,n]of Object.entries(t)){let s=o[e];if(s)if(typeof s=="string"&&typeof n=="string")e==="type"&&s||(o[e]=s+n);else if(Array.isArray(s)&&Array.isArray(n))if(s.every(r=>ue(r))&&n.every(r=>ue(r))){let r=new Map(s.map(f=>[f.index,f])),a=new Map(n.map(f=>[f.index,f]));for(let[f,v]of a)if(r.has(f)){let R=r.get(f);r.set(f,L(R,v))}else r.set(f,v);let c=Math.max(...Array.from(r.keys()),-1)+1,u=c>s.length?Array.from({length:c}):s;for(let[f,v]of r)u[f]=v;o[e]=u}else o[e]=[...s,...n];else se(s)&&se(n)&&(o[e]=L(s,n));else o[e]=n}return o};function Pe(o,t){let e=[];for(let n=0;n<o.length;n++){let s=o[n];if(s.role==="assistant"&&s.tool_calls&&s.tool_calls.length>0){let r=new Set(s.tool_calls.map(u=>u.id)),a=new Set;for(let u=n+1;u<o.length;u++){let f=o[u];f.role==="tool"&&f.tool_call_id&&r.has(f.tool_call_id)&&a.add(f.tool_call_id)}let c=s.tool_calls.map(u=>u.id).filter(u=>!a.has(u));c.length>0&&e.push({insertAfterIndex:n,missingToolCallIds:c})}}for(let n=e.length-1;n>=0;n--){let{insertAfterIndex:s,missingToolCallIds:r}=e[n],a=r.map(c=>({role:"tool",tool_call_id:c,content:t}));o.splice(s+1,0,...a)}}var Ee="remove";function Oe(o,t,e,n){let s=o.filter(a=>a[e]),r=new Set(s.flatMap(a=>a.tool_calls?.map(c=>c.id)??[]));if(t===Ee)for(let a=o.length-1;a>=0;a--){let c=o[a];(c[e]||c[n]||c.tool_call_id&&r.has(c.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[n]=!0,delete a[e])}var yt=o=>{let{getTools:t,beforeCallTools:e,callTool:n,onToolCallStart:s,onToolCallEnd:r,toolCallCancelledContent:a="Tool call cancelled.",toolCallFailedContent:c="Tool call failed.",autoFillMissingToolMessages:u=!1,excludeToolMessagesNextTurn:f=!1,...v}=o,R=Symbol("doNotSendNextTurn"),x=Symbol("doNotSend"),A=(...m)=>{let[h,{primaryMessage:T}]=m;T.state.toolCall[h.id].status="running",s?.(...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&&Pe(h,a),f&&Oe(h,f,R,x),v.onTurnStart?.(m)},onBeforeRequest:async m=>{let{messages:h,requestBody:T}=m;f===!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:l}=m;if(T?.finish_reason!=="tool_calls"||!h.tool_calls?.length)return;f&&(h[R]=!0),i("processing","calling-tools"),await e?.(h.tool_calls,{...m,currentMessage:h});let d=h.tool_calls.map(async y=>{let S=Math.floor(Date.now()/1e3),w=Se({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=n(y,U),g=X(I);for await(let p of g){if(typeof p=="string")w.content+=p;else{let M={};try{M=JSON.parse(w.content||"{}")}catch(P){console.warn(P)}w.content=JSON.stringify(L(M,p))}w.metadata.updatedAt=Math.floor(Date.now()/1e3)}b(y,{...U,status:"success"})}catch(I){let g=I instanceof Error?I:new Error(String(I));if(E.aborted){b(y,{...U,status:"cancelled",error:g});return}console.error(I),w.content.length===0&&(w.content=c),b(y,{...U,status:"failed",error:g})}});return await Promise.all(d),l(),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 De=o=>{let t=[];for(let e of o){if(e.name){let n=t.findIndex(s=>s.name===e.name);n!==-1&&t.splice(n,1)}t.push(e)}return t},re=o=>{let{initialMessages:t=[],requestMessageFields:e=[],requestMessageFieldsExclude:n=["state","metadata","loading"],plugins:s=[],onCompletionChunk:r}=o,a=Q("idle"),c=Q(void 0),u=Q(t),f=Q(o.responseProvider),v=null,R=[],x={},A=[ae(),le(),ie()],b=De(A.concat(s)),m=Ie(()=>a.value==="processing"),h=async g=>{if(!g||!g.trim()){console.warn("Cannot send empty message");return}if(m.value){console.warn("Cannot send message while processing is in progress");return}let p=Math.floor(Date.now()/1e3);u.value.push({role:"user",content:g.trim(),metadata:{createdAt:p,updatedAt:p}}),R.push(u.value[u.value.length-1]),await S()},T=async(...g)=>{if(m.value){console.warn("Cannot send message while processing is in progress");return}u.value.push(...g),R.push(...g),await S()},O=g=>{let p=g;return e.length&&(p=p.map(M=>ge(M,e))),n.length&&(p=p.map(M=>pe(M,n))),p},E=(g,p)=>{a.value=g,g==="processing"?c.value=p||"requesting":c.value=void 0},i=g=>{Object.assign(x,g)},l=g=>({messages:u.value,currentTurn:R,requestState:a.value,processingState:c.value,plugins:b,abortSignal:g,setRequestState:E,customContext:x,setCustomContext:i}),d=(g,p)=>typeof g.disabled=="function"?g.disabled(p):!!g.disabled,y=async(g,p)=>{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=l(p);for(let D of b.filter(k=>!d(k,P)))await D.onBeforeRequest?.({...P,requestBody:M});let C=ke({role:"",content:"",loading:!0});I(C);let _,Y=g(M,p),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:W,...ye}=D;C.metadata.createdAt=W,C.metadata.updatedAt=Math.floor(Date.now()/1e3),Object.assign(C.metadata,ye),L(C,k.message||k.delta)};if(r){let W=l(p);r({...W,chunk:D,choice:k,currentMessage:C},F)}else F()}let B=l(p);for(let F of b.filter(W=>!d(W,B)))F.onCompletionChunk?.({...B,abortSignal:p,chunk:D,choice:k,currentMessage:C})}await U(C,g,p,_)},S=async()=>{let g=new AbortController;v=g,x={};try{E("processing","requesting");let p=l(g.signal);for(let C of b.filter(_=>!d(_,p)))await C.onTurnStart?.(p);let M=f.value;try{await y(M,g.signal),E("completed")}catch(C){if(g.signal.aborted||C instanceof j||C instanceof Error&&C.name==="AbortError")E("aborted");else throw C}let P=l(g.signal);for(let C of b.filter(_=>!d(_,P)))await C.onTurnEnd?.(P)}catch(p){E("error");let M=!1,P=l(g.signal);for(let C of b.filter(_=>!d(_,P)))C.onError&&(M=!0,C.onError({...P,error:p}));if(!M)throw p}finally{let p=l(g.signal);for(let M of b.filter(P=>!d(P,p)))try{M.onFinally?.(p)}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(g=>{let p=_e(m,M=>{M||(p(),g())},{immediate:!0})})},U=async(g,p,M,P)=>{let C=!1,_=l(M),Y=b.filter(q=>!d(q,_)).map(q=>{if(!q.onAfterRequest)return null;let D=B=>{I(B)},k=()=>{C=!0};return q.onAfterRequest({..._,currentMessage:g,lastChoice:P,appendMessage:D,requestNext:k})}).filter(q=>q!==null);await de(Promise.all(Y),M),C&&await y(p,M)},I=g=>{let p=Array.isArray(g)?g:[g];u.value.push(...p),R.push(...p)};return{requestState:a,processingState:c,messages:u,responseProvider:f,isProcessing:m,sendMessage:h,send:T,abortRequest:w}};import{isRef as Ue,toValue as qe}from"vue";function me(o,t=200,e=!1,n=!0,s=!1){return Be(Ne(t,e,n,s),o)}function Be(o,t){function e(...n){return new Promise((s,r)=>{Promise.resolve(o(()=>t.apply(this,n),{fn:t,thisArg:this,args:n})).then(s).catch(r)})}return e}var fe=()=>{};function Ne(...o){let t=0,e,n=!0,s=fe,r,a,c,u,f;!Ue(o[0])&&typeof o[0]=="object"?{delay:a,trailing:c=!0,leading:u=!0,rejectOnCancel:f=!1}=o[0]:[a,c=!0,u=!0,f=!1]=o;let v=()=>{e&&(clearTimeout(e),e=void 0,s(),s=fe)};return x=>{let A=qe(a),b=Date.now()-t,m=()=>r=x();return v(),A<=0?(t=Date.now(),m()):(b>A?(t=Date.now(),(u||!n)&&m()):c&&(r=new Promise((h,T)=>{s=f?T:h,e=setTimeout(()=>{t=Date.now(),n=!0,h(m()),v()},Math.max(0,A-b))})),!u&&!e&&(e=setTimeout(()=>n=!0,A)),n=!1,r)}}var It=o=>{let t=o.storage||ne(),e=he([]),n=new Map,s=new Map,r=he(null),a=Fe(()=>{let i=r.value;if(!i)return null;let l=e.value.find(y=>y.id===i);if(!l)return null;let d=n.get(i);return d?{...l,engine:d}:null}),c=i=>{if(!t?.saveMessages)return;let l=i||r.value,d=e.value.find(S=>S.id===l);if(!d)return;d.updatedAt=Date.now(),t?.saveConversation?.(d);let y=n.get(d.id);y&&t.saveMessages(d.id,y.messages.value)},u=(i,l)=>{if(!o.autoSaveMessages||!t?.saveMessages)return;let d=s.get(i);d&&d();let y=o.autoSaveThrottle??1e3,S=me(()=>{c(i)},y,!0,!0),w=Ke(l.messages,S,{deep:!0});s.set(i,w)},f=i=>{let l=s.get(i);l&&(l(),s.delete(i))};t?.loadConversations&&Promise.resolve(t.loadConversations()).then(i=>{if(!i?.length)return;if(e.value.length===0){e.value=i;return}let l=new Map(e.value.map(d=>[d.id,d]));i.forEach(d=>{l.has(d.id)||l.set(d.id,d)}),e.value=Array.from(l.values())}).catch(i=>{console.error("[useConversation] loadConversations failed:",i)});let v=async(i,l)=>{let d=n.get(i);if(d)return d;let y=l?.initialMessages??o.useMessageOptions.initialMessages??[];if(t?.loadMessages)try{y=await t.loadMessages(i)}catch(w){console.error("[useConversation] loadMessages failed:",w)}let S=re({...o.useMessageOptions,...l,initialMessages:y});return n.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:l=R(),title:d,metadata:y,useMessageOptions:S}=i||{},w=Date.now(),U={id:l,title:d,createdAt:w,updatedAt:w,metadata:y};e.value.unshift(U);let I=re({...o.useMessageOptions,...S});return n.set(l,I),u(l,I),t?.saveConversation?.(U),t?.saveMessages?.(l,I.messages.value),r.value=l,a.value},A=i=>{let{excludeId:l}=i||{};n.forEach((d,y)=>{if(l&&y===l)return;d.isProcessing?.value||(f(y),n.delete(y))})};return{conversations:e,activeConversationId:r,activeConversation:a,createConversation:x,switchConversation:async i=>i?r.value===i?a.value:e.value.find(d=>d.id===i)?(await v(i),A({excludeId:i}),r.value=i,a.value):null:null,deleteConversation:async i=>{let l=e.value.findIndex(y=>y.id===i);if(l===-1)return;await n.get(i)?.abortRequest(),f(i),n.delete(i),e.value.splice(l,1),t?.deleteConversation?.(i),r.value===i&&(r.value=null,A())},clear:()=>{e.value.map(l=>l.id).forEach(l=>{t?.deleteConversation?.(l)}),n.forEach(l=>{l.abortRequest()}),s.forEach(l=>{l()}),e.value=[],n.clear(),s.clear(),r.value=null},updateConversationTitle:(i,l)=>{let d=e.value.find(y=>y.id===i);d&&(d.title=l,d.updatedAt=Date.now(),t?.saveConversation?.(d))},saveMessages:c,sendMessage:i=>{a.value?.engine.sendMessage(i)},abortActiveRequest:async()=>{await a.value?.engine.abortRequest()}}};export{oe as AIClient,H as BaseModelProvider,Ee as EXCLUDE_MODE_REMOVE,Ce as ErrorType,J as IndexedDBStrategy,$ as LocalStorageStrategy,K as OpenAIProvider,ve as StreamEventType,be as extractTextFromResponse,ae as fallbackRolePlugin,Me as formatMessages,te as handleSSEStream,we as indexedDBStorageStrategyFactory,ie as lengthPlugin,ne as localStorageStrategyFactory,Re as sseStreamToGenerator,le as thinkingPlugin,yt as toolPlugin,It as useConversation,re as useMessage};
|
|
3
|
+
`);r=R.pop()||"";for(let x of R)if(x.trim()!==""){if(x.trim()==="data: [DONE]"){c&&(a=c),t.onDone(a);continue}try{let A=x.match(/^data: (.+)$/m);if(!A)continue;let M=JSON.parse(A[1]);t.onData(M),c=M.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(d){if(e?.aborted)return;throw d}}function Re(o){return o.map(t=>typeof t=="object"&&"role"in t&&"content"in t?{role:t.role,content:String(t.content),...t.name?{name:t.name}:{}}:typeof t=="string"?{role:"user",content:t}:{role:"user",content:String(t)})}function xe(o){return!o.choices||!o.choices.length?"":o.choices[0].message?.content||""}function oe(o="The operation was aborted"){let t=new Error(o);return t.name="AbortError",t}async function*Ae(o,t={}){let e=o.body?.getReader();if(!e)throw new Error("ReadableStream not supported");let{signal:n}=t,s=new TextDecoder,r="",a=()=>{e.cancel()};n?.addEventListener("abort",a);try{for(;;){if(n?.aborted)throw oe();let c;try{c=await e.read()}catch(x){throw n?.aborted?oe():x}let{done:d,value:f}=c;if(d){if(n?.aborted)throw oe();return}let v=s.decode(f,{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(M){console.warn("Failed to parse SSE data:",A,M)}}}}finally{n?.removeEventListener("abort",a),e.releaseLock()}}var j=class extends G{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 n={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...e.options,stream:!1},s={method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)};this.apiKey&&Object.assign(s.headers,{Authorization:`Bearer ${this.apiKey}`});let r=await fetch(`${this.baseURL}/chat/completions`,s);if(!r.ok){let a=await r.text();throw new Error(`HTTP error! status: ${r.status}, details: ${a}`)}return await r.json()}catch(n){throw te(n)}}async chatStream(e,n){let{signal:s,...r}=e.options||{};try{this.validateRequest(e);let a={model:e.options?.model||this.config.defaultModel||this.defaultModel,messages:e.messages,...r,stream:!0},c={method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,Accept:"text/event-stream"},body:JSON.stringify(a),signal:s};this.apiKey&&Object.assign(c.headers,{Authorization:`Bearer ${this.apiKey}`});let d=await fetch(`${this.baseURL}/chat/completions`,c);if(!d.ok){let f=await d.text();throw new Error(`HTTP error! status: ${d.status}, details: ${f}`)}await ne(d,n,s)}catch(a){if(s?.aborted)return;n.onError(te(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 se=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 j({...e,...t})}else return new j(t)}async chat(t){return this.provider.chat(t)}async chatStream(t,e){let n={...t,options:{...t.options,stream:!0}};return this.provider.chatStream(n,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 we}from"vue";function V(o,t=new WeakMap){if(o==null||typeof o!="object")return o;try{let e=we(o);if(t.has(e))return t.get(e);if(Array.isArray(e)){let s=[];return t.set(e,s),s.push(...e.map(r=>V(r,t))),s}if(e instanceof Date||e instanceof RegExp||e instanceof ArrayBuffer||e instanceof Blob)return e;let n={};t.set(e,n);for(let s of Object.keys(e)){let r=Object.getOwnPropertyDescriptor(e,s);if(!r||r.get||r.set)continue;let a=e[s];typeof a!="function"&&typeof a!="symbol"&&(n[s]=V(a,t))}return n}catch(e){return console.warn("unwrapProxy error:",e),Array.isArray(o)?[]:{}}}var X=o=>o.map(t=>{let{renderContent:e,...n}=t;if(!Array.isArray(e))return t;let s=e.filter(a=>a.type==="collapsible-text"),r=e.filter(a=>a.type==="markdown"||a.type==="text");return s.length>0&&(n.reasoning_content=s.map(a=>a.content).join("")),r.length>0&&(n.content=r.map(a=>a.content).join("")),n});var $=o=>{let t=localStorage.getItem(o);return t?JSON.parse(t):[]},J=class{constructor(t="tiny-robot-ai-conversations"){this.storageKey=t}saveConversation(t){try{let e=$(this.storageKey),n=e.findIndex(s=>s.id===t.id);n!==-1?Object.assign(e[n],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 $(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 n=$(this.storageKey),s=n.findIndex(r=>r.id===t);s!==-1&&(n[s].messages=e),localStorage.setItem(this.storageKey,JSON.stringify(n))}catch(n){console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",n)}}loadMessages(t){try{let n=$(this.storageKey).find(r=>r.id===t);return X(n?.messages||[])}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}deleteConversation(t){let e=$(this.storageKey),n=e.findIndex(s=>s.id===t);n!==-1&&e.splice(n,1),localStorage.setItem(this.storageKey,JSON.stringify(e))}};import{openDB as Te}from"idb";var z=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 Te(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 n=await(await this.getDB()).get("messages",t);return n?X(n.messages):[]}catch(e){return console.error("\u52A0\u8F7D\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",e),[]}}async saveConversation(t){try{let e=await this.getDB(),n=V(t);await e.put("conversations",n)}catch(e){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u5931\u8D25:",e),e}}async saveMessages(t,e){try{let n=await this.getDB(),s=V(e);await n.put("messages",{conversationId:t,messages:s})}catch(n){throw console.error("\u4FDD\u5B58\u4F1A\u8BDD\u6D88\u606F\u5931\u8D25:",n),n}}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 re(o={}){return new J(o.key||"tiny-robot-ai-conversations")}function Se(o={}){return new z(o.dbName||"tiny-robot-ai-db",o.dbVersion||1)}import{computed as je,ref as Ce,watch as Le}from"vue";import{computed as _e,reactive as De,ref as Y,watch as Ue}from"vue";var le=(o={})=>{let{fallbackRole:t="assistant",...e}=o;return{name:"fallbackRole",...e,onBeforeRequest(n){let{requestBody:s}=n;return s.messages=s.messages.map(r=>({...r,role:r.role||t})),e.onBeforeRequest?.(n)}}};var ce=(o={})=>{let{continueContent:t="Please continue with your previous answer.",...e}=o;return{name:"length",...e,onAfterRequest:async n=>{let{lastChoice:s,appendMessage:r,requestNext:a}=n;return s?.finish_reason==="length"&&(r({role:"user",content:t}),a()),e.onAfterRequest?.(n)}}};var ue=(o={})=>({name:"thinking",...o,onCompletionChunk(t){let{choice:e,currentMessage:n}=t,r=typeof(e?.message?.reasoning_content||e?.delta?.reasoning_content)=="string";return n.state?n.state.thinking=r:n.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 Ee}from"vue";var L=class extends Error{constructor(t){super(t),this.name="AbortError"}};function Pe(o){if(o.aborted)return{promise:Promise.reject(new L(String(o.reason??"Aborted"))),cleanup:()=>{}};let t=null;return{promise:new Promise((s,r)=>{t=()=>{r(new L(String(o.reason??"Aborted")))},o.addEventListener("abort",t,{once:!0})}),cleanup:()=>{t&&(o.removeEventListener("abort",t),t=null)}}}function pe(o,t){let{promise:e,cleanup:n}=Pe(t);return Promise.race([o,e]).finally(n)}function fe(o,t){let e={};for(let n in o)t.includes(n)&&(e[n]=o[n]);return e}function me(o,t){let e={};for(let n in o)t.includes(n)||(e[n]=o[n]);return e}async function*Q(o){if(de(o)){yield*o;return}let t=await o;if(de(t)){yield*t;return}yield t}function de(o){return o&&typeof o=="object"&&typeof o[Symbol.asyncIterator]=="function"}var ae=o=>typeof o=="object"&&o!==null,ge=o=>ae(o)&&typeof o.index=="number",W=(o,t)=>{for(let[e,n]of Object.entries(t)){let s=o[e];if(s)if(typeof s=="string"&&typeof n=="string")e==="type"&&s||(o[e]=s+n);else if(Array.isArray(s)&&Array.isArray(n))if(s.every(r=>ge(r))&&n.every(r=>ge(r))){let r=new Map(s.map(f=>[f.index,f])),a=new Map(n.map(f=>[f.index,f]));for(let[f,v]of a)if(r.has(f)){let R=r.get(f);r.set(f,W(R,v))}else r.set(f,v);let c=Math.max(...Array.from(r.keys()),-1)+1,d=c>s.length?Array.from({length:c}):s;for(let[f,v]of r)d[f]=v;o[e]=d}else o[e]=[...s,...n];else ae(s)&&ae(n)&&(o[e]=W(s,n));else o[e]=n}return o};function Oe(o,t){let e=[];for(let n=0;n<o.length;n++){let s=o[n];if(s.role==="assistant"&&s.tool_calls&&s.tool_calls.length>0){let r=new Set(s.tool_calls.map(d=>d.id)),a=new Set;for(let d=n+1;d<o.length;d++){let f=o[d];f.role==="tool"&&f.tool_call_id&&r.has(f.tool_call_id)&&a.add(f.tool_call_id)}let c=s.tool_calls.map(d=>d.id).filter(d=>!a.has(d));c.length>0&&e.push({insertAfterIndex:n,missingToolCallIds:c})}}for(let n=e.length-1;n>=0;n--){let{insertAfterIndex:s,missingToolCallIds:r}=e[n],a=r.map(c=>({role:"tool",tool_call_id:c,content:t}));o.splice(s+1,0,...a)}}var Ie="remove";function ke(o,t,e,n){let s=o.filter(a=>a[e]),r=new Set(s.flatMap(a=>a.tool_calls?.map(c=>c.id)??[]));if(t===Ie)for(let a=o.length-1;a>=0;a--){let c=o[a];(c[e]||c[n]||c.tool_call_id&&r.has(c.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[n]=!0,delete a[e])}var vt=o=>{let{getTools:t,beforeCallTools:e,callTool:n,onToolCallStart:s,onToolCallEnd:r,toolCallCancelledContent:a="Tool call cancelled.",toolCallFailedContent:c="Tool call failed.",autoFillMissingToolMessages:d=!1,excludeToolMessagesNextTurn:f=!1,...v}=o,R=Symbol("doNotSendNextTurn"),x=Symbol("doNotSend"),A=(...m)=>{let[h,{primaryMessage:T}]=m;T.state.toolCall[h.id].status="running",s?.(...m)},M=(...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 d&&Oe(h,a),f&&ke(h,f,R,x),v.onTurnStart?.(m)},onBeforeRequest:async m=>{let{messages:h,requestBody:T}=m;f===!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:l}=m;if(T?.finish_reason!=="tool_calls"||!h.tool_calls?.length)return;f&&(h[R]=!0),i("processing","calling-tools"),await e?.(h.tool_calls,{...m,currentMessage:h});let g=h.tool_calls.map(async y=>{let S=Math.floor(Date.now()/1e3),w=Ee({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 k=n(y,U),B=Q(k);for await(let u of B){if(typeof u=="string")w.content+=u;else{let p={};try{p=JSON.parse(w.content||"{}")}catch(b){console.warn(b)}w.content=JSON.stringify(W(p,u))}w.metadata.updatedAt=Math.floor(Date.now()/1e3)}M(y,{...U,status:"success"})}catch(k){let B=k instanceof Error?k:new Error(String(k));if(E.aborted){M(y,{...U,status:"cancelled",error:B});return}console.error(k),w.content.length===0&&(w.content=c),M(y,{...U,status:"failed",error:B})}});return await Promise.all(g),l(),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 qe=o=>{let t=[];for(let e of o){if(e.name){let n=t.findIndex(s=>s.name===e.name);n!==-1&&t.splice(n,1)}t.push(e)}return t},ie=o=>{let{initialMessages:t=[],requestMessageFields:e=[],requestMessageFieldsExclude:n=["state","metadata","loading"],plugins:s=[],onCompletionChunk:r}=o,a=Y("idle"),c=Y(void 0),d=Y(t),f=Y(o.responseProvider),v=null,R=[],x={},A=[le(),ue(),ce()],M=qe(A.concat(s)),m=_e(()=>a.value==="processing"),h=async u=>{if(!u||!u.trim()){console.warn("Cannot send empty message");return}if(m.value){console.warn("Cannot send message while processing is in progress");return}let p=Math.floor(Date.now()/1e3);d.value.push({role:"user",content:u.trim(),metadata:{createdAt:p,updatedAt:p}}),R.push(d.value[d.value.length-1]),await w()},T=async(...u)=>{if(m.value){console.warn("Cannot send message while processing is in progress");return}d.value.push(...u),R.push(...u),await w()},O=u=>{let p=u;return e.length&&(p=p.map(b=>fe(b,e))),n.length&&(p=p.map(b=>me(b,n))),p},E=(u,p)=>{a.value=u,u==="processing"?c.value=p||"requesting":c.value=void 0},i=u=>{Object.assign(x,u)},l=u=>({messages:d.value,currentTurn:R,requestState:a.value,processingState:c.value,plugins:M,abortSignal:u,setRequestState:E,customContext:x,setCustomContext:i}),g=(u,p)=>typeof u.disabled=="function"?u.disabled(p):!!u.disabled,y=u=>!u||Object.keys(u).length===0?!1:Object.values(u).some(p=>!!p),S=async(u,p)=>{E("processing","requesting");let b=new Proxy({messages:O(d.value)},{set(D,P,N){return P==="messages"?(D.messages=O(N),!0):(D[P]=N,!0)}}),I=l(p);for(let D of M.filter(P=>!g(P,I)))await D.onBeforeRequest?.({...I,requestBody:b});let C=De({role:"",content:"",loading:!0});B(C);let _,Z=u(b,p),q=Q(Z);for await(let D of q){E("processing","completing"),C.loading&&(C.loading=void 0);let P=D.choices?.find(K=>K.index===0);if(P){_=P;let K=()=>{C.metadata||(C.metadata={});let{created:H,...ve}=D;C.metadata.createdAt=H,C.metadata.updatedAt=Math.floor(Date.now()/1e3),Object.assign(C.metadata,ve);let ee=y(P.delta)?P.delta:P.message;ee||(ee=P.delta||P.message||{}),W(C,ee)};if(r){let H=l(p);r({...H,chunk:D,choice:P,currentMessage:C},K)}else K()}let N=l(p);for(let K of M.filter(H=>!g(H,N)))K.onCompletionChunk?.({...N,abortSignal:p,chunk:D,choice:P,currentMessage:C})}await k(C,u,p,_)},w=async()=>{let u=new AbortController;v=u,x={};try{E("processing","requesting");let p=l(u.signal);for(let C of M.filter(_=>!g(_,p)))await C.onTurnStart?.(p);let b=f.value;try{await S(b,u.signal),E("completed")}catch(C){if(u.signal.aborted||C instanceof L||C instanceof Error&&C.name==="AbortError")E("aborted");else throw C}let I=l(u.signal);for(let C of M.filter(_=>!g(_,I)))await C.onTurnEnd?.(I)}catch(p){E("error");let b=!1,I=l(u.signal);for(let C of M.filter(_=>!g(_,I)))C.onError&&(b=!0,C.onError({...I,error:p}));if(!b)throw p}finally{let p=l(u.signal);for(let b of M.filter(I=>!g(I,p)))try{b.onFinally?.(p)}catch(I){console.error(`Error in onFinally hook for plugin ${b.name||"Anonymous"}:`,I)}v=null,R.slice(-1)[0]&&(R.slice(-1)[0].loading=void 0),R=[]}},U=async()=>{v?.abort(),m.value&&await new Promise(u=>{let p=Ue(m,b=>{b||(p(),u())},{immediate:!0})})},k=async(u,p,b,I)=>{let C=!1,_=l(b),Z=M.filter(q=>!g(q,_)).map(q=>{if(!q.onAfterRequest)return null;let D=N=>{B(N)},P=()=>{C=!0};return q.onAfterRequest({..._,currentMessage:u,lastChoice:I,appendMessage:D,requestNext:P})}).filter(q=>q!==null);await pe(Promise.all(Z),b),C&&await S(p,b)},B=u=>{let p=Array.isArray(u)?u:[u];d.value.push(...p),R.push(...p)};return{requestState:a,processingState:c,messages:d,responseProvider:f,isProcessing:m,sendMessage:h,send:T,abortRequest:U}};import{isRef as Be,toValue as Ne}from"vue";function ye(o,t=200,e=!1,n=!0,s=!1){return Fe(Ke(t,e,n,s),o)}function Fe(o,t){function e(...n){return new Promise((s,r)=>{Promise.resolve(o(()=>t.apply(this,n),{fn:t,thisArg:this,args:n})).then(s).catch(r)})}return e}var he=()=>{};function Ke(...o){let t=0,e,n=!0,s=he,r,a,c,d,f;!Be(o[0])&&typeof o[0]=="object"?{delay:a,trailing:c=!0,leading:d=!0,rejectOnCancel:f=!1}=o[0]:[a,c=!0,d=!0,f=!1]=o;let v=()=>{e&&(clearTimeout(e),e=void 0,s(),s=he)};return x=>{let A=Ne(a),M=Date.now()-t,m=()=>r=x();return v(),A<=0?(t=Date.now(),m()):(M>A?(t=Date.now(),(d||!n)&&m()):c&&(r=new Promise((h,T)=>{s=f?T:h,e=setTimeout(()=>{t=Date.now(),n=!0,h(m()),v()},Math.max(0,A-M))})),!d&&!e&&(e=setTimeout(()=>n=!0,A)),n=!1,r)}}var _t=o=>{let t=o.storage||re(),e=Ce([]),n=new Map,s=new Map,r=Ce(null),a=je(()=>{let i=r.value;if(!i)return null;let l=e.value.find(y=>y.id===i);if(!l)return null;let g=n.get(i);return g?{...l,engine:g}:null}),c=i=>{if(!t?.saveMessages)return;let l=i||r.value,g=e.value.find(S=>S.id===l);if(!g)return;g.updatedAt=Date.now(),t?.saveConversation?.(g);let y=n.get(g.id);y&&t.saveMessages(g.id,y.messages.value)},d=(i,l)=>{if(!o.autoSaveMessages||!t?.saveMessages)return;let g=s.get(i);g&&g();let y=o.autoSaveThrottle??1e3,S=ye(()=>{c(i)},y,!0,!0),w=Le(l.messages,S,{deep:!0});s.set(i,w)},f=i=>{let l=s.get(i);l&&(l(),s.delete(i))};t?.loadConversations&&Promise.resolve(t.loadConversations()).then(i=>{if(!i?.length)return[];if(e.value.length===0)return e.value=i,e.value;let l=new Map(e.value.map(g=>[g.id,g]));return i.forEach(g=>{l.has(g.id)||l.set(g.id,g)}),e.value=Array.from(l.values()),e.value}).then(i=>{o.onLoad?.(i)}).catch(i=>{console.error("[useConversation] loadConversations failed:",i)});let v=async(i,l)=>{let g=n.get(i);if(g)return g;let y=l?.initialMessages??o.useMessageOptions.initialMessages??[];if(t?.loadMessages)try{y=await t.loadMessages(i)}catch(w){console.error("[useConversation] loadMessages failed:",w)}let S=ie({...o.useMessageOptions,...l,initialMessages:y});return n.set(i,S),d(i,S),S};function R(){return Date.now().toString(36)+Math.random().toString(36).substring(2,9)}let x=i=>{let{id:l=R(),title:g,metadata:y,useMessageOptions:S}=i||{},w=Date.now(),U={id:l,title:g,createdAt:w,updatedAt:w,metadata:y};e.value.unshift(U);let k=ie({...o.useMessageOptions,...S});return n.set(l,k),d(l,k),t?.saveConversation?.(U),t?.saveMessages?.(l,k.messages.value),r.value=l,a.value},A=i=>{let{excludeId:l}=i||{};n.forEach((g,y)=>{if(l&&y===l)return;g.isProcessing?.value||(f(y),n.delete(y))})};return{conversations:e,activeConversationId:r,activeConversation:a,createConversation:x,switchConversation:async i=>i?r.value===i?a.value:e.value.find(g=>g.id===i)?(await v(i),A({excludeId:i}),r.value=i,a.value):null:null,deleteConversation:async i=>{let l=e.value.findIndex(y=>y.id===i);if(l===-1)return;await n.get(i)?.abortRequest(),f(i),n.delete(i),e.value.splice(l,1),t?.deleteConversation?.(i),r.value===i&&(r.value=null,A())},clear:()=>{e.value.map(l=>l.id).forEach(l=>{t?.deleteConversation?.(l)}),n.forEach(l=>{l.abortRequest()}),s.forEach(l=>{l()}),e.value=[],n.clear(),s.clear(),r.value=null},updateConversationTitle:(i,l)=>{let g=e.value.find(y=>y.id===i);g&&(g.title=l,g.updatedAt=Date.now(),t?.saveConversation?.(g))},saveMessages:c,sendMessage:i=>{a.value?.engine.sendMessage(i)},abortActiveRequest:async()=>{await a.value?.engine.abortRequest()}}};export{se as AIClient,G as BaseModelProvider,Ie as EXCLUDE_MODE_REMOVE,Me as ErrorType,z as IndexedDBStrategy,J as LocalStorageStrategy,j as OpenAIProvider,be as StreamEventType,xe as extractTextFromResponse,le as fallbackRolePlugin,Re as formatMessages,ne as handleSSEStream,Se as indexedDBStorageStrategyFactory,ce as lengthPlugin,re as localStorageStrategyFactory,Ae as sseStreamToGenerator,ue as thinkingPlugin,vt as toolPlugin,_t as useConversation,ie as useMessage};
|
package/package.json
CHANGED
|
@@ -1,10 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentiny/tiny-robot-kit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"homepage": "https://docs.opentiny.design/tiny-robot/",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/opentiny/tiny-robot.git"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/opentiny/tiny-robot/issues"
|
|
11
|
+
},
|
|
4
12
|
"publishConfig": {
|
|
5
13
|
"access": "public"
|
|
6
14
|
},
|
|
7
15
|
"description": "AI大模型请求与数据处理工具包",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"vue",
|
|
18
|
+
"vue3",
|
|
19
|
+
"ai",
|
|
20
|
+
"ai-client",
|
|
21
|
+
"ai-sdk",
|
|
22
|
+
"chat",
|
|
23
|
+
"chatbot",
|
|
24
|
+
"llm",
|
|
25
|
+
"openai",
|
|
26
|
+
"assistant",
|
|
27
|
+
"streaming",
|
|
28
|
+
"composables",
|
|
29
|
+
"conversation",
|
|
30
|
+
"model-provider",
|
|
31
|
+
"tiny-robot",
|
|
32
|
+
"opentiny"
|
|
33
|
+
],
|
|
8
34
|
"main": "dist/index.js",
|
|
9
35
|
"module": "dist/index.mjs",
|
|
10
36
|
"types": "dist/index.d.ts",
|
|
@@ -29,5 +55,5 @@
|
|
|
29
55
|
"dependencies": {
|
|
30
56
|
"idb": "^8.0.3"
|
|
31
57
|
},
|
|
32
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "e7d831041cd49068110e17c5b5f410ed7fa2706c"
|
|
33
59
|
}
|