@vyr/service-chat 0.0.34
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/package.json +18 -0
- package/src/ChatService.ts +104 -0
- package/src/chat/ChatHistory.ts +127 -0
- package/src/chat/ChatSession.ts +183 -0
- package/src/chat/ChatWindow.ts +92 -0
- package/src/chat/ChatWindowManager.ts +72 -0
- package/src/chat/index.ts +4 -0
- package/src/common/Draggable.ts +36 -0
- package/src/common/index.ts +1 -0
- package/src/components/ChatAgent.vue +44 -0
- package/src/components/ChatExecutor.vue +85 -0
- package/src/components/ChatProcess.vue +144 -0
- package/src/components/ChatView.vue +135 -0
- package/src/components/ChatWindow.vue +64 -0
- package/src/components/Input.vue +170 -0
- package/src/components/Welcome.vue +56 -0
- package/src/components/executor/ExecutorHeader.vue +72 -0
- package/src/components/executor/GearLoading.vue +104 -0
- package/src/components/executor/ParamSection.vue +243 -0
- package/src/components/executor/ToolItem.vue +262 -0
- package/src/components/executor/types.ts +27 -0
- package/src/components/executor/useExecutor.ts +117 -0
- package/src/components/index.ts +11 -0
- package/src/executor/index.ts +17 -0
- package/src/index.ts +10 -0
- package/src/locale/Language.ts +10 -0
- package/src/locale/LanguageProvider.ts +49 -0
- package/src/locale/index.ts +2 -0
- package/tsconfig.json +37 -0
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vyr/service-chat",
|
|
3
|
+
"version": "0.0.34",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"keywords": [],
|
|
7
|
+
"author": "",
|
|
8
|
+
"license": "ISC",
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@matechat/core": "^1.5.2",
|
|
11
|
+
"@mdit/plugin-katex": "^0.24.2",
|
|
12
|
+
"devui-theme": "^0.1.0",
|
|
13
|
+
"mermaid": "^11.12.2"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"sass": "^1.32.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { reactive } from "vue";
|
|
3
|
+
import { MastraClient } from "@mastra/client-js";
|
|
4
|
+
import { AsyncTask } from "@vyr/engine";
|
|
5
|
+
import { Option } from "@vyr/design";
|
|
6
|
+
import { Service } from "@vyr/service";
|
|
7
|
+
import { DraggableService, runtime } from "@vyr/runtime";
|
|
8
|
+
import { api, request, ChatWindow as _Window, GatewayService, } from "@vyr/service-gateway"
|
|
9
|
+
import { ChatSession, ChatWindowManager } from "./chat"
|
|
10
|
+
import { Draggable } from "./common";
|
|
11
|
+
import { bindExecutor } from "./executor";
|
|
12
|
+
|
|
13
|
+
class ChatService extends Service {
|
|
14
|
+
private static _client: MastraClient | null = null
|
|
15
|
+
static get client() {
|
|
16
|
+
if (this._client === null) {
|
|
17
|
+
const location = new URL(window.location.href)
|
|
18
|
+
location.pathname = api.chat.prefix.path
|
|
19
|
+
this._client = new MastraClient({ baseUrl: location.origin + location.pathname, retries: 0, fetch: ChatSession.fetch })
|
|
20
|
+
}
|
|
21
|
+
return this._client
|
|
22
|
+
}
|
|
23
|
+
readonly initializeTask: AsyncTask
|
|
24
|
+
readonly manager = reactive<ChatWindowManager>(new ChatWindowManager())
|
|
25
|
+
agents: Option[] = []
|
|
26
|
+
|
|
27
|
+
constructor(name: string) {
|
|
28
|
+
super(name)
|
|
29
|
+
this.initializeTask = new AsyncTask(async () => {
|
|
30
|
+
await this.manager.initialize()
|
|
31
|
+
await this.initialize()
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async initialize() {
|
|
36
|
+
const collection = await ChatService.client.listAgents()
|
|
37
|
+
const keys = Object.keys(collection)
|
|
38
|
+
for (const key of keys) {
|
|
39
|
+
this.agents.push({ label: key, value: collection[key].id })
|
|
40
|
+
}
|
|
41
|
+
const agent = this.agents[0]
|
|
42
|
+
if (agent) this.manager.window.agentId = agent.value
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async setup() {
|
|
46
|
+
const draggableService = runtime.get<DraggableService>('draggable')
|
|
47
|
+
const draggable = new Draggable()
|
|
48
|
+
draggableService.set(draggable.id, draggable)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async ready() {
|
|
52
|
+
const gatewayService = runtime.get<GatewayService>('gateway')
|
|
53
|
+
bindExecutor(gatewayService)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async start() {
|
|
57
|
+
await this.initializeTask.done()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getDefaultAgentId() {
|
|
61
|
+
return this.agents[0]?.value ?? ''
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async list(data: z.infer<typeof api.chat.list.requestSchema>) {
|
|
66
|
+
const res = await request<{ list: _Window[] }>({
|
|
67
|
+
url: api.chat.list.path,
|
|
68
|
+
method: api.chat.list.method,
|
|
69
|
+
data,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
return res.data
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async create(data: z.infer<typeof api.chat.create.requestSchema>) {
|
|
76
|
+
await request<{ list: _Window[] }>({
|
|
77
|
+
url: api.chat.create.path,
|
|
78
|
+
method: api.chat.create.method,
|
|
79
|
+
data,
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async delete(data: z.infer<typeof api.chat.delete.requestSchema>) {
|
|
84
|
+
await request({
|
|
85
|
+
url: api.chat.delete.path,
|
|
86
|
+
method: api.chat.delete.method,
|
|
87
|
+
data,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async stop(data: z.infer<typeof api.chat.stop.requestSchema>) {
|
|
92
|
+
const res = await request<{ success: boolean }>({
|
|
93
|
+
url: api.chat.stop.path,
|
|
94
|
+
method: api.chat.stop.method,
|
|
95
|
+
data,
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
return res.data
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
ChatService
|
|
104
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { MastraDBMessage } from "@mastra/core/agent";
|
|
2
|
+
import { TaskItem } from "../components/executor/types";
|
|
3
|
+
import { ChatWindow } from "./ChatWindow";
|
|
4
|
+
|
|
5
|
+
interface ChatMessageTextChunk {
|
|
6
|
+
type: 'text'
|
|
7
|
+
content: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ChatMessageReasoningChunk {
|
|
11
|
+
type: 'reasoning'
|
|
12
|
+
content: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ChatMessageToolChunk {
|
|
16
|
+
type: 'tool'
|
|
17
|
+
collection: Map<string, TaskItem>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type ChatMessageChunk = ChatMessageTextChunk | ChatMessageReasoningChunk | ChatMessageToolChunk
|
|
21
|
+
|
|
22
|
+
interface ChatMessageTool {
|
|
23
|
+
toolName: string
|
|
24
|
+
args: any
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class ChatMessage {
|
|
28
|
+
id: string
|
|
29
|
+
role: 'user' | 'assistant' | 'system';
|
|
30
|
+
chunks: ChatMessageChunk[]
|
|
31
|
+
avatarPosition: 'side-left' | 'side-right';
|
|
32
|
+
avatarConfig?: string
|
|
33
|
+
loading?: boolean
|
|
34
|
+
complete?: boolean;
|
|
35
|
+
current?: ChatMessageChunk
|
|
36
|
+
|
|
37
|
+
constructor(msg: Partial<ChatMessage> = {}) {
|
|
38
|
+
this.id = msg.id ?? ''
|
|
39
|
+
this.role = msg.role ?? 'assistant'
|
|
40
|
+
this.chunks = msg.chunks ?? []
|
|
41
|
+
this.avatarPosition = `side-${this.role === 'user' ? 'right' : 'left'}`
|
|
42
|
+
this.avatarConfig = `logo/${this.role === 'user' ? 'chat' : '透明图标'}.png`
|
|
43
|
+
this.loading = msg.loading ?? false
|
|
44
|
+
this.complete = msg.complete ?? true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
add(chunk: ChatMessageChunk) {
|
|
48
|
+
this.chunks.push(chunk)
|
|
49
|
+
this.current = chunk
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
startNewChunk(type: ChatMessageChunk['type'], initialContent = '') {
|
|
53
|
+
let currentChunk: ChatMessageChunk
|
|
54
|
+
if (type === 'tool') {
|
|
55
|
+
currentChunk = { type, collection: new Map }
|
|
56
|
+
} else {
|
|
57
|
+
currentChunk = { type, content: initialContent }
|
|
58
|
+
}
|
|
59
|
+
this.add(currentChunk)
|
|
60
|
+
return currentChunk
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
startToolChunk(): ChatMessageToolChunk {
|
|
64
|
+
const chunk = this.chunks[this.chunks.length - 1]
|
|
65
|
+
if (chunk === undefined || chunk.type !== 'tool') {
|
|
66
|
+
return this.startNewChunk('tool') as ChatMessageToolChunk
|
|
67
|
+
} else {
|
|
68
|
+
return chunk
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
addFromParts(parts: { type: string, [k: string]: any }[]) {
|
|
73
|
+
for (const part of parts) {
|
|
74
|
+
if (part.type === 'text') {
|
|
75
|
+
if (part.text.length === 0) continue
|
|
76
|
+
this.add({ type: 'text', content: part.text })
|
|
77
|
+
} else if (part.type === 'reasoning') {
|
|
78
|
+
if (part.reasoning.length === 0) continue
|
|
79
|
+
this.add({ type: 'reasoning', content: part.reasoning })
|
|
80
|
+
} else if (part.type === 'tool-invocation') {
|
|
81
|
+
const tool = part.toolInvocation as any
|
|
82
|
+
const chunk = this.startToolChunk()
|
|
83
|
+
chunk.collection.set(tool.toolCallId, {
|
|
84
|
+
...tool,
|
|
85
|
+
state: tool.result?.error ? 'failed' : 'success'
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class ChatHistory {
|
|
93
|
+
private messages: ChatMessage[] = []
|
|
94
|
+
get list() {
|
|
95
|
+
return [...this.messages]
|
|
96
|
+
}
|
|
97
|
+
lastMessage!: ChatMessage
|
|
98
|
+
|
|
99
|
+
addMessage(msg: ChatMessage) {
|
|
100
|
+
this.messages.push(msg)
|
|
101
|
+
this.lastMessage = msg
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
addMessageFromRecord(record: MastraDBMessage) {
|
|
105
|
+
const chatMsg = new ChatMessage({ role: record.role })
|
|
106
|
+
chatMsg.addFromParts(record.content.parts)
|
|
107
|
+
this.addMessage(chatMsg)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async restore(window: ChatWindow) {
|
|
111
|
+
try {
|
|
112
|
+
const thread = await window.getThread()
|
|
113
|
+
const result = await thread.listMessages({ resourceId: window.threadId })
|
|
114
|
+
for (const record of result.messages) this.addMessageFromRecord(record)
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.log(error)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export {
|
|
122
|
+
ChatMessageToolChunk,
|
|
123
|
+
ChatMessageChunk,
|
|
124
|
+
ChatMessageTool,
|
|
125
|
+
ChatMessage,
|
|
126
|
+
ChatHistory,
|
|
127
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { MastraMessagePart } from "@mastra/core/agent/message-list"
|
|
3
|
+
import { RequestContext } from "@mastra/core/request-context"
|
|
4
|
+
import { AsyncTask, Generate } from "@vyr/engine"
|
|
5
|
+
import { runtime } from "@vyr/runtime"
|
|
6
|
+
import { api, GatewayService, setAuthorization, setClient } from "@vyr/service-gateway"
|
|
7
|
+
import { UserService } from "@vyr/service-user"
|
|
8
|
+
import { language } from "../locale"
|
|
9
|
+
import { TaskItem } from "../components/executor/types"
|
|
10
|
+
import { ChatMessage, ChatMessageChunk } from "./ChatHistory"
|
|
11
|
+
import { ChatWindow } from "./ChatWindow"
|
|
12
|
+
import { ChatService } from "../ChatService"
|
|
13
|
+
|
|
14
|
+
class ChatSession {
|
|
15
|
+
static fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
16
|
+
const modifiedInit = { ...init }
|
|
17
|
+
if (modifiedInit.headers === undefined) modifiedInit.headers = {}
|
|
18
|
+
setAuthorization(modifiedInit.headers, UserService.getToken())
|
|
19
|
+
setClient(modifiedInit.headers)
|
|
20
|
+
|
|
21
|
+
return fetch(input, modifiedInit)
|
|
22
|
+
}
|
|
23
|
+
private executor
|
|
24
|
+
readonly runId = Generate.uuid()
|
|
25
|
+
readonly window
|
|
26
|
+
|
|
27
|
+
constructor(window: ChatWindow) {
|
|
28
|
+
this.window = window
|
|
29
|
+
this.executor = new AsyncTask(this.start)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private onChunk = async (chunk: any) => {
|
|
33
|
+
if (chunk.type === 'start') {
|
|
34
|
+
this.window.history.lastMessage.id = chunk?.payload?.messageId ?? ''
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (chunk.type === 'text-start' || chunk.type === 'reasoning-start') {
|
|
38
|
+
this.window.history.lastMessage.loading = false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (chunk.type === 'tool-call') {
|
|
42
|
+
this.window.history.lastMessage.loading = false
|
|
43
|
+
const payload = chunk.payload ?? {}
|
|
44
|
+
const toolChunk = this.window.history.lastMessage.startToolChunk()
|
|
45
|
+
toolChunk.collection.set(payload.toolCallId, {
|
|
46
|
+
...payload,
|
|
47
|
+
state: 'running',
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (chunk.type === 'tool-result') {
|
|
52
|
+
const payload = chunk.payload || {};
|
|
53
|
+
const toolChunk = this.window.history.lastMessage.startToolChunk()
|
|
54
|
+
const task = toolChunk.collection.get(payload.toolCallId) as TaskItem
|
|
55
|
+
task.state = payload.result?.error ? 'failed' : 'success'
|
|
56
|
+
task.result = payload.result
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (chunk.type === 'text-delta' && chunk.payload?.text) {
|
|
61
|
+
if (!this.window.history.lastMessage.current || this.window.history.lastMessage.current.type !== 'text') {
|
|
62
|
+
this.window.history.lastMessage.startNewChunk('text', chunk.payload.text)
|
|
63
|
+
} else {
|
|
64
|
+
this.window.history.lastMessage.current.content += chunk.payload.text
|
|
65
|
+
}
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (chunk.type === 'reasoning-delta' && chunk.payload?.text) {
|
|
70
|
+
if (!this.window.history.lastMessage.current || this.window.history.lastMessage.current.type !== 'reasoning') {
|
|
71
|
+
this.window.history.lastMessage.startNewChunk('reasoning', chunk.payload.text)
|
|
72
|
+
} else {
|
|
73
|
+
this.window.history.lastMessage.current.content += chunk.payload.text;
|
|
74
|
+
}
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 成功完成
|
|
79
|
+
if (chunk.type === 'finish') {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 异常终止
|
|
84
|
+
if (chunk.type === 'error') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log('未处理的数据块类型:', chunk.type)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private start = async (question: string) => {
|
|
92
|
+
this.window.history.addMessage(new ChatMessage({ role: 'user', chunks: [{ type: 'text', content: question }] }))
|
|
93
|
+
this.window.history.addMessage(new ChatMessage({ role: 'assistant', complete: false, loading: true }))
|
|
94
|
+
|
|
95
|
+
const requestContext = new RequestContext()
|
|
96
|
+
requestContext.set('client', GatewayService.client)
|
|
97
|
+
|
|
98
|
+
const response = await this.window.getAgent().stream(question, {
|
|
99
|
+
runId: this.runId,
|
|
100
|
+
memory: { thread: this.window.threadId, resource: this.window.threadId },
|
|
101
|
+
requestContext
|
|
102
|
+
})
|
|
103
|
+
await response.processDataStream({ onChunk: this.onChunk })
|
|
104
|
+
|
|
105
|
+
this.window.history.lastMessage.loading = false
|
|
106
|
+
this.window.history.lastMessage.complete = true
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
run(question: string) {
|
|
110
|
+
return this.executor.run(question)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
done() {
|
|
114
|
+
return this.executor.done()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async closeRequest() {
|
|
118
|
+
const chatService = runtime.get<ChatService>('chat')
|
|
119
|
+
|
|
120
|
+
const data: z.infer<typeof api.chat.stop.requestSchema> = {
|
|
121
|
+
runId: this.runId
|
|
122
|
+
}
|
|
123
|
+
return await chatService.stop(data)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async shouldSaveMessage() {
|
|
127
|
+
if (this.window.history.lastMessage.id) {
|
|
128
|
+
const thread = await this.window.getThread()
|
|
129
|
+
const result = await thread.listMessages({ page: 0, perPage: 1, orderBy: { field: 'createdAt', direction: 'DESC' } })
|
|
130
|
+
const lastId = result.messages[0]?.id
|
|
131
|
+
if (lastId === this.window.history.lastMessage.id) return false
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return true
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async saveMessage() {
|
|
138
|
+
const chunk: ChatMessageChunk = { type: 'text', content: language.get('chat.user.stop') }
|
|
139
|
+
this.window.history.lastMessage.add(chunk)
|
|
140
|
+
let content = ''
|
|
141
|
+
const parts: MastraMessagePart[] = []
|
|
142
|
+
const toolInvocations: any[] = []
|
|
143
|
+
for (const chunk of this.window.history.lastMessage.chunks) {
|
|
144
|
+
if (chunk.type === 'text') {
|
|
145
|
+
content += chunk.content
|
|
146
|
+
parts.push({ type: 'text', text: chunk.content })
|
|
147
|
+
} else if (chunk.type === 'tool') {
|
|
148
|
+
const toolInvocation = { ...chunk, type: undefined, state: 'result' }
|
|
149
|
+
toolInvocations.push(toolInvocation)
|
|
150
|
+
parts.push({ type: 'tool-invocation', toolInvocation })
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const agentId = this.window.agentId
|
|
155
|
+
await ChatService.client.saveMessageToMemory({
|
|
156
|
+
messages: [
|
|
157
|
+
{
|
|
158
|
+
id: this.window.history.lastMessage.id,
|
|
159
|
+
type: 'text',
|
|
160
|
+
createdAt: new Date(),
|
|
161
|
+
threadId: this.window.threadId,
|
|
162
|
+
resourceId: this.window.threadId,
|
|
163
|
+
role: 'assistant',
|
|
164
|
+
content: { format: 2, content, parts, toolInvocations },
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
agentId,
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async stop() {
|
|
172
|
+
const data = await this.closeRequest()
|
|
173
|
+
this.window.history.lastMessage.loading = false
|
|
174
|
+
if (data.success === false) return
|
|
175
|
+
const isSave = await this.shouldSaveMessage()
|
|
176
|
+
if (isSave === false) return
|
|
177
|
+
await this.saveMessage()
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export {
|
|
182
|
+
ChatSession
|
|
183
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { MemoryThread } from "@mastra/client-js/dist/resources";
|
|
3
|
+
import { Generate } from "@vyr/engine";
|
|
4
|
+
import { runtime } from "@vyr/runtime";
|
|
5
|
+
import { ChatWindow as _Window, api } from "@vyr/service-gateway";
|
|
6
|
+
import { ChatHistory } from "./ChatHistory";
|
|
7
|
+
import { ChatSession } from "./ChatSession";
|
|
8
|
+
import { ChatService } from "../ChatService";
|
|
9
|
+
|
|
10
|
+
class ChatWindow {
|
|
11
|
+
private _needCreate = true
|
|
12
|
+
get completed() {
|
|
13
|
+
return this._needCreate === false
|
|
14
|
+
}
|
|
15
|
+
private _thread: MemoryThread | null = null
|
|
16
|
+
readonly history
|
|
17
|
+
readonly threadId
|
|
18
|
+
readonly title
|
|
19
|
+
agentId = ''
|
|
20
|
+
runing = false
|
|
21
|
+
disbaled = false
|
|
22
|
+
maxLength = 2000
|
|
23
|
+
question = ''
|
|
24
|
+
|
|
25
|
+
constructor(options: Partial<_Window> = {}) {
|
|
26
|
+
this.history = new ChatHistory()
|
|
27
|
+
this.threadId = options.id ?? Generate.uuid()
|
|
28
|
+
this.title = options.title ?? this.threadId
|
|
29
|
+
this.agentId = options.agentId ?? ''
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private async tryCreate() {
|
|
33
|
+
const chatService = runtime.get<ChatService>('chat')
|
|
34
|
+
const listData: z.infer<typeof api.chat.list.requestSchema> = {
|
|
35
|
+
id: this.threadId,
|
|
36
|
+
}
|
|
37
|
+
const data = await chatService.list(listData)
|
|
38
|
+
if (data.list.length > 0) {
|
|
39
|
+
this._needCreate = false
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const createData: z.infer<typeof api.chat.create.requestSchema> = {
|
|
44
|
+
window: {
|
|
45
|
+
id: this.threadId,
|
|
46
|
+
title: this.title,
|
|
47
|
+
agentId: this.agentId,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
await chatService.create(createData)
|
|
51
|
+
this._needCreate = false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setNeedCreate(create: boolean) {
|
|
55
|
+
this._needCreate = create
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async getThread() {
|
|
59
|
+
if (this._thread === null) {
|
|
60
|
+
try {
|
|
61
|
+
this._thread = ChatService.client.getMemoryThread({ threadId: this.threadId, agentId: this.agentId })
|
|
62
|
+
} catch (error) {
|
|
63
|
+
await ChatService.client.createMemoryThread({
|
|
64
|
+
threadId: this.threadId,
|
|
65
|
+
resourceId: this.threadId,
|
|
66
|
+
agentId: this.agentId
|
|
67
|
+
})
|
|
68
|
+
this._thread = ChatService.client.getMemoryThread({
|
|
69
|
+
threadId: this.threadId,
|
|
70
|
+
agentId: this.agentId
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return this._thread
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getAgent() {
|
|
79
|
+
return ChatService.client.getAgent(this.agentId)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async ask(question: string) {
|
|
83
|
+
if (this._needCreate) await this.tryCreate()
|
|
84
|
+
const chatSession = new ChatSession(this)
|
|
85
|
+
chatSession.run(question)
|
|
86
|
+
return chatSession
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
ChatWindow,
|
|
92
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { api, ChatWindow as _Window } from "@vyr/service-gateway";
|
|
3
|
+
import { ChatWindow } from "./ChatWindow";
|
|
4
|
+
import { runtime } from "@vyr/runtime";
|
|
5
|
+
import { ChatService } from "../ChatService";
|
|
6
|
+
|
|
7
|
+
class ChatWindowManager {
|
|
8
|
+
private collection = new Map<string, ChatWindow>()
|
|
9
|
+
window = this.createWindow()
|
|
10
|
+
|
|
11
|
+
createWindow(options: Partial<_Window> = {}) {
|
|
12
|
+
const window = new ChatWindow(options)
|
|
13
|
+
this.collection.set(window.threadId, window)
|
|
14
|
+
return window
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async deleteWindow(window: ChatWindow) {
|
|
18
|
+
if (window.completed) {
|
|
19
|
+
const thread = await window.getThread()
|
|
20
|
+
await thread.delete()
|
|
21
|
+
const chatService = runtime.get<ChatService>('chat')
|
|
22
|
+
|
|
23
|
+
const data: z.infer<typeof api.chat.delete.requestSchema> = {
|
|
24
|
+
id: window.threadId,
|
|
25
|
+
}
|
|
26
|
+
await chatService.delete(data)
|
|
27
|
+
}
|
|
28
|
+
this.collection.delete(window.threadId)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setCurrentWindow(window: ChatWindow) {
|
|
32
|
+
this.window = window
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getWindow(id: string) {
|
|
36
|
+
return this.collection.get(id) ?? null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getWindows() {
|
|
40
|
+
return [...this.collection.values()]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
isPendingWindow() {
|
|
44
|
+
const windows = this.collection.values()
|
|
45
|
+
for (const window of windows) {
|
|
46
|
+
if (window.completed === false) return true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async initialize() {
|
|
53
|
+
const chatService = runtime.get<ChatService>('chat')
|
|
54
|
+
|
|
55
|
+
const listData: z.infer<typeof api.chat.list.requestSchema> = {}
|
|
56
|
+
const data = await chatService.list(listData)
|
|
57
|
+
const first = data?.list[0]
|
|
58
|
+
if (first === undefined) return
|
|
59
|
+
this.collection.clear()
|
|
60
|
+
for await (const item of data.list) {
|
|
61
|
+
const window = this.createWindow(item)
|
|
62
|
+
window.setNeedCreate(false)
|
|
63
|
+
await window.history.restore(window)
|
|
64
|
+
}
|
|
65
|
+
const window = this.collection.get(first.id) as ChatWindow
|
|
66
|
+
this.setCurrentWindow(window)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
ChatWindowManager
|
|
72
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Descriptor } from "@vyr/engine"
|
|
2
|
+
import { Draggable as _Draggable, DraggableService, runtime } from "@vyr/runtime"
|
|
3
|
+
import { DraggableData, DraggableEndType, } from '@vyr/declare'
|
|
4
|
+
import { Prefab, VirtualNode } from "@vyr/service-gateway"
|
|
5
|
+
import { language } from '../locale'
|
|
6
|
+
import { ChatService } from "../ChatService"
|
|
7
|
+
|
|
8
|
+
class Draggable extends _Draggable<VirtualNode> {
|
|
9
|
+
readonly id = DraggableService.key.chat
|
|
10
|
+
|
|
11
|
+
starter(dragData: VirtualNode) {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
validator(dragData: DraggableData<VirtualNode>, targetData: DraggableData<VirtualNode>) {
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
finished(dragData: DraggableData<VirtualNode | Descriptor | Prefab>, targetData: DraggableData, type: DraggableEndType) {
|
|
20
|
+
const chatService = runtime.get<ChatService>('chat')
|
|
21
|
+
|
|
22
|
+
let content = ''
|
|
23
|
+
if (dragData.id === DraggableService.key.footer && dragData.data instanceof VirtualNode) {
|
|
24
|
+
content = language.get('chat.input.format.content.asset', { url: dragData.data.url })
|
|
25
|
+
} else if (dragData.id === DraggableService.key.asset && dragData.data instanceof Descriptor) {
|
|
26
|
+
content = language.get('chat.input.format.content.node', { uuid: dragData.data.uuid, url: targetData.data.value })
|
|
27
|
+
} else if (dragData.id === DraggableService.key.prefab) {
|
|
28
|
+
const prefab = dragData.data as unknown as Prefab
|
|
29
|
+
content = language.get('chat.input.format.content.prefab', { id: prefab.id })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
chatService.manager.window.question += content
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { Draggable }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Draggable'
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<vyr-dropdown class="agent-wrapper" :data="chatService.agents" :model-value="chatService.manager.window.agentId">
|
|
3
|
+
<div class="agent">
|
|
4
|
+
{{ agentName }}
|
|
5
|
+
</div>
|
|
6
|
+
</vyr-dropdown>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup lang="ts">
|
|
10
|
+
import { computed } from 'vue';
|
|
11
|
+
import { runtime } from '@vyr/runtime';
|
|
12
|
+
import { VyrDropdown } from '@vyr/design';
|
|
13
|
+
import { ChatService } from '../ChatService';
|
|
14
|
+
|
|
15
|
+
const chatService = runtime.get<ChatService>('chat')
|
|
16
|
+
|
|
17
|
+
const agentName = computed(() => {
|
|
18
|
+
for (const agent of chatService.agents) {
|
|
19
|
+
if (agent.value === chatService.manager.window.agentId) return agent.label
|
|
20
|
+
}
|
|
21
|
+
return ' - '
|
|
22
|
+
})
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<style scoped lang="scss">
|
|
26
|
+
@import "devui-theme/styles-var/devui-var.scss";
|
|
27
|
+
|
|
28
|
+
$item-height: 24px;
|
|
29
|
+
|
|
30
|
+
.agent-wrapper {
|
|
31
|
+
width: 120px;
|
|
32
|
+
background-color: $devui-base-bg;
|
|
33
|
+
box-shadow: 0px 1px 8px 0px rgba(25, 25, 25, 0.06);
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
|
|
36
|
+
.agent {
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
height: $item-height;
|
|
39
|
+
line-height: $item-height;
|
|
40
|
+
padding: 0 0 0 8px;
|
|
41
|
+
box-sizing: border-box;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
</style>
|