@opentiny/next-remoter 0.2.2 → 0.2.4
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 +839 -76
- package/dist/components/BubbleImageRenderer.vue.d.ts +15 -0
- package/dist/components/TinyRobotChat.vue.d.ts +303 -0
- package/dist/components/tokenUsage.vue.d.ts +46 -0
- package/dist/composable/CustomAgentModelProvider.d.ts +1 -23
- package/dist/composable/streamVisitor.d.ts +75 -0
- package/dist/composable/useConversationHistory.d.ts +24 -0
- package/dist/composable/useMessageRoles.d.ts +54 -0
- package/dist/composable/usePluginSession.d.ts +25 -0
- package/dist/composable/useSkill.d.ts +21 -22
- package/dist/multimodal/index.d.ts +6 -0
- package/dist/multimodal/useMultimodal.d.ts +210 -0
- package/dist/multimodal/utils.d.ts +23 -0
- package/dist/next-remoter-runtime.es.js +199151 -190389
- package/dist/next-remoter.cjs.js +49 -70
- package/dist/next-remoter.css +1 -1
- package/dist/next-remoter.es.js +14501 -13937
- package/dist/style.css +1 -1
- package/dist/types/model-config.d.ts +14 -0
- package/package.json +26 -7
package/README.md
CHANGED
|
@@ -1,62 +1,171 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OpenTiny Next Remoter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
一个基于 `@opentiny/tiny-robot` 开发的 Vue3 AI 对话组件,提供开箱即用的智能对话能力。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## ✨ 主要功能
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
- 🤖 **AI 对话**:支持与大语言模型进行智能对话
|
|
8
|
+
- 🎨 **自定义界面**:支持自定义欢迎界面和提示建议
|
|
9
|
+
- 🔌 **MCP 插件市场**:内置插件市场,支持添加和管理 MCP 服务
|
|
10
|
+
- 📱 **扫码添加应用**:支持扫码快速添加应用和工具
|
|
11
|
+
- 🔄 **多模型切换**:支持在多个大语言模型之间切换
|
|
12
|
+
- 🎯 **技能系统**:支持自定义技能和提示词
|
|
13
|
+
- 🎭 **多角色展示**:支持多角色消息展示,支持 Markdown 和工具调用渲染
|
|
14
|
+
- 🎪 **生成式 UI**:支持生成式 UI 渲染(可选)
|
|
15
|
+
- 🌐 **国际化支持**:支持中英文切换
|
|
9
16
|
|
|
10
|
-
##
|
|
17
|
+
## 📦 安装
|
|
11
18
|
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
:sessionId="sessionId"
|
|
17
|
-
:title="项目名字"
|
|
18
|
-
:llmConfig="llmConfig"
|
|
19
|
-
:custom-market-mcp-servers="customMarketMcpServers"
|
|
20
|
-
>
|
|
21
|
-
<template #welcome>
|
|
22
|
-
<!-- 自定义标题+图标
|
|
23
|
-
自定义的Promts, 点击后调用 sendMessage()
|
|
24
|
-
其它任意欢迎界面元素 -->
|
|
25
|
-
</template>
|
|
26
|
-
<template #suggestions>
|
|
27
|
-
<!-- 输入框上面的提示词。 有丰富的用法,并不仅是点击,插入一个模板,详见官网。 所以用插槽代替 -->
|
|
28
|
-
</template>
|
|
29
|
-
</tiny-remoter>
|
|
19
|
+
```bash
|
|
20
|
+
npm install @opentiny/next-remoter
|
|
21
|
+
# 或
|
|
22
|
+
pnpm add @opentiny/next-remoter
|
|
30
23
|
```
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
## 🏗️ 项目结构
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
next-remoter/
|
|
29
|
+
├── src/
|
|
30
|
+
│ ├── components/ # 组件实现
|
|
31
|
+
│ │ ├── TinyRobotChat.vue # 核心对话组件
|
|
32
|
+
│ │ └── QrCodeScan.vue # 二维码扫码组件
|
|
33
|
+
│ ├── composable/ # 组合式函数
|
|
34
|
+
│ │ └── usePlugin.ts # 插件管理逻辑
|
|
35
|
+
│ ├── index.ts # 导出 Vue 组件,供用户使用
|
|
36
|
+
│ └── App.vue # 部署在服务器上的扫码访问页面
|
|
37
|
+
├── package.json
|
|
38
|
+
└── README.md
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**说明:**
|
|
42
|
+
|
|
43
|
+
- `index.ts`:导出 `TinyRemoter` 组件供外部使用
|
|
44
|
+
- `App.vue`:可部署到服务器,提供扫码访问的独立页面
|
|
45
|
+
- `components/TinyRobotChat.vue`:核心对话组件实现
|
|
46
|
+
- `composable/usePlugin.ts`:MCP 插件管理的核心逻辑
|
|
47
|
+
|
|
48
|
+
## 🚀 快速开始
|
|
49
|
+
|
|
50
|
+
### 基本使用
|
|
51
|
+
|
|
52
|
+
```vue
|
|
53
|
+
<template>
|
|
54
|
+
<TinyRemoter
|
|
55
|
+
v-model:show="showAiChat"
|
|
56
|
+
:sessionId="sessionId"
|
|
57
|
+
title="我的AI助手"
|
|
58
|
+
/>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<script setup>
|
|
62
|
+
import { ref } from 'vue'
|
|
63
|
+
import { TinyRemoter } from '@opentiny/next-remoter'
|
|
64
|
+
|
|
65
|
+
const showAiChat = ref(false)
|
|
66
|
+
const sessionId = ref('your-session-id')
|
|
67
|
+
</script>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 完整使用示例
|
|
71
|
+
|
|
72
|
+
```vue
|
|
73
|
+
<template>
|
|
74
|
+
<TinyRemoter
|
|
75
|
+
ref="remoterRef"
|
|
76
|
+
v-model:show="showAiChat"
|
|
77
|
+
:sessionId="sessionId"
|
|
78
|
+
:title="项目名称"
|
|
79
|
+
:llmConfig="llmConfig"
|
|
80
|
+
:customMarketMcpServers="customMarketMcpServers"
|
|
81
|
+
>
|
|
82
|
+
<!-- 自定义欢迎界面 -->
|
|
83
|
+
<template #welcome>
|
|
84
|
+
<div class="welcome-content">
|
|
85
|
+
<h2>欢迎使用 AI 助手</h2>
|
|
86
|
+
<p>我可以帮您完成各种任务</p>
|
|
87
|
+
<button @click="sendPrompt('帮我分析数据')">分析数据</button>
|
|
88
|
+
</div>
|
|
89
|
+
</template>
|
|
90
|
+
|
|
91
|
+
<!-- 自定义输入框上方的提示建议 -->
|
|
92
|
+
<template #suggestions>
|
|
93
|
+
<div class="suggestions">
|
|
94
|
+
<button @click="sendPrompt('查询天气')">查询天气</button>
|
|
95
|
+
<button @click="sendPrompt('生成代码')">生成代码</button>
|
|
96
|
+
</div>
|
|
97
|
+
</template>
|
|
98
|
+
</TinyRemoter>
|
|
99
|
+
</template>
|
|
100
|
+
|
|
101
|
+
<script setup>
|
|
102
|
+
import { ref } from 'vue'
|
|
103
|
+
import { TinyRemoter } from '@opentiny/next-remoter'
|
|
35
104
|
|
|
36
105
|
const showAiChat = ref(false)
|
|
37
106
|
const remoterRef = ref()
|
|
107
|
+
const sessionId = ref('your-session-id')
|
|
38
108
|
|
|
39
|
-
// 配置自定义LLM
|
|
109
|
+
// 配置自定义 LLM
|
|
40
110
|
const llmConfig = {
|
|
41
111
|
apiKey: 'your-api-key',
|
|
42
112
|
baseURL: 'https://api.openai.com/v1',
|
|
43
|
-
providerType: 'openai'
|
|
113
|
+
providerType: 'openai',
|
|
114
|
+
model: 'gpt-4o'
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 自定义 MCP 服务器
|
|
118
|
+
const customMarketMcpServers = [
|
|
119
|
+
{
|
|
120
|
+
id: 'custom-server',
|
|
121
|
+
name: '自定义服务器',
|
|
122
|
+
description: '我的自定义 MCP 服务',
|
|
123
|
+
url: 'https://your-server.com/mcp',
|
|
124
|
+
type: 'sse',
|
|
125
|
+
enabled: false,
|
|
126
|
+
addState: 'idle',
|
|
127
|
+
tools: []
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
// 发送预设提示词
|
|
132
|
+
const sendPrompt = (message) => {
|
|
133
|
+
remoterRef.value?.sendMessage(message)
|
|
44
134
|
}
|
|
135
|
+
</script>
|
|
136
|
+
```
|
|
45
137
|
|
|
46
|
-
|
|
138
|
+
## 📖 使用场景
|
|
139
|
+
|
|
140
|
+
### 场景一:创建 WebMCP 连接
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { WebMcpClient, WebMcpServer, createMessageChannelPairTransport } from '@opentiny/next-sdk'
|
|
144
|
+
|
|
145
|
+
// 1. 创建消息通道
|
|
47
146
|
const [serverTransport, clientTransport] = createMessageChannelPairTransport()
|
|
48
|
-
const server = new WebMcpServer({ name: 'demo-server', version: '1.0.0' })
|
|
49
|
-
const client = new WebMcpClient({ name: 'demo-client', version: '1.0.0' })
|
|
50
147
|
|
|
148
|
+
// 2. 创建并连接 MCP 服务端
|
|
149
|
+
const server = new WebMcpServer({ name: 'demo-server', version: '1.0.0' })
|
|
51
150
|
await server.connect(serverTransport)
|
|
151
|
+
|
|
152
|
+
// 3. 创建并连接 MCP 客户端
|
|
153
|
+
const client = new WebMcpClient({ name: 'demo-client', version: '1.0.0' })
|
|
52
154
|
await client.connect(clientTransport)
|
|
53
155
|
|
|
156
|
+
// 4. 连接到远程 MCP 服务
|
|
54
157
|
const { sessionId } = await client.connect({
|
|
55
158
|
url: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp',
|
|
56
159
|
agent: true
|
|
57
160
|
})
|
|
161
|
+
```
|
|
58
162
|
|
|
59
|
-
|
|
163
|
+
### 场景二:创建页面浮动遥控器
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { createRemoter } from '@opentiny/next-sdk'
|
|
167
|
+
|
|
168
|
+
// 创建页面右下角的浮动遥控器
|
|
60
169
|
createRemoter({
|
|
61
170
|
sessionId,
|
|
62
171
|
qrCodeUrl: 'https://ai.opentiny.design/next-remoter',
|
|
@@ -64,94 +173,748 @@ createRemoter({
|
|
|
64
173
|
showAiChat.value = true
|
|
65
174
|
}
|
|
66
175
|
})
|
|
176
|
+
```
|
|
67
177
|
|
|
68
|
-
|
|
69
|
-
// v-model:fullscreen 双向绑定是否全屏
|
|
70
|
-
// v-model:show 双向绑定是否显示,内部关闭是emit('update:show',false)
|
|
71
|
-
// sessionId 必须传
|
|
72
|
-
// title 左上角的 container.title
|
|
73
|
-
// agentRoot 后端代理的地址,有默认值
|
|
178
|
+
## ⚙️ 组件属性
|
|
74
179
|
|
|
75
|
-
|
|
76
|
-
// expose({ sendMessage, abortRequest, messages, messageState,senderRef})
|
|
180
|
+
### 基础属性
|
|
77
181
|
|
|
78
|
-
|
|
182
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
183
|
+
|------|------|------|--------|------|
|
|
184
|
+
| `v-model:show` | `boolean` | 是 | - | 双向绑定是否显示对话框 |
|
|
185
|
+
| `sessionId` | `string` | 是 | - | 会话 ID |
|
|
186
|
+
| `title` | `string` | 否 | - | 对话框左上角标题 |
|
|
187
|
+
| `v-model:fullscreen` | `boolean` | 否 | `false` | 双向绑定是否全屏 |
|
|
188
|
+
| `agentRoot` | `string` | 否 | - | 后端代理地址 |
|
|
189
|
+
| `locale` | `'zh-CN' \| 'en-US'` | 否 | `'zh-CN'` | 国际化语言 |
|
|
190
|
+
| `mode` | `'remoter' \| 'chat-dialog'` | 否 | `'chat-dialog'` | 展示模式 |
|
|
191
|
+
| `systemPrompt` | `string` | 否 | - | 系统提示词 |
|
|
192
|
+
| `llmConfig` | `object` | 否 | - | 大语言模型配置 |
|
|
193
|
+
| `llmConfigs` | `array` | 否 | - | 多模型配置数组 |
|
|
194
|
+
| `v-model:selectedModelId` | `string` | 否 | - | 当前选中的模型 ID |
|
|
195
|
+
| `customMarketMcpServers` | `array` | 否 | - | 自定义 MCP 市场服务列表 |
|
|
196
|
+
| `skills` | `array` | 否 | - | 技能配置数组 |
|
|
197
|
+
| `inBrowserExt` | `boolean` | 否 | `false` | 是否运行在浏览器扩展中 |
|
|
198
|
+
| `genUiAble` | `boolean` | 否 | `false` | 是否支持生成式 UI |
|
|
199
|
+
| `genUiComponents` | `object` | 否 | - | 生成式 UI 组件 |
|
|
79
200
|
|
|
80
|
-
|
|
201
|
+
### LLM 配置(llmConfig)
|
|
81
202
|
|
|
82
|
-
TinyRemoter组件支持自定义大语言模型配置,统一通过 `llmConfig` 传入:
|
|
203
|
+
TinyRemoter 组件支持自定义大语言模型配置,统一通过 `llmConfig` 传入:
|
|
83
204
|
|
|
84
|
-
|
|
205
|
+
#### 方式一:使用 providerType 配置
|
|
85
206
|
|
|
86
207
|
```typescript
|
|
87
208
|
const llmConfig = {
|
|
88
209
|
apiKey: 'your-api-key',
|
|
89
210
|
baseURL: 'https://api.openai.com/v1',
|
|
90
|
-
providerType: 'openai' // 支持 'openai' | 'deepseek'
|
|
211
|
+
providerType: 'openai', // 支持 'openai' | 'deepseek'
|
|
212
|
+
model: 'gpt-4o',
|
|
213
|
+
maxSteps: 10
|
|
91
214
|
}
|
|
92
215
|
```
|
|
93
216
|
|
|
94
|
-
|
|
217
|
+
#### 方式二:使用自定义 Provider 实例
|
|
95
218
|
|
|
96
219
|
```typescript
|
|
97
220
|
import { createOpenAI } from '@ai-sdk/openai'
|
|
98
221
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
222
|
+
const llmConfig = {
|
|
223
|
+
llm: createOpenAI({
|
|
224
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
225
|
+
baseURL: 'https://api.openai.com/v1'
|
|
226
|
+
}),
|
|
227
|
+
model: 'gpt-4o'
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### 支持的 LLM 提供商
|
|
232
|
+
|
|
233
|
+
- ✅ OpenAI (`providerType: 'openai'`)
|
|
234
|
+
- ✅ DeepSeek (`providerType: 'deepseek'`)
|
|
235
|
+
- ✅ Anthropic Claude(通过自定义 Provider)
|
|
236
|
+
- ✅ 其他 ai-sdk 兼容的提供商
|
|
237
|
+
|
|
238
|
+
### 插槽
|
|
239
|
+
|
|
240
|
+
| 插槽名 | 说明 |
|
|
241
|
+
|--------|------|
|
|
242
|
+
| `#welcome` | 无对话消息时展示的欢迎界面,可自定义标题、图标、预设提示词等 |
|
|
243
|
+
| `#suggestions` | 输入框上方的提示建议区域 |
|
|
244
|
+
| `#operations` | 容器头部右侧的操作区域(新建会话、历史会话等) |
|
|
245
|
+
| `#header-actions` | MCP 插件市场头部的操作区域 |
|
|
246
|
+
|
|
247
|
+
### 导出方法和属性
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
defineExpose({
|
|
251
|
+
agent, // 大模型代理实例
|
|
252
|
+
welcomeIcon, // 欢迎图标
|
|
253
|
+
messages, // 对话消息列表
|
|
254
|
+
messageState, // 对话消息状态
|
|
255
|
+
roles, // 对话角色配置
|
|
256
|
+
inputMessage, // 输入框文本
|
|
257
|
+
senderRef, // 输入框组件实例
|
|
258
|
+
abortRequest, // 取消发送函数
|
|
259
|
+
sendMessage, // 发送消息函数
|
|
260
|
+
loadMcpServerToPlugin, // 加载 MCP 服务到插件
|
|
261
|
+
handleClientDisconnected, // 处理客户端断开
|
|
262
|
+
addMessage // 添加消息函数
|
|
102
263
|
})
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## 💡 使用示例
|
|
267
|
+
|
|
268
|
+
### 示例 1:使用 OpenAI
|
|
269
|
+
|
|
270
|
+
```vue
|
|
271
|
+
<template>
|
|
272
|
+
<TinyRemoter
|
|
273
|
+
v-model:show="show"
|
|
274
|
+
:sessionId="sessionId"
|
|
275
|
+
title="OpenAI 助手"
|
|
276
|
+
:llmConfig="openAIConfig"
|
|
277
|
+
/>
|
|
278
|
+
</template>
|
|
279
|
+
|
|
280
|
+
<script setup>
|
|
281
|
+
import { ref } from 'vue'
|
|
282
|
+
import { TinyRemoter } from '@opentiny/next-remoter'
|
|
283
|
+
|
|
284
|
+
const show = ref(false)
|
|
285
|
+
const sessionId = ref('session-001')
|
|
286
|
+
|
|
287
|
+
const openAIConfig = {
|
|
288
|
+
apiKey: import.meta.env.VITE_OPENAI_API_KEY,
|
|
289
|
+
baseURL: 'https://api.openai.com/v1',
|
|
290
|
+
providerType: 'openai',
|
|
291
|
+
model: 'gpt-4o',
|
|
292
|
+
maxSteps: 10
|
|
293
|
+
}
|
|
294
|
+
</script>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### 示例 2:使用 DeepSeek
|
|
298
|
+
|
|
299
|
+
```vue
|
|
300
|
+
<template>
|
|
301
|
+
<TinyRemoter
|
|
302
|
+
v-model:show="show"
|
|
303
|
+
:sessionId="sessionId"
|
|
304
|
+
title="DeepSeek 助手"
|
|
305
|
+
:llmConfig="deepSeekConfig"
|
|
306
|
+
/>
|
|
307
|
+
</template>
|
|
308
|
+
|
|
309
|
+
<script setup>
|
|
310
|
+
import { ref } from 'vue'
|
|
311
|
+
import { TinyRemoter } from '@opentiny/next-remoter'
|
|
312
|
+
|
|
313
|
+
const show = ref(false)
|
|
314
|
+
const sessionId = ref('session-002')
|
|
315
|
+
|
|
316
|
+
const deepSeekConfig = {
|
|
317
|
+
apiKey: import.meta.env.VITE_DEEPSEEK_API_KEY,
|
|
318
|
+
baseURL: 'https://api.deepseek.com',
|
|
319
|
+
providerType: 'deepseek',
|
|
320
|
+
model: 'deepseek-chat',
|
|
321
|
+
maxSteps: 15
|
|
322
|
+
}
|
|
323
|
+
</script>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 示例 3:使用 Anthropic Claude
|
|
327
|
+
|
|
328
|
+
```vue
|
|
329
|
+
<template>
|
|
330
|
+
<TinyRemoter
|
|
331
|
+
v-model:show="show"
|
|
332
|
+
:sessionId="sessionId"
|
|
333
|
+
title="Claude 助手"
|
|
334
|
+
:llmConfig="claudeConfig"
|
|
335
|
+
/>
|
|
336
|
+
</template>
|
|
337
|
+
|
|
338
|
+
<script setup>
|
|
339
|
+
import { ref } from 'vue'
|
|
340
|
+
import { TinyRemoter } from '@opentiny/next-remoter'
|
|
341
|
+
import { createAnthropic } from '@ai-sdk/anthropic'
|
|
342
|
+
|
|
343
|
+
const show = ref(false)
|
|
344
|
+
const sessionId = ref('session-003')
|
|
345
|
+
|
|
346
|
+
const claudeConfig = {
|
|
347
|
+
llm: createAnthropic({
|
|
348
|
+
apiKey: import.meta.env.VITE_ANTHROPIC_API_KEY
|
|
349
|
+
}),
|
|
350
|
+
model: 'claude-3-5-sonnet-20241022'
|
|
351
|
+
}
|
|
352
|
+
</script>
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### 示例 4:多模型切换
|
|
356
|
+
|
|
357
|
+
```vue
|
|
358
|
+
<template>
|
|
359
|
+
<TinyRemoter
|
|
360
|
+
v-model:show="show"
|
|
361
|
+
v-model:selected-model-id="selectedModelId"
|
|
362
|
+
:sessionId="sessionId"
|
|
363
|
+
title="智能助手"
|
|
364
|
+
:llmConfigs="modelConfigs"
|
|
365
|
+
/>
|
|
366
|
+
</template>
|
|
367
|
+
|
|
368
|
+
<script setup>
|
|
369
|
+
import { ref, watch } from 'vue'
|
|
370
|
+
import { TinyRemoter } from '@opentiny/next-remoter'
|
|
371
|
+
import IconOpenAI from './icons/openai.svg'
|
|
372
|
+
import IconDeepSeek from './icons/deepseek.svg'
|
|
373
|
+
|
|
374
|
+
const show = ref(false)
|
|
375
|
+
const sessionId = ref('session-004')
|
|
376
|
+
const selectedModelId = ref('gpt-4o')
|
|
377
|
+
|
|
378
|
+
const modelConfigs = [
|
|
379
|
+
{
|
|
380
|
+
id: 'gpt-4o',
|
|
381
|
+
label: 'GPT-4o',
|
|
382
|
+
icon: IconOpenAI,
|
|
383
|
+
isDefault: true,
|
|
384
|
+
apiKey: import.meta.env.VITE_OPENAI_API_KEY,
|
|
385
|
+
baseURL: 'https://api.openai.com/v1',
|
|
386
|
+
providerType: 'openai',
|
|
387
|
+
model: 'gpt-4o'
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
id: 'deepseek-v3',
|
|
391
|
+
label: 'DeepSeek V3',
|
|
392
|
+
icon: IconDeepSeek,
|
|
393
|
+
apiKey: import.meta.env.VITE_DEEPSEEK_API_KEY,
|
|
394
|
+
baseURL: 'https://api.deepseek.com',
|
|
395
|
+
providerType: 'deepseek',
|
|
396
|
+
model: 'deepseek-chat'
|
|
397
|
+
}
|
|
398
|
+
]
|
|
399
|
+
|
|
400
|
+
// 监听模型切换
|
|
401
|
+
watch(selectedModelId, (newModelId) => {
|
|
402
|
+
console.log('当前选中的模型:', newModelId)
|
|
403
|
+
})
|
|
404
|
+
</script>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### 示例 5:配置技能列表
|
|
408
|
+
|
|
409
|
+
```vue
|
|
410
|
+
<template>
|
|
411
|
+
<TinyRemoter
|
|
412
|
+
v-model:show="show"
|
|
413
|
+
:sessionId="sessionId"
|
|
414
|
+
title="技能助手"
|
|
415
|
+
:llmConfig="llmConfig"
|
|
416
|
+
:skills="skills"
|
|
417
|
+
/>
|
|
418
|
+
</template>
|
|
419
|
+
|
|
420
|
+
<script setup>
|
|
421
|
+
import { ref } from 'vue'
|
|
422
|
+
import { TinyRemoter } from '@opentiny/next-remoter'
|
|
423
|
+
|
|
424
|
+
const show = ref(false)
|
|
425
|
+
const sessionId = ref('session-005')
|
|
103
426
|
|
|
104
427
|
const llmConfig = {
|
|
105
|
-
|
|
428
|
+
apiKey: import.meta.env.VITE_OPENAI_API_KEY,
|
|
429
|
+
baseURL: 'https://api.openai.com/v1',
|
|
430
|
+
providerType: 'openai',
|
|
431
|
+
model: 'gpt-4o'
|
|
106
432
|
}
|
|
433
|
+
|
|
434
|
+
// 在输入框输入 @ 可唤起技能列表
|
|
435
|
+
const skills = [
|
|
436
|
+
{
|
|
437
|
+
label: '办公助手',
|
|
438
|
+
value: '你是一个办公助手,擅长处理文档、邮件和日程安排'
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
label: '代码专家',
|
|
442
|
+
value: '你是一个代码专家,擅长编写和优化代码',
|
|
443
|
+
tools: ['codeAnalysis', 'codeGeneration']
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
label: '数据分析师',
|
|
447
|
+
value: '你是一个数据分析师,擅长数据分析和可视化'
|
|
448
|
+
}
|
|
449
|
+
]
|
|
450
|
+
</script>
|
|
107
451
|
```
|
|
108
452
|
|
|
109
|
-
|
|
453
|
+
### 示例 6:自定义插槽
|
|
454
|
+
|
|
455
|
+
```vue
|
|
456
|
+
<template>
|
|
457
|
+
<TinyRemoter
|
|
458
|
+
ref="robotRef"
|
|
459
|
+
v-model:show="show"
|
|
460
|
+
:sessionId="sessionId"
|
|
461
|
+
title="自定义助手"
|
|
462
|
+
:llmConfig="llmConfig"
|
|
463
|
+
>
|
|
464
|
+
<!-- 自定义欢迎界面 -->
|
|
465
|
+
<template #welcome>
|
|
466
|
+
<div class="custom-welcome">
|
|
467
|
+
<img src="./logo.png" alt="Logo" />
|
|
468
|
+
<h2>欢迎使用智能助手</h2>
|
|
469
|
+
<p>选择一个快速开始的功能</p>
|
|
470
|
+
<div class="quick-actions">
|
|
471
|
+
<button
|
|
472
|
+
v-for="action in quickActions"
|
|
473
|
+
:key="action.id"
|
|
474
|
+
@click="handleQuickAction(action)"
|
|
475
|
+
>
|
|
476
|
+
{{ action.label }}
|
|
477
|
+
</button>
|
|
478
|
+
</div>
|
|
479
|
+
</div>
|
|
480
|
+
</template>
|
|
481
|
+
|
|
482
|
+
<!-- 自定义提示建议 -->
|
|
483
|
+
<template #suggestions>
|
|
484
|
+
<div class="suggestion-pills">
|
|
485
|
+
<span
|
|
486
|
+
v-for="suggestion in suggestions"
|
|
487
|
+
:key="suggestion"
|
|
488
|
+
@click="handleSuggestion(suggestion)"
|
|
489
|
+
>
|
|
490
|
+
{{ suggestion }}
|
|
491
|
+
</span>
|
|
492
|
+
</div>
|
|
493
|
+
</template>
|
|
494
|
+
|
|
495
|
+
<!-- 自定义头部操作 -->
|
|
496
|
+
<template #operations>
|
|
497
|
+
<button @click="exportChat">导出对话</button>
|
|
498
|
+
<button @click="clearHistory">清空历史</button>
|
|
499
|
+
</template>
|
|
500
|
+
</TinyRemoter>
|
|
501
|
+
</template>
|
|
502
|
+
|
|
503
|
+
<script setup>
|
|
504
|
+
import { ref } from 'vue'
|
|
505
|
+
import { TinyRemoter } from '@opentiny/next-remoter'
|
|
506
|
+
|
|
507
|
+
const show = ref(false)
|
|
508
|
+
const robotRef = ref()
|
|
509
|
+
const sessionId = ref('session-006')
|
|
110
510
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
:
|
|
114
|
-
:
|
|
115
|
-
|
|
511
|
+
const llmConfig = {
|
|
512
|
+
apiKey: import.meta.env.VITE_OPENAI_API_KEY,
|
|
513
|
+
baseURL: 'https://api.openai.com/v1',
|
|
514
|
+
providerType: 'openai',
|
|
515
|
+
model: 'gpt-4o'
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const quickActions = [
|
|
519
|
+
{ id: 1, label: '📊 数据分析', prompt: '帮我分析这份数据' },
|
|
520
|
+
{ id: 2, label: '💻 代码生成', prompt: '帮我生成代码' },
|
|
521
|
+
{ id: 3, label: '📝 文档撰写', prompt: '帮我撰写文档' }
|
|
522
|
+
]
|
|
523
|
+
|
|
524
|
+
const suggestions = ['天气查询', '日程安排', '邮件撰写', '数据可视化']
|
|
525
|
+
|
|
526
|
+
const handleQuickAction = (action) => {
|
|
527
|
+
robotRef.value?.sendMessage(action.prompt)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const handleSuggestion = (suggestion) => {
|
|
531
|
+
robotRef.value?.sendMessage(suggestion)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const exportChat = () => {
|
|
535
|
+
const messages = robotRef.value?.messages
|
|
536
|
+
console.log('导出对话:', messages)
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const clearHistory = () => {
|
|
540
|
+
console.log('清空历史')
|
|
541
|
+
}
|
|
542
|
+
</script>
|
|
543
|
+
|
|
544
|
+
<style scoped>
|
|
545
|
+
.custom-welcome {
|
|
546
|
+
text-align: center;
|
|
547
|
+
padding: 40px 20px;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.quick-actions {
|
|
551
|
+
display: flex;
|
|
552
|
+
gap: 10px;
|
|
553
|
+
justify-content: center;
|
|
554
|
+
margin-top: 20px;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.suggestion-pills {
|
|
558
|
+
display: flex;
|
|
559
|
+
gap: 8px;
|
|
560
|
+
flex-wrap: wrap;
|
|
561
|
+
padding: 10px;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.suggestion-pills span {
|
|
565
|
+
padding: 6px 12px;
|
|
566
|
+
background: #f0f0f0;
|
|
567
|
+
border-radius: 16px;
|
|
568
|
+
cursor: pointer;
|
|
569
|
+
transition: all 0.2s;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.suggestion-pills span:hover {
|
|
573
|
+
background: #e0e0e0;
|
|
574
|
+
}
|
|
575
|
+
</style>
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
## 🔌 自定义 MCP 插件
|
|
579
|
+
|
|
580
|
+
`TinyRemoter` 通过 `customMarketMcpServers` 属性支持自定义 MCP 插件。传入的插件会与组件内置的 `DEFAULT_SERVERS` 合并,显示在插件市场中。
|
|
581
|
+
|
|
582
|
+
### 插件配置说明
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
interface PluginInfo {
|
|
586
|
+
id: string // 插件唯一标识(最终会拼成 plugin-${id})
|
|
587
|
+
name: string // 插件名称
|
|
588
|
+
description: string // 插件描述
|
|
589
|
+
icon?: string // 插件图标 URL
|
|
590
|
+
url: string // MCP 服务器地址
|
|
591
|
+
type: 'sse' | 'StreamableHTTP' // 协议类型
|
|
592
|
+
enabled: boolean // 是否已启用
|
|
593
|
+
addState: 'idle' | 'adding' | 'added' | 'error' // 添加状态
|
|
594
|
+
tools: any[] // 工具列表
|
|
595
|
+
}
|
|
116
596
|
```
|
|
117
597
|
|
|
118
|
-
###
|
|
598
|
+
### 使用示例
|
|
119
599
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
600
|
+
```vue
|
|
601
|
+
<template>
|
|
602
|
+
<TinyRemoter
|
|
603
|
+
v-model:show="show"
|
|
604
|
+
:sessionId="sessionId"
|
|
605
|
+
:customMarketMcpServers="customServers"
|
|
606
|
+
/>
|
|
607
|
+
</template>
|
|
123
608
|
|
|
124
|
-
|
|
609
|
+
<script setup>
|
|
610
|
+
import { ref } from 'vue'
|
|
611
|
+
import { TinyRemoter } from '@opentiny/next-remoter'
|
|
125
612
|
|
|
126
|
-
|
|
613
|
+
const show = ref(false)
|
|
614
|
+
const sessionId = ref('session-007')
|
|
127
615
|
|
|
128
|
-
|
|
129
|
-
const customMarketMcpServers = [
|
|
616
|
+
const customServers = [
|
|
130
617
|
{
|
|
131
618
|
id: 'ppt-mcp',
|
|
132
|
-
name: 'PPT
|
|
133
|
-
description: '可以创建、编辑、保存PPT文档',
|
|
134
|
-
icon: 'https://
|
|
135
|
-
url: 'https://
|
|
619
|
+
name: 'PPT 文档服务',
|
|
620
|
+
description: '可以创建、编辑、保存 PPT 文档',
|
|
621
|
+
icon: 'https://example.com/icons/ppt.png',
|
|
622
|
+
url: 'https://your-server.com/servers/ppt-mcp/sse',
|
|
136
623
|
type: 'sse',
|
|
137
624
|
enabled: false,
|
|
138
625
|
addState: 'idle',
|
|
139
626
|
tools: []
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
id: 'excel-mcp',
|
|
630
|
+
name: 'Excel 数据处理',
|
|
631
|
+
description: '支持 Excel 文件读取、分析和生成',
|
|
632
|
+
icon: 'https://example.com/icons/excel.png',
|
|
633
|
+
url: 'https://your-server.com/servers/excel-mcp/sse',
|
|
634
|
+
type: 'sse',
|
|
635
|
+
enabled: false,
|
|
636
|
+
addState: 'idle',
|
|
637
|
+
tools: []
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
id: 'database-mcp',
|
|
641
|
+
name: '数据库查询',
|
|
642
|
+
description: '支持多种数据库的查询操作',
|
|
643
|
+
url: 'https://your-server.com/servers/db-mcp',
|
|
644
|
+
type: 'StreamableHTTP',
|
|
645
|
+
enabled: false,
|
|
646
|
+
addState: 'idle',
|
|
647
|
+
tools: []
|
|
140
648
|
}
|
|
141
649
|
]
|
|
650
|
+
</script>
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### 字段说明
|
|
654
|
+
|
|
655
|
+
- **id**:插件唯一标识,需保持全局唯一性
|
|
656
|
+
- **type**:需与后端 MCP 服务器协议类型保持一致(`sse` 或 `StreamableHTTP`)
|
|
657
|
+
- **enabled**:标记插件是否已启用,用于 UI 状态显示
|
|
658
|
+
- **addState**:插件添加状态,驱动 UI 按钮和进度显示
|
|
659
|
+
- `idle`:未添加
|
|
660
|
+
- `adding`:添加中
|
|
661
|
+
- `added`:已添加
|
|
662
|
+
- `error`:添加失败
|
|
663
|
+
- **tools**:MCP 服务提供的工具列表,连接成功后自动填充
|
|
664
|
+
|
|
665
|
+
### 在浏览器扩展中使用
|
|
666
|
+
|
|
667
|
+
在浏览器扩展场景中,可以通过配置文件自动聚合自定义插件:
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
// packages/next-wxt/entrypoints/sidepanel/useCustomMarketMcpServers.ts
|
|
671
|
+
import { customServers } from './meta'
|
|
672
|
+
|
|
673
|
+
const customMarketMcpServers = useCustomMarketMcpServers(customServers)
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
## 🛠️ 参与开发
|
|
677
|
+
|
|
678
|
+
### 环境要求
|
|
679
|
+
|
|
680
|
+
- **Node.js** >= 18
|
|
681
|
+
- **pnpm** >= 8
|
|
682
|
+
- **Vue** 3.x
|
|
683
|
+
|
|
684
|
+
### 开发流程
|
|
685
|
+
|
|
686
|
+
#### 1. 克隆项目
|
|
687
|
+
|
|
688
|
+
```bash
|
|
689
|
+
git clone https://github.com/opentiny/next-sdk.git
|
|
690
|
+
cd next-sdk
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
#### 2. 安装依赖
|
|
694
|
+
|
|
695
|
+
```bash
|
|
696
|
+
pnpm install
|
|
142
697
|
```
|
|
143
698
|
|
|
144
|
-
|
|
145
|
-
- `type`:与后端 MCP 服务器协议保持一致,如 `sse`、`StreamableHTTP`
|
|
146
|
-
- `enabled/addState/tools`:驱动 TinyRemoter 市场 UI 的状态字段
|
|
699
|
+
#### 3. 开发模式
|
|
147
700
|
|
|
148
|
-
|
|
701
|
+
```bash
|
|
702
|
+
# 进入 next-remoter 包目录
|
|
703
|
+
cd packages/next-remoter
|
|
704
|
+
|
|
705
|
+
# 启动开发服务器
|
|
706
|
+
pnpm dev
|
|
707
|
+
|
|
708
|
+
# 浏览器访问 http://localhost:5173
|
|
709
|
+
```
|
|
149
710
|
|
|
150
|
-
|
|
711
|
+
#### 4. 构建项目
|
|
151
712
|
|
|
152
|
-
```
|
|
153
|
-
|
|
713
|
+
```bash
|
|
714
|
+
# 构建生产版本
|
|
715
|
+
pnpm build
|
|
716
|
+
|
|
717
|
+
# 构建运行时版本
|
|
718
|
+
pnpm build:runtime
|
|
719
|
+
|
|
720
|
+
# 构建站点版本
|
|
721
|
+
pnpm build:site
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
### 项目结构详解
|
|
725
|
+
|
|
726
|
+
```
|
|
727
|
+
packages/next-remoter/
|
|
728
|
+
├── src/
|
|
729
|
+
│ ├── components/ # 组件目录
|
|
730
|
+
│ │ ├── TinyRobotChat.vue # 核心对话组件(689 行)
|
|
731
|
+
│ │ └── QrCodeScan.vue # 二维码扫描组件
|
|
732
|
+
│ ├── composable/ # 组合式函数
|
|
733
|
+
│ │ └── usePlugin.ts # 插件管理逻辑(418 行)
|
|
734
|
+
│ ├── index.ts # 组件导出入口
|
|
735
|
+
│ └── App.vue # 独立部署页面
|
|
736
|
+
├── vite.config.ts # Vite 配置
|
|
737
|
+
├── vite.runtime.config.ts # 运行时构建配置
|
|
738
|
+
├── vite.remoter.config.ts # Remoter 构建配置
|
|
739
|
+
├── package.json # 包配置
|
|
740
|
+
└── README.md # 本文档
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### 核心文件说明
|
|
744
|
+
|
|
745
|
+
#### TinyRobotChat.vue
|
|
746
|
+
|
|
747
|
+
- 核心对话组件实现
|
|
748
|
+
- 包含消息展示、输入处理、工具调用等核心功能
|
|
749
|
+
- 支持插槽自定义和主题配置
|
|
750
|
+
|
|
751
|
+
#### usePlugin.ts
|
|
752
|
+
|
|
753
|
+
- MCP 插件管理的核心逻辑
|
|
754
|
+
- 处理插件的添加、删除、启用/禁用
|
|
755
|
+
- 管理工具列表和调用
|
|
756
|
+
|
|
757
|
+
#### index.ts
|
|
758
|
+
|
|
759
|
+
- 导出 `TinyRemoter` 组件
|
|
760
|
+
- 作为 npm 包的入口文件
|
|
761
|
+
|
|
762
|
+
#### App.vue
|
|
763
|
+
|
|
764
|
+
- 可独立部署的扫码访问页面
|
|
765
|
+
- 用于移动端或独立部署场景
|
|
766
|
+
|
|
767
|
+
### 构建脚本说明
|
|
768
|
+
|
|
769
|
+
```json
|
|
770
|
+
{
|
|
771
|
+
"scripts": {
|
|
772
|
+
"dev": "vite", // 开发服务器
|
|
773
|
+
"build": "vite build", // 构建 npm 包
|
|
774
|
+
"build:runtime": "vite build --config vite.runtime.config.ts", // 构建运行时
|
|
775
|
+
"build:site": "vite build --config vite.remoter.config.ts", // 构建站点
|
|
776
|
+
"build:visualizer": "vite build --config vite.remoter.config.ts --mode visualizer", // 构建分析
|
|
777
|
+
"preview": "vite preview" // 预览构建结果
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
### 发布流程
|
|
783
|
+
|
|
784
|
+
```bash
|
|
785
|
+
# 1. 更新版本号
|
|
786
|
+
# 编辑 packages/next-remoter/package.json 中的 version 字段
|
|
787
|
+
|
|
788
|
+
# 2. 安装依赖(如果还没安装)
|
|
789
|
+
pnpm install
|
|
790
|
+
|
|
791
|
+
# 3. 构建项目
|
|
154
792
|
pnpm -F @opentiny/next-remoter build
|
|
793
|
+
|
|
794
|
+
# 4. 进入包目录
|
|
155
795
|
cd packages/next-remoter
|
|
796
|
+
|
|
797
|
+
# 5. 发布到 npm(需要 npm 登录)
|
|
156
798
|
npm publish
|
|
157
799
|
```
|
|
800
|
+
|
|
801
|
+
### 调试技巧
|
|
802
|
+
|
|
803
|
+
#### 1. 使用开发工具
|
|
804
|
+
|
|
805
|
+
```bash
|
|
806
|
+
# 启动开发服务器,支持热重载
|
|
807
|
+
pnpm dev
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
#### 2. 查看构建产物
|
|
811
|
+
|
|
812
|
+
```bash
|
|
813
|
+
# 使用 visualizer 模式查看打包分析
|
|
814
|
+
pnpm build:visualizer
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
#### 3. 本地测试 npm 包
|
|
818
|
+
|
|
819
|
+
```bash
|
|
820
|
+
# 在 next-remoter 目录下创建本地链接
|
|
821
|
+
pnpm link
|
|
822
|
+
|
|
823
|
+
# 在测试项目中使用
|
|
824
|
+
cd /path/to/test-project
|
|
825
|
+
pnpm link @opentiny/next-remoter
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
### 贡献指南
|
|
829
|
+
|
|
830
|
+
我们欢迎所有形式的贡献!以下是参与贡献的方式:
|
|
831
|
+
|
|
832
|
+
#### 报告问题
|
|
833
|
+
|
|
834
|
+
- 在 [GitHub Issues](https://github.com/opentiny/next-sdk/issues) 提交 Bug 报告
|
|
835
|
+
- 提供详细的复现步骤和环境信息
|
|
836
|
+
|
|
837
|
+
#### 提交代码
|
|
838
|
+
|
|
839
|
+
1. Fork 项目到你的 GitHub 账号
|
|
840
|
+
2. 创建特性分支:`git checkout -b feature/amazing-feature`
|
|
841
|
+
3. 提交改动:`git commit -m 'Add some amazing feature'`
|
|
842
|
+
4. 推送到分支:`git push origin feature/amazing-feature`
|
|
843
|
+
5. 提交 Pull Request
|
|
844
|
+
|
|
845
|
+
#### 代码规范
|
|
846
|
+
|
|
847
|
+
- 遵循 Vue3 组合式 API 最佳实践
|
|
848
|
+
- 使用 TypeScript 编写类型安全的代码
|
|
849
|
+
- 添加必要的注释和文档
|
|
850
|
+
- 确保代码通过 ESLint 检查
|
|
851
|
+
|
|
852
|
+
## 📚 文档和资源
|
|
853
|
+
|
|
854
|
+
- [官方文档](https://docs.opentiny.design/next-sdk/guide/tiny-robot-remoter.html)
|
|
855
|
+
- [GitHub 仓库](https://github.com/opentiny/next-sdk)
|
|
856
|
+
- [问题反馈](https://github.com/opentiny/next-sdk/issues)
|
|
857
|
+
- [OpenTiny 官网](https://opentiny.design)
|
|
858
|
+
|
|
859
|
+
## ❓ 常见问题
|
|
860
|
+
|
|
861
|
+
### 1. 如何获取 sessionId?
|
|
862
|
+
|
|
863
|
+
```typescript
|
|
864
|
+
import { WebMcpClient } from '@opentiny/next-sdk'
|
|
865
|
+
|
|
866
|
+
const client = new WebMcpClient({ name: 'my-client', version: '1.0.0' })
|
|
867
|
+
const { sessionId } = await client.connect({
|
|
868
|
+
url: 'https://your-mcp-server.com/mcp',
|
|
869
|
+
agent: true
|
|
870
|
+
})
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### 2. 如何切换大语言模型?
|
|
874
|
+
|
|
875
|
+
使用 `llmConfigs` 属性配置多个模型,通过 `v-model:selectedModelId` 切换:
|
|
876
|
+
|
|
877
|
+
```vue
|
|
878
|
+
<TinyRemoter
|
|
879
|
+
v-model:selected-model-id="modelId"
|
|
880
|
+
:llmConfigs="models"
|
|
881
|
+
/>
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
### 3. 如何自定义欢迎界面?
|
|
885
|
+
|
|
886
|
+
使用 `#welcome` 插槽:
|
|
887
|
+
|
|
888
|
+
```vue
|
|
889
|
+
<TinyRemoter>
|
|
890
|
+
<template #welcome>
|
|
891
|
+
<div>自定义内容</div>
|
|
892
|
+
</template>
|
|
893
|
+
</TinyRemoter>
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
### 4. 如何调用组件方法?
|
|
897
|
+
|
|
898
|
+
通过 ref 引用组件实例:
|
|
899
|
+
|
|
900
|
+
```vue
|
|
901
|
+
<script setup>
|
|
902
|
+
const robotRef = ref()
|
|
903
|
+
// 发送消息
|
|
904
|
+
robotRef.value?.sendMessage('你好')
|
|
905
|
+
// 取消请求
|
|
906
|
+
robotRef.value?.abortRequest()
|
|
907
|
+
</script>
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
## 📄 许可证
|
|
911
|
+
|
|
912
|
+
MIT
|
|
913
|
+
|
|
914
|
+
## 🙏 致谢
|
|
915
|
+
|
|
916
|
+
感谢所有为本项目做出贡献的开发者!
|
|
917
|
+
|
|
918
|
+
---
|
|
919
|
+
|
|
920
|
+
如有任何问题或建议,欢迎提交 [Issue](https://github.com/opentiny/next-sdk/issues) 或 [Pull Request](https://github.com/opentiny/next-sdk/pulls)。
|