@opentiny/tiny-robot-kit 0.2.0-alpha.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/README.md +164 -0
- package/dist/index.d.mts +351 -0
- package/dist/index.d.ts +351 -0
- package/dist/index.js +561 -0
- package/dist/index.mjs +524 -0
- package/package.json +26 -0
- package/src/client.ts +101 -0
- package/src/error.ts +100 -0
- package/src/index.ts +10 -0
- package/src/providers/base.ts +62 -0
- package/src/providers/openai.ts +134 -0
- package/src/types.ts +163 -0
- package/src/utils.ts +125 -0
- package/src/vue/index.ts +1 -0
- package/src/vue/useMessage.ts +227 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useMessage composable
|
|
3
|
+
* 提供消息管理和状态控制功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { reactive, Reactive, ref, toRaw, type Ref } from 'vue'
|
|
7
|
+
import type { ChatMessage } from '../types'
|
|
8
|
+
import type { AIClient } from '../client'
|
|
9
|
+
|
|
10
|
+
export enum STATUS {
|
|
11
|
+
INIT = 'init', // 初始状态
|
|
12
|
+
PROCESSING = 'processing', // AI请求正在处理中, 还未响应,显示加载动画
|
|
13
|
+
STREAMING = 'streaming', // 流式响应中分块数据返回中
|
|
14
|
+
FINISHED = 'finished', // AI请求已完成
|
|
15
|
+
ABORTED = 'aborted', // 用户中止请求
|
|
16
|
+
ERROR = 'error', // AI请求发生错误
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const GeneratingStatus = [STATUS.PROCESSING, STATUS.STREAMING]
|
|
20
|
+
export const FinalStatus = [STATUS.FINISHED, STATUS.ABORTED, STATUS.ERROR]
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 消息状态接口
|
|
24
|
+
*/
|
|
25
|
+
export interface MessageState {
|
|
26
|
+
status: STATUS
|
|
27
|
+
errorMsg: string | null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* useMessage选项接口
|
|
32
|
+
*/
|
|
33
|
+
export interface UseMessageOptions {
|
|
34
|
+
/** AI客户端实例 */
|
|
35
|
+
client: AIClient
|
|
36
|
+
/** 是否默认使用流式响应 */
|
|
37
|
+
useStreamByDefault?: boolean
|
|
38
|
+
/** 错误消息模板 */
|
|
39
|
+
errorMessage?: string
|
|
40
|
+
/** 初始消息列表 */
|
|
41
|
+
initialMessages?: ChatMessage[]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* useMessage返回值接口
|
|
46
|
+
*/
|
|
47
|
+
export interface UseMessageReturn {
|
|
48
|
+
messages: Ref<ChatMessage[]>
|
|
49
|
+
/** 消息状态 */
|
|
50
|
+
messageState: Reactive<MessageState>
|
|
51
|
+
/** 输入消息 */
|
|
52
|
+
inputMessage: Ref<string>
|
|
53
|
+
/** 是否使用流式响应 */
|
|
54
|
+
useStream: Ref<boolean>
|
|
55
|
+
/** 发送消息 */
|
|
56
|
+
sendMessage: (content?: string, clearInput?: boolean) => Promise<void>
|
|
57
|
+
/** 清空消息 */
|
|
58
|
+
clearMessages: () => void
|
|
59
|
+
/** 添加消息 */
|
|
60
|
+
addMessage: (message: ChatMessage) => void
|
|
61
|
+
/** 中止请求 */
|
|
62
|
+
abortRequest: () => void
|
|
63
|
+
/** 重试请求 */
|
|
64
|
+
retryRequest: (msgIndex: number) => Promise<void>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* useMessage composable
|
|
69
|
+
* 提供消息管理和状态控制功能
|
|
70
|
+
*
|
|
71
|
+
* @param options useMessage选项
|
|
72
|
+
* @returns UseMessageReturn
|
|
73
|
+
*/
|
|
74
|
+
export function useMessage(options: UseMessageOptions): UseMessageReturn {
|
|
75
|
+
const { client, useStreamByDefault = true, errorMessage = '请求失败,请稍后重试', initialMessages = [] } = options
|
|
76
|
+
|
|
77
|
+
// 消息列表
|
|
78
|
+
const messages = ref<ChatMessage[]>([...initialMessages])
|
|
79
|
+
|
|
80
|
+
// 输入消息
|
|
81
|
+
const inputMessage = ref('')
|
|
82
|
+
|
|
83
|
+
// 是否使用流式响应
|
|
84
|
+
const useStream = ref(useStreamByDefault)
|
|
85
|
+
|
|
86
|
+
// 请求控制器
|
|
87
|
+
let abortController: AbortController | null = null
|
|
88
|
+
|
|
89
|
+
// 消息状态
|
|
90
|
+
const messageState = reactive<MessageState>({
|
|
91
|
+
status: STATUS.INIT,
|
|
92
|
+
errorMsg: null,
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// 普通请求
|
|
96
|
+
const chat = async (abortController: AbortController) => {
|
|
97
|
+
const response = await client.chat({
|
|
98
|
+
messages: toRaw(messages.value),
|
|
99
|
+
options: {
|
|
100
|
+
stream: false,
|
|
101
|
+
signal: abortController.signal,
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const assistantMessage: ChatMessage = {
|
|
106
|
+
role: 'assistant',
|
|
107
|
+
content: response.choices[0].message.content,
|
|
108
|
+
}
|
|
109
|
+
messages.value.push(assistantMessage)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 流式请求
|
|
113
|
+
const streamChat = async (abortController: AbortController) => {
|
|
114
|
+
await client.chatStream(
|
|
115
|
+
{
|
|
116
|
+
messages: toRaw(messages.value),
|
|
117
|
+
options: {
|
|
118
|
+
stream: true,
|
|
119
|
+
signal: abortController.signal,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
onData: (data) => {
|
|
124
|
+
messageState.status = STATUS.STREAMING
|
|
125
|
+
if (messages.value[messages.value.length - 1].role === 'user') {
|
|
126
|
+
messages.value.push({ role: 'assistant', content: '' })
|
|
127
|
+
}
|
|
128
|
+
const choice = data.choices[0]
|
|
129
|
+
if (choice && choice.delta.content) {
|
|
130
|
+
messages.value[messages.value.length - 1].content += choice.delta.content
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
onError: (error) => {
|
|
134
|
+
messageState.status = STATUS.ERROR
|
|
135
|
+
messageState.errorMsg = errorMessage
|
|
136
|
+
console.error('Stream request error:', error)
|
|
137
|
+
},
|
|
138
|
+
onDone: () => {
|
|
139
|
+
messageState.status = STATUS.FINISHED
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const chatRequest = async () => {
|
|
146
|
+
// 更新状态
|
|
147
|
+
messageState.status = STATUS.PROCESSING
|
|
148
|
+
messageState.errorMsg = null
|
|
149
|
+
|
|
150
|
+
// 创建中止控制器
|
|
151
|
+
abortController = new AbortController()
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
if (useStream.value) {
|
|
155
|
+
await streamChat(abortController)
|
|
156
|
+
} else {
|
|
157
|
+
await chat(abortController)
|
|
158
|
+
}
|
|
159
|
+
messageState.status = STATUS.FINISHED
|
|
160
|
+
} catch (error) {
|
|
161
|
+
messageState.errorMsg = errorMessage
|
|
162
|
+
messageState.status = STATUS.ERROR
|
|
163
|
+
console.error('Send message error:', error)
|
|
164
|
+
} finally {
|
|
165
|
+
abortController = null
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 发送消息
|
|
170
|
+
const sendMessage = async (content: string = inputMessage.value, clearInput: boolean = true) => {
|
|
171
|
+
if (!content?.trim() || GeneratingStatus.includes(messageState.status)) {
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const userMessage: ChatMessage = {
|
|
176
|
+
role: 'user',
|
|
177
|
+
content,
|
|
178
|
+
}
|
|
179
|
+
messages.value.push(userMessage)
|
|
180
|
+
if (clearInput) {
|
|
181
|
+
inputMessage.value = ''
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
await chatRequest()
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 中止请求
|
|
188
|
+
const abortRequest = () => {
|
|
189
|
+
if (abortController) {
|
|
190
|
+
abortController.abort()
|
|
191
|
+
abortController = null
|
|
192
|
+
messageState.status = STATUS.ABORTED
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 重试请求
|
|
197
|
+
const retryRequest = async (msgIndex: number) => {
|
|
198
|
+
if (msgIndex === 0 || !messages.value[msgIndex] || messages.value[msgIndex].role === 'user') {
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
messages.value.splice(msgIndex)
|
|
202
|
+
await chatRequest()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
//清空消息
|
|
206
|
+
const clearMessages = () => {
|
|
207
|
+
messages.value = []
|
|
208
|
+
messageState.errorMsg = null
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 添加消息
|
|
212
|
+
const addMessage = (message: ChatMessage) => {
|
|
213
|
+
messages.value.push(message)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
messages,
|
|
218
|
+
messageState,
|
|
219
|
+
inputMessage,
|
|
220
|
+
useStream,
|
|
221
|
+
sendMessage,
|
|
222
|
+
clearMessages,
|
|
223
|
+
addMessage,
|
|
224
|
+
abortRequest,
|
|
225
|
+
retryRequest,
|
|
226
|
+
}
|
|
227
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "./dist",
|
|
12
|
+
"rootDir": "./src",
|
|
13
|
+
"lib": ["ESNext", "DOM"],
|
|
14
|
+
"baseUrl": ".",
|
|
15
|
+
"paths": {
|
|
16
|
+
"*": ["node_modules/*"]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*"],
|
|
20
|
+
"exclude": ["node_modules", "dist"]
|
|
21
|
+
}
|