-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsdk.js
More file actions
134 lines (126 loc) · 4.97 KB
/
sdk.js
File metadata and controls
134 lines (126 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
;(function () {
class ChatClient {
constructor({ baseUrl = '', chatPath = '/api/chat', uploadPath = '/upload', headers = {} } = {}) {
this.baseUrl = baseUrl
this.chatPath = chatPath
this.uploadPath = uploadPath
this.headers = headers
}
setHeaders(headers) {
this.headers = headers || {}
}
async upload(file) {
const formData = new FormData()
formData.append('file', file)
const res = await fetch(this.baseUrl + this.uploadPath, { method: 'POST', body: formData, headers: this.headers })
if (!res.ok) throw new Error('上传失败: ' + res.status)
const json = await res.json()
const url = (json && json.data && json.data.url) || json.url || (json && json.data && json.data.url)
if (!url) throw new Error('未能获取图片 URL')
return { url, raw: json }
}
streamChat({ query, history = [], files }, { onDelta, onComplete, onError, signal } = {}) {
const controller = new AbortController()
const usedSignal = signal || controller.signal
const headers = Object.assign({ 'Content-Type': 'application/json' }, this.headers)
let full = ''
let buffer = ''
const done = (async () => {
try {
const res = await fetch(this.baseUrl + this.chatPath, {
method: 'POST',
headers,
body: JSON.stringify({ query, history, files }),
signal: usedSignal
})
if (!res.ok) throw new Error('HTTP error: ' + res.status)
if (!res.body) throw new Error('No response body')
const reader = res.body.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value, { stream: true })
buffer += chunk
// 按行分割并解析 JSON
const lines = buffer.split('\n')
buffer = lines.pop() // 保留最后一行,可能不完整
for (const line of lines) {
if (line.trim() === '') continue
try {
const data = JSON.parse(line)
let displayText = ''
// 根据 type 处理不同类型的消息
switch (data.type) {
case 'content':
displayText = data.content
break
case 'tool_call':
displayText = `[工具调用] ${data.content}`
break
case 'tool_progress':
displayText = `[工具进度] ${data.content}`
break
case 'tool_output':
displayText = `[工具输出] ${data.content}`
break
case 'error':
displayText = `[错误] ${data.content}`
break
default:
displayText = `[未知类型] ${data.content}`
}
full += displayText
if (onDelta) onDelta(displayText, full)
} catch (e) {
console.error('解析 JSON 失败:', e, line)
}
}
}
// 处理最后一行
if (buffer.trim() !== '') {
try {
const data = JSON.parse(buffer)
let displayText = ''
switch (data.type) {
case 'content':
displayText = data.content
break
case 'tool_call':
displayText = `[工具调用] ${data.content}`
break
case 'tool_progress':
displayText = `[工具进度] ${data.content}`
break
case 'tool_output':
displayText = `[工具输出] ${data.content}`
break
case 'error':
displayText = `[错误] ${data.content}`
break
default:
displayText = `[未知类型] ${data.content}`
}
full += displayText
if (onDelta) onDelta(displayText, full)
} catch (e) {
console.error('解析最后一行 JSON 失败:', e, buffer)
}
}
if (onComplete) onComplete(full)
} catch (e) {
if (onError) onError(e)
}
})()
return { abort: () => controller.abort(), done }
}
async sendChat(params) {
let text = ''
await this.streamChat(params, { onDelta: (_, f) => (text = f) }).done
return text
}
}
var AgenticHub = window.AgenticHub || {}
AgenticHub.ChatClient = ChatClient
window.AgenticHub = AgenticHub
})()