ai-chat-ui-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +74 -0
- package/.github/actions/screenshot/action.yml +35 -0
- package/.github/workflows/pages.yml +46 -0
- package/README.md +285 -0
- package/docs/README.md +176 -0
- package/docs/api/components.md +344 -0
- package/docs/api/core.md +349 -0
- package/docs/chat-style-1-minimal.html +78 -0
- package/docs/chat-style-2-neon.html +74 -0
- package/docs/chat-style-3-glass.html +73 -0
- package/docs/chat-style-4-terminal.html +84 -0
- package/docs/chat-style-5-gradient.html +69 -0
- package/docs/chat-style-6-corporate.html +116 -0
- package/docs/examples/basic-chat.md +291 -0
- package/docs/examples/custom-plugins.md +431 -0
- package/docs/examples/multi-model.md +466 -0
- package/docs/guide/api-adapters.md +431 -0
- package/docs/guide/getting-started.md +244 -0
- package/docs/guide/headless-mode.md +508 -0
- package/docs/guide/plugins.md +416 -0
- package/docs/guide/themes.md +327 -0
- package/docs/index.html +256 -0
- package/docs/theme-preview-1-minimal.html +74 -0
- package/docs/theme-preview-2-neon.html +73 -0
- package/docs/theme-preview-3-glass.html +77 -0
- package/docs/theme-preview-4-terminal.html +86 -0
- package/docs/theme-preview-5-gradient.html +79 -0
- package/docs/theme-preview-6-corporate.html +71 -0
- package/examples/index.html +414 -0
- package/examples/react-app/App.tsx +131 -0
- package/examples/react-app/index.html +12 -0
- package/examples/react-app/main.tsx +15 -0
- package/examples/react-app/package.json +24 -0
- package/examples/vue-app/index.html +12 -0
- package/examples/vue-app/package.json +22 -0
- package/examples/vue-app/src/App.vue +145 -0
- package/examples/vue-app/src/main.ts +9 -0
- package/package.json +44 -0
- package/packages/components/package.json +25 -0
- package/packages/components/src/chat/chat.css +80 -0
- package/packages/components/src/chat/chat.ts +236 -0
- package/packages/components/src/index.ts +36 -0
- package/packages/components/src/input/input.css +52 -0
- package/packages/components/src/input/input.ts +116 -0
- package/packages/components/src/markdown/markdown.css +118 -0
- package/packages/components/src/markdown/markdown.ts +229 -0
- package/packages/components/src/message/message.css +56 -0
- package/packages/components/src/message/message.ts +72 -0
- package/packages/components/src/styles/global.css +43 -0
- package/packages/components/src/tool-call/tool-call.css +98 -0
- package/packages/components/src/tool-call/tool-call.ts +171 -0
- package/packages/components/src/types.ts +55 -0
- package/packages/components/src/utils/helpers.ts +128 -0
- package/packages/components/tsconfig.json +25 -0
- package/packages/components/tsup.config.ts +18 -0
- package/packages/core/package.json +47 -0
- package/packages/core/pnpm-lock.yaml +2032 -0
- package/packages/core/pnpm-workspace.yaml +2 -0
- package/packages/core/src/api/adapters.ts +717 -0
- package/packages/core/src/api/base.ts +210 -0
- package/packages/core/src/api/index.ts +54 -0
- package/packages/core/src/index.ts +93 -0
- package/packages/core/src/parser/latex.ts +274 -0
- package/packages/core/src/parser/markdown.test.ts +58 -0
- package/packages/core/src/parser/markdown.ts +206 -0
- package/packages/core/src/parser/mermaid.ts +276 -0
- package/packages/core/src/plugins/PluginManager.ts +232 -0
- package/packages/core/src/plugins/builtin.ts +406 -0
- package/packages/core/src/store/ChatStore.ts +163 -0
- package/packages/core/src/store/ModelConfigStore.ts +136 -0
- package/packages/core/src/store/ToolCallStore.ts +164 -0
- package/packages/core/src/store/base.ts +75 -0
- package/packages/core/src/types/index.ts +133 -0
- package/packages/core/tsup.config.ts +18 -0
- package/packages/themes/package.json +33 -0
- package/packages/themes/src/corporate/index.ts +52 -0
- package/packages/themes/src/corporate/theme.css +228 -0
- package/packages/themes/src/glass/index.ts +52 -0
- package/packages/themes/src/glass/theme.css +237 -0
- package/packages/themes/src/gradient/index.ts +53 -0
- package/packages/themes/src/gradient/theme.css +218 -0
- package/packages/themes/src/index.ts +13 -0
- package/packages/themes/src/minimal/index.ts +52 -0
- package/packages/themes/src/minimal/theme.css +198 -0
- package/packages/themes/src/neon/index.ts +52 -0
- package/packages/themes/src/neon/theme.css +233 -0
- package/packages/themes/src/terminal/index.ts +52 -0
- package/packages/themes/src/terminal/theme.css +235 -0
- package/packages/themes/src/types.ts +10 -0
- package/packages/themes/src/vite-env.d.ts +9 -0
- package/packages/themes/tsup.config.ts +21 -0
- package/pnpm-workspace.yaml +4 -0
- package/tsconfig.json +27 -0
- package/vite.config.ts +25 -0
- package/vitest.config.ts +28 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# API 适配器
|
|
2
|
+
|
|
3
|
+
API 适配器允许您将 AI Chat UI Kit 连接到不同的后端 API,无论它们使用何种数据格式。
|
|
4
|
+
|
|
5
|
+
## 为什么需要 API 适配器?
|
|
6
|
+
|
|
7
|
+
不同的 AI 服务有不同的 API 格式:
|
|
8
|
+
|
|
9
|
+
- **OpenAI API**: `{ messages: [{ role: 'user', content: '...' }] }`
|
|
10
|
+
- **Claude API**: `{ messages: [{ role: 'user', content: '...' }], model: '...' }`
|
|
11
|
+
- **自定义 API**: 可能有完全不同的格式
|
|
12
|
+
|
|
13
|
+
API 适配器的作用是将您的后端 API 格式转换为 AI Chat UI Kit 期望的格式。
|
|
14
|
+
|
|
15
|
+
## 适配器接口
|
|
16
|
+
|
|
17
|
+
### 基本结构
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
interface APIAdapter {
|
|
21
|
+
// 发送消息
|
|
22
|
+
sendMessage(request: ChatRequest): Promise<ChatResponse>;
|
|
23
|
+
|
|
24
|
+
// 流式发送消息(可选)
|
|
25
|
+
sendMessageStream?(request: ChatRequest, onChunk: (chunk: string) => void): Promise<void>;
|
|
26
|
+
|
|
27
|
+
// 获取消息历史(可选)
|
|
28
|
+
getHistory?(): Promise<Message[]>;
|
|
29
|
+
|
|
30
|
+
// 清除历史(可选)
|
|
31
|
+
clearHistory?(): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface ChatRequest {
|
|
35
|
+
message: string;
|
|
36
|
+
history?: Message[];
|
|
37
|
+
model?: string;
|
|
38
|
+
temperature?: number;
|
|
39
|
+
maxTokens?: number;
|
|
40
|
+
[key: string]: any; // 其他自定义参数
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface ChatResponse {
|
|
44
|
+
content: string;
|
|
45
|
+
role: 'assistant';
|
|
46
|
+
usage?: {
|
|
47
|
+
promptTokens: number;
|
|
48
|
+
completionTokens: number;
|
|
49
|
+
totalTokens: number;
|
|
50
|
+
};
|
|
51
|
+
[key: string]: any; // 其他自定义字段
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 创建适配器
|
|
56
|
+
|
|
57
|
+
### 示例 1:OpenAI API 适配器
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// adapters/openai.ts
|
|
61
|
+
import { APIAdapter, ChatRequest, ChatResponse } from '@ai-chat/core';
|
|
62
|
+
|
|
63
|
+
export class OpenAIAdapter implements APIAdapter {
|
|
64
|
+
private apiKey: string;
|
|
65
|
+
private model: string;
|
|
66
|
+
private baseURL: string;
|
|
67
|
+
|
|
68
|
+
constructor(config: { apiKey: string; model?: string; baseURL?: string }) {
|
|
69
|
+
this.apiKey = config.apiKey;
|
|
70
|
+
this.model = config.model || 'gpt-3.5-turbo';
|
|
71
|
+
this.baseURL = config.baseURL || 'https://api.openai.com/v1';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async sendMessage(request: ChatRequest): Promise<ChatResponse> {
|
|
75
|
+
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: {
|
|
78
|
+
'Content-Type': 'application/json',
|
|
79
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
80
|
+
},
|
|
81
|
+
body: JSON.stringify({
|
|
82
|
+
model: request.model || this.model,
|
|
83
|
+
messages: [
|
|
84
|
+
...(request.history || []).map(msg => ({
|
|
85
|
+
role: msg.role,
|
|
86
|
+
content: msg.content,
|
|
87
|
+
})),
|
|
88
|
+
{ role: 'user', content: request.message },
|
|
89
|
+
],
|
|
90
|
+
temperature: request.temperature,
|
|
91
|
+
max_tokens: request.maxTokens,
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw new Error(`OpenAI API error: ${response.statusText}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const data = await response.json();
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
content: data.choices[0].message.content,
|
|
103
|
+
role: 'assistant',
|
|
104
|
+
usage: {
|
|
105
|
+
promptTokens: data.usage.prompt_tokens,
|
|
106
|
+
completionTokens: data.usage.completion_tokens,
|
|
107
|
+
totalTokens: data.usage.total_tokens,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async sendMessageStream(
|
|
113
|
+
request: ChatRequest,
|
|
114
|
+
onChunk: (chunk: string) => void
|
|
115
|
+
): Promise<void> {
|
|
116
|
+
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: {
|
|
119
|
+
'Content-Type': 'application/json',
|
|
120
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
121
|
+
},
|
|
122
|
+
body: JSON.stringify({
|
|
123
|
+
model: request.model || this.model,
|
|
124
|
+
messages: [
|
|
125
|
+
...(request.history || []).map(msg => ({
|
|
126
|
+
role: msg.role,
|
|
127
|
+
content: msg.content,
|
|
128
|
+
})),
|
|
129
|
+
{ role: 'user', content: request.message },
|
|
130
|
+
],
|
|
131
|
+
stream: true,
|
|
132
|
+
}),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
throw new Error(`OpenAI API error: ${response.statusText}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const reader = response.body.getReader();
|
|
140
|
+
const decoder = new TextDecoder();
|
|
141
|
+
|
|
142
|
+
while (true) {
|
|
143
|
+
const { done, value } = await reader.read();
|
|
144
|
+
if (done) break;
|
|
145
|
+
|
|
146
|
+
const chunk = decoder.decode(value);
|
|
147
|
+
const lines = chunk.split('\n').filter(line => line.trim() !== '');
|
|
148
|
+
|
|
149
|
+
for (const line of lines) {
|
|
150
|
+
if (line.startsWith('data: ')) {
|
|
151
|
+
const data = JSON.parse(line.slice(6));
|
|
152
|
+
if (data.choices && data.choices[0].delta.content) {
|
|
153
|
+
onChunk(data.choices[0].delta.content);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 示例 2:Claude API 适配器
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// adapters/claude.ts
|
|
166
|
+
import { APIAdapter, ChatRequest, ChatResponse } from '@ai-chat/core';
|
|
167
|
+
|
|
168
|
+
export class ClaudeAdapter implements APIAdapter {
|
|
169
|
+
private apiKey: string;
|
|
170
|
+
private model: string;
|
|
171
|
+
private baseURL: string;
|
|
172
|
+
|
|
173
|
+
constructor(config: { apiKey: string; model?: string; baseURL?: string }) {
|
|
174
|
+
this.apiKey = config.apiKey;
|
|
175
|
+
this.model = config.model || 'claude-3-opus-20240229';
|
|
176
|
+
this.baseURL = config.baseURL || 'https://api.anthropic.com/v1';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async sendMessage(request: ChatRequest): Promise<ChatResponse> {
|
|
180
|
+
const response = await fetch(`${this.baseURL}/messages`, {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
headers: {
|
|
183
|
+
'Content-Type': 'application/json',
|
|
184
|
+
'x-api-key': this.apiKey,
|
|
185
|
+
'anthropic-version': '2023-06-01',
|
|
186
|
+
},
|
|
187
|
+
body: JSON.stringify({
|
|
188
|
+
model: this.model,
|
|
189
|
+
messages: [
|
|
190
|
+
...(request.history || []).map(msg => ({
|
|
191
|
+
role: msg.role === 'assistant' ? 'assistant' : 'user',
|
|
192
|
+
content: msg.content,
|
|
193
|
+
})),
|
|
194
|
+
{ role: 'user', content: request.message },
|
|
195
|
+
],
|
|
196
|
+
max_tokens: request.maxTokens || 4096,
|
|
197
|
+
}),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (!response.ok) {
|
|
201
|
+
throw new Error(`Claude API error: ${response.statusText}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const data = await response.json();
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
content: data.content[0].text,
|
|
208
|
+
role: 'assistant',
|
|
209
|
+
usage: {
|
|
210
|
+
promptTokens: data.usage.input_tokens,
|
|
211
|
+
completionTokens: data.usage.output_tokens,
|
|
212
|
+
totalTokens: data.usage.input_tokens + data.usage.output_tokens,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 示例 3:自定义 API 适配器
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// adapters/custom.ts
|
|
223
|
+
import { APIAdapter, ChatRequest, ChatResponse } from '@ai-chat/core';
|
|
224
|
+
|
|
225
|
+
export class CustomAPIAdapter implements APIAdapter {
|
|
226
|
+
private baseURL: string;
|
|
227
|
+
private token: string;
|
|
228
|
+
|
|
229
|
+
constructor(config: { baseURL: string; token: string }) {
|
|
230
|
+
this.baseURL = config.baseURL;
|
|
231
|
+
this.token = config.token;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async sendMessage(request: ChatRequest): Promise<ChatResponse> {
|
|
235
|
+
const response = await fetch(`${this.baseURL}/api/chat`, {
|
|
236
|
+
method: 'POST',
|
|
237
|
+
headers: {
|
|
238
|
+
'Content-Type': 'application/json',
|
|
239
|
+
'Authorization': `Bearer ${this.token}`,
|
|
240
|
+
},
|
|
241
|
+
body: JSON.stringify({
|
|
242
|
+
prompt: request.message, // 自定义字段名
|
|
243
|
+
// 转换格式
|
|
244
|
+
}),
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (!response.ok) {
|
|
248
|
+
throw new Error(`Custom API error: ${response.statusText}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const data = await response.json();
|
|
252
|
+
|
|
253
|
+
// 转换为标准格式
|
|
254
|
+
return {
|
|
255
|
+
content: data.reply, // 自定义字段名
|
|
256
|
+
role: 'assistant',
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## 使用适配器
|
|
263
|
+
|
|
264
|
+
### 在 Core 中使用
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// store.ts
|
|
268
|
+
import { createChatStore } from '@ai-chat/core';
|
|
269
|
+
import { OpenAIAdapter } from './adapters/openai';
|
|
270
|
+
|
|
271
|
+
// 创建适配器实例
|
|
272
|
+
const adapter = new OpenAIAdapter({
|
|
273
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
274
|
+
model: 'gpt-4',
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// 创建 store
|
|
278
|
+
export const chatStore = createChatStore({
|
|
279
|
+
async onMessage(message: string) {
|
|
280
|
+
const response = await adapter.sendMessage({
|
|
281
|
+
message,
|
|
282
|
+
history: chatStore.getMessages(),
|
|
283
|
+
});
|
|
284
|
+
return response.content;
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 在 Components 中使用
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// main.ts
|
|
293
|
+
import '@ai-chat/components';
|
|
294
|
+
import '@ai-chat/themes/default';
|
|
295
|
+
import { OpenAIAdapter } from './adapters/openai';
|
|
296
|
+
|
|
297
|
+
const adapter = new OpenAIAdapter({
|
|
298
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const chat = document.querySelector('ai-chat');
|
|
302
|
+
|
|
303
|
+
chat.setMessageHandler(async (message) => {
|
|
304
|
+
const response = await adapter.sendMessage({
|
|
305
|
+
message,
|
|
306
|
+
history: chat.getMessages(),
|
|
307
|
+
});
|
|
308
|
+
return response.content;
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 使用流式响应
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
const chat = document.querySelector('ai-chat');
|
|
316
|
+
|
|
317
|
+
chat.setMessageHandler(async (message) => {
|
|
318
|
+
let fullResponse = '';
|
|
319
|
+
|
|
320
|
+
await adapter.sendMessageStream(
|
|
321
|
+
{
|
|
322
|
+
message,
|
|
323
|
+
history: chat.getMessages(),
|
|
324
|
+
},
|
|
325
|
+
(chunk) => {
|
|
326
|
+
fullResponse += chunk;
|
|
327
|
+
// 实时更新 UI
|
|
328
|
+
chat.updateLastMessage(fullResponse);
|
|
329
|
+
}
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
return fullResponse;
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## 多模型支持
|
|
337
|
+
|
|
338
|
+
### 动态切换模型
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
class MultiModelAdapter implements APIAdapter {
|
|
342
|
+
private adapters: Map<string, APIAdapter> = new Map();
|
|
343
|
+
|
|
344
|
+
registerModel(name: string, adapter: APIAdapter) {
|
|
345
|
+
this.adapters.set(name, adapter);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async sendMessage(request: ChatRequest): Promise<ChatResponse> {
|
|
349
|
+
const model = request.model || 'default';
|
|
350
|
+
const adapter = this.adapters.get(model);
|
|
351
|
+
|
|
352
|
+
if (!adapter) {
|
|
353
|
+
throw new Error(`Model ${model} not found`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return adapter.sendMessage(request);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// 使用
|
|
361
|
+
const multiAdapter = new MultiModelAdapter();
|
|
362
|
+
multiAdapter.registerModel('gpt-4', new OpenAIAdapter({ model: 'gpt-4', /* ... */ }));
|
|
363
|
+
multiAdapter.registerModel('claude', new ClaudeAdapter({ /* ... */ }));
|
|
364
|
+
|
|
365
|
+
// 切换模型
|
|
366
|
+
chatStore.sendMessage('Hello', { model: 'gpt-4' });
|
|
367
|
+
chatStore.sendMessage('Hello', { model: 'claude' });
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## 错误处理
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
class ResilientAdapter implements APIAdapter {
|
|
374
|
+
private adapters: APIAdapter[];
|
|
375
|
+
private currentIndex: number = 0;
|
|
376
|
+
|
|
377
|
+
constructor(adapters: APIAdapter[]) {
|
|
378
|
+
this.adapters = adapters;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async sendMessage(request: ChatRequest): Promise<ChatResponse> {
|
|
382
|
+
let lastError: Error | null = null;
|
|
383
|
+
|
|
384
|
+
// 尝试所有适配器
|
|
385
|
+
for (let i = 0; i < this.adapters.length; i++) {
|
|
386
|
+
try {
|
|
387
|
+
const adapter = this.adapters[(this.currentIndex + i) % this.adapters.length];
|
|
388
|
+
const response = await adapter.sendMessage(request);
|
|
389
|
+
return response;
|
|
390
|
+
} catch (error) {
|
|
391
|
+
lastError = error as Error;
|
|
392
|
+
console.warn(`Adapter ${i} failed, trying next...`, error);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
throw lastError || new Error('All adapters failed');
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## 缓存适配器
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
class CachedAdapter implements APIAdapter {
|
|
405
|
+
private adapter: APIAdapter;
|
|
406
|
+
private cache: Map<string, ChatResponse> = new Map();
|
|
407
|
+
|
|
408
|
+
constructor(adapter: APIAdapter) {
|
|
409
|
+
this.adapter = adapter;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async sendMessage(request: ChatRequest): Promise<ChatResponse> {
|
|
413
|
+
const cacheKey = JSON.stringify(request);
|
|
414
|
+
|
|
415
|
+
if (this.cache.has(cacheKey)) {
|
|
416
|
+
return this.cache.get(cacheKey)!;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const response = await this.adapter.sendMessage(request);
|
|
420
|
+
this.cache.set(cacheKey, response);
|
|
421
|
+
|
|
422
|
+
return response;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## 下一步
|
|
428
|
+
|
|
429
|
+
- [插件开发](./plugins.md) - 学习如何扩展功能
|
|
430
|
+
- [主题定制](./themes.md) - 学习如何自定义主题
|
|
431
|
+
- [API 参考](../../api/) - 查看完整 API 文档
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# 快速开始
|
|
2
|
+
|
|
3
|
+
本指南将帮助您在 5 分钟内上手 AI Chat UI Kit。
|
|
4
|
+
|
|
5
|
+
## 前置要求
|
|
6
|
+
|
|
7
|
+
- Node.js >= 16.0.0
|
|
8
|
+
- 包管理器:PNPM(推荐)、NPM 或 Yarn
|
|
9
|
+
|
|
10
|
+
## 安装
|
|
11
|
+
|
|
12
|
+
### 使用 PNPM(推荐)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @ai-chat/core @ai-chat/components @ai-chat/themes
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 使用 NPM
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @ai-chat/core @ai-chat/components @ai-chat/themes
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 使用 Yarn
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
yarn add @ai-chat/core @ai-chat/components @ai-chat/themes
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 基本用法
|
|
31
|
+
|
|
32
|
+
### 原生 HTML 项目
|
|
33
|
+
|
|
34
|
+
1. 在 HTML 文件中引入组件和主题:
|
|
35
|
+
|
|
36
|
+
```html
|
|
37
|
+
<!DOCTYPE html>
|
|
38
|
+
<html lang="zh-CN">
|
|
39
|
+
<head>
|
|
40
|
+
<meta charset="UTF-8">
|
|
41
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
42
|
+
<title>AI Chat Demo</title>
|
|
43
|
+
<script type="module">
|
|
44
|
+
import '@ai-chat/components';
|
|
45
|
+
import '@ai-chat/themes/default';
|
|
46
|
+
</script>
|
|
47
|
+
<style>
|
|
48
|
+
html, body {
|
|
49
|
+
margin: 0;
|
|
50
|
+
padding: 0;
|
|
51
|
+
height: 100%;
|
|
52
|
+
}
|
|
53
|
+
#app {
|
|
54
|
+
height: 100vh;
|
|
55
|
+
}
|
|
56
|
+
</style>
|
|
57
|
+
</head>
|
|
58
|
+
<body>
|
|
59
|
+
<div id="app">
|
|
60
|
+
<ai-chat></ai-chat>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<script>
|
|
64
|
+
// 获取组件实例
|
|
65
|
+
const chat = document.querySelector('ai-chat');
|
|
66
|
+
|
|
67
|
+
// 设置消息处理回调
|
|
68
|
+
chat.setMessageHandler(async (message) => {
|
|
69
|
+
// 这里调用您的 AI API
|
|
70
|
+
const response = await fetch('/api/chat', {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: { 'Content-Type': 'application/json' },
|
|
73
|
+
body: JSON.stringify({ message })
|
|
74
|
+
});
|
|
75
|
+
const data = await response.json();
|
|
76
|
+
return data.reply;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// 添加欢迎消息
|
|
80
|
+
chat.addMessage({
|
|
81
|
+
role: 'assistant',
|
|
82
|
+
content: '你好!我是 AI 助手,有什么可以帮助你的吗?'
|
|
83
|
+
});
|
|
84
|
+
</script>
|
|
85
|
+
</body>
|
|
86
|
+
</html>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### React 项目
|
|
90
|
+
|
|
91
|
+
1. 安装依赖(同上)
|
|
92
|
+
|
|
93
|
+
2. 创建聊天组件:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// ChatApp.tsx
|
|
97
|
+
import React, { useEffect, useRef } from 'react';
|
|
98
|
+
import '@ai-chat/components';
|
|
99
|
+
import '@ai-chat/themes/default';
|
|
100
|
+
|
|
101
|
+
const ChatApp: React.FC = () => {
|
|
102
|
+
const chatRef = useRef<HTMLElement>(null);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (chatRef.current) {
|
|
106
|
+
// 设置消息处理回调
|
|
107
|
+
(chatRef.current as any).setMessageHandler(async (message: string) => {
|
|
108
|
+
// 调用您的 AI API
|
|
109
|
+
const response = await fetch('/api/chat', {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: { 'Content-Type': 'application/json' },
|
|
112
|
+
body: JSON.stringify({ message })
|
|
113
|
+
});
|
|
114
|
+
const data = await response.json();
|
|
115
|
+
return data.reply;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// 添加欢迎消息
|
|
119
|
+
(chatRef.current as any).addMessage({
|
|
120
|
+
role: 'assistant',
|
|
121
|
+
content: '你好!我是 AI 助手,有什么可以帮助你的吗?'
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}, []);
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div style={{ height: '100vh' }}>
|
|
128
|
+
<ai-chat ref={chatRef}></ai-chat>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export default ChatApp;
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
3. 在 App.tsx 中使用:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import React from 'react';
|
|
140
|
+
import ChatApp from './ChatApp';
|
|
141
|
+
|
|
142
|
+
function App() {
|
|
143
|
+
return <ChatApp />;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default App;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Vue 3 项目
|
|
150
|
+
|
|
151
|
+
1. 安装依赖(同上)
|
|
152
|
+
|
|
153
|
+
2. 创建聊天组件:
|
|
154
|
+
|
|
155
|
+
```vue
|
|
156
|
+
<!-- ChatApp.vue -->
|
|
157
|
+
<template>
|
|
158
|
+
<div style="height: 100vh">
|
|
159
|
+
<ai-chat ref="chatRef"></ai-chat>
|
|
160
|
+
</div>
|
|
161
|
+
</template>
|
|
162
|
+
|
|
163
|
+
<script setup lang="ts">
|
|
164
|
+
import { ref, onMounted } from 'vue';
|
|
165
|
+
import '@ai-chat/components';
|
|
166
|
+
import '@ai-chat/themes/default';
|
|
167
|
+
|
|
168
|
+
const chatRef = ref<HTMLElement>();
|
|
169
|
+
|
|
170
|
+
onMounted(() => {
|
|
171
|
+
if (chatRef.value) {
|
|
172
|
+
// 设置消息处理回调
|
|
173
|
+
(chatRef.value as any).setMessageHandler(async (message: string) => {
|
|
174
|
+
// 调用您的 AI API
|
|
175
|
+
const response = await fetch('/api/chat', {
|
|
176
|
+
method: 'POST',
|
|
177
|
+
headers: { 'Content-Type': 'application/json' },
|
|
178
|
+
body: JSON.stringify({ message })
|
|
179
|
+
});
|
|
180
|
+
const data = await response.json();
|
|
181
|
+
return data.reply;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// 添加欢迎消息
|
|
185
|
+
(chatRef.value as any).addMessage({
|
|
186
|
+
role: 'assistant',
|
|
187
|
+
content: '你好!我是 AI 助手,有什么可以帮助你的吗?'
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
</script>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
3. 在 App.vue 中使用:
|
|
195
|
+
|
|
196
|
+
```vue
|
|
197
|
+
<template>
|
|
198
|
+
<ChatApp />
|
|
199
|
+
</template>
|
|
200
|
+
|
|
201
|
+
<script setup lang="ts">
|
|
202
|
+
import ChatApp from './components/ChatApp.vue';
|
|
203
|
+
</script>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## 下一步
|
|
207
|
+
|
|
208
|
+
- [Headless 模式](./headless-mode.md) - 学习如何只使用逻辑层,自定义 UI
|
|
209
|
+
- [主题定制](./themes.md) - 学习如何自定义主题
|
|
210
|
+
- [插件开发](./plugins.md) - 学习如何扩展功能
|
|
211
|
+
- [API 参考](../../api/) - 查看完整 API 文档
|
|
212
|
+
|
|
213
|
+
## 常见问题
|
|
214
|
+
|
|
215
|
+
### Q: 如何切换主题?
|
|
216
|
+
|
|
217
|
+
A: 只需导入不同的主题包:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// 默认主题
|
|
221
|
+
import '@ai-chat/themes/default';
|
|
222
|
+
|
|
223
|
+
// 气泡主题(仿微信/QQ 风格)
|
|
224
|
+
import '@ai-chat/themes/bubble';
|
|
225
|
+
|
|
226
|
+
// 扁平主题
|
|
227
|
+
import '@ai-chat/themes/flat';
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Q: 如何自定义样式?
|
|
231
|
+
|
|
232
|
+
A: 所有主题都使用 CSS 变量,您可以在自己的 CSS 中覆盖这些变量:
|
|
233
|
+
|
|
234
|
+
```css
|
|
235
|
+
:root {
|
|
236
|
+
--ai-primary: #your-color;
|
|
237
|
+
--ai-message-user-bg: #your-color;
|
|
238
|
+
/* ... */
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Q: 如何兼容不同的 AI API?
|
|
243
|
+
|
|
244
|
+
A: 使用 API 适配器,详见 [API 适配器](./api-adapters.md)。
|