Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-bi
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-bi
Commits
7dae00a4
提交
7dae00a4
authored
3月 19, 2025
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: update
上级
cadb59e8
隐藏空白字符变更
内嵌
并排
正在显示
9 个修改的文件
包含
445 行增加
和
298 行删除
+445
-298
AIChat.tsx
src/components/ai/AIChat.tsx
+5
-6
useAI.ts
src/hooks/useAI.ts
+26
-132
useQuery.ts
src/hooks/useQuery.ts
+5
-0
main.tsx
src/main.tsx
+2
-2
NullButtonModal.tsx
...ules/data/read/exploration/components/NullButtonModal.tsx
+61
-16
Index.tsx
src/modules/data/read/understanding/views/Index.tsx
+14
-7
Index.tsx
src/modules/data/write/upload/views/Index.tsx
+2
-0
ai.ts
src/stores/ai.ts
+34
-135
ai.ts
src/utils/ai.ts
+296
-0
没有找到文件。
src/components/ai/AIChat.tsx
浏览文件 @
7dae00a4
...
...
@@ -25,7 +25,6 @@ export default function AIChat() {
const
toggleCollapsed
=
()
=>
{
setCollapsed
(
!
collapsed
)
}
const
{
ai
,
setAI
,
options
,
post
,
messages
,
isLoading
}
=
useAIStore
()
const
[
content
,
setContent
]
=
useState
(
''
)
...
...
@@ -38,7 +37,7 @@ export default function AIChat() {
}
const
handleSearch
=
()
=>
{
setContent
(
''
)
post
({
content
})
post
({
messages
:
[{
role
:
'user'
,
content
}]
})
}
const
messageScrollRef
=
useRef
<
HTMLDivElement
|
null
>
(
null
)
...
...
@@ -47,7 +46,7 @@ export default function AIChat() {
const
scrollContainer
=
messageScrollRef
.
current
scrollContainer
.
scrollTop
=
scrollContainer
.
scrollHeight
}
},
[
isLoading
])
},
[
messages
])
if
(
collapsed
)
{
return
(
...
...
@@ -57,8 +56,8 @@ export default function AIChat() {
extra=
{
<
span
onClick=
{
toggleCollapsed
}
>
{
collapsed
?
<
CircleArrowRight
/>
:
<
CircleArrowLeft
/>
}
</
span
>
}
>
<
div
className=
"ai-chat-container"
>
<
div
className=
"message-scroll"
ref=
{
messageScrollRef
}
>
{
messages
.
map
((
message
)
=>
{
return
<
MessageItem
message=
{
message
}
></
MessageItem
>
{
messages
.
map
((
message
,
index
)
=>
{
return
<
MessageItem
message=
{
message
}
key=
{
index
}
></
MessageItem
>
})
}
</
div
>
<
div
className=
"input-container"
>
...
...
@@ -81,7 +80,7 @@ export default function AIChat() {
onChange=
{
setAI
}
variant=
"filled"
suffixIcon=
{
null
}
popupMatchSelectWidth=
{
1
0
0
}
></
Select
>
popupMatchSelectWidth=
{
1
1
0
}
></
Select
>
<
Button
type=
"primary"
shape=
"circle"
...
...
src/hooks/useAI.ts
浏览文件 @
7dae00a4
import
{
useState
,
useEffect
,
useCallback
}
from
'react'
import
md5
from
'blueimp-md5'
import
axios
from
'axios'
import
{
fetchEventSource
}
from
'@fortaine/fetch-event-source'
export
interface
AIOption
{
label
:
string
value
:
string
}
export
interface
AIMessage
{
id
?:
string
role
:
'user'
|
'assistant'
content
:
string
}
import
aiService
,
{
AIMessage
,
AIData
,
AI_OPTIONS
}
from
'@/utils/ai'
export
function
useAI
()
{
const
options
:
AIOption
[]
=
[
{
label
:
'文心一言'
,
value
:
'yiyan'
},
{
label
:
'DeepSeek'
,
value
:
'deepseek'
},
{
label
:
'通义千问'
,
value
:
'qwen'
},
{
label
:
'天工'
,
value
:
'tiangong'
},
]
const
[
ai
,
setAI
]
=
useState
<
string
>
(
localStorage
.
getItem
(
'ai'
)
||
'qwen'
)
const
[
messages
,
setMessages
]
=
useState
<
AIMessage
[]
>
([])
const
[
isLoading
,
setIsLoading
]
=
useState
<
boolean
>
(
false
)
console
.
log
(
messages
)
useEffect
(()
=>
{
localStorage
.
setItem
(
'ai'
,
ai
)
},
[
ai
])
const
post
=
useCallback
(
async
(
data
:
{
content
:
string
}
)
=>
{
async
(
data
:
AIData
)
=>
{
setIsLoading
(
true
)
setMessages
((
prevMessages
)
=>
[...
prevMessages
,
{
role
:
'user'
,
content
:
data
.
content
}
])
setMessages
((
prevMessages
)
=>
[...
prevMessages
,
...
data
.
messages
.
filter
((
item
)
=>
item
.
role
!==
'system'
)
])
try
{
switch
(
ai
)
{
case
'yiyan'
:
await
yiyan
(
data
)
break
case
'deepseek'
:
await
deepseek
(
data
)
break
case
'qwen'
:
await
qwen
(
data
)
break
case
'tiangong'
:
await
tiangong
(
data
)
break
default
:
throw
new
Error
(
'未找到对应的 AI 配置'
)
}
await
aiService
.
post
(
ai
,
data
,
{
onUpdate
:
(
response
)
=>
{
setMessages
((
prevMessages
)
=>
{
const
messageIndex
=
prevMessages
.
findIndex
((
msg
)
=>
msg
.
id
===
response
.
id
)
if
(
messageIndex
===
-
1
)
{
return
[...
prevMessages
,
{
id
:
response
.
id
,
role
:
'assistant'
,
content
:
response
.
content
}]
}
else
{
return
prevMessages
.
map
((
msg
)
=>
msg
.
id
===
response
.
id
?
{
...
msg
,
content
:
msg
.
content
+
response
.
content
}
:
msg
)
}
})
},
onError
:
(
err
)
=>
{
console
.
error
(
'AI 请求失败:'
,
err
)
setIsLoading
(
false
)
},
onComplete
:
()
=>
{
setIsLoading
(
false
)
},
})
}
catch
(
err
)
{
console
.
error
(
'AI 请求失败:'
,
err
)
}
finally
{
setIsLoading
(
false
)
}
},
[
ai
]
)
async
function
yiyan
(
data
:
any
)
{
const
getAccessToken
=
async
()
=>
{
const
AK
=
'wY7bvMpkWeZbDVq9w3EDvpjU'
const
SK
=
'XJwpiJWxs5HXkOtbo6tQrvYPZFJAWdAy'
const
resp
=
await
axios
.
post
(
`/api/qianfan/oauth/2.0/token?grant_type=client_credentials&client_id=
${
AK
}
&client_secret=
${
SK
}
`
)
return
resp
.
data
.
access_token
}
const
resp
=
await
axios
.
post
(
`/api/qianfan/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant?access_token=
${
await
getAccessToken
()}
`
,
{
messages
:
[{
role
:
'user'
,
content
:
data
.
content
}],
}
)
setMessages
((
prevMessages
)
=>
[...
prevMessages
,
{
role
:
'assistant'
,
content
:
resp
.
data
.
result
}])
}
async
function
deepseek
(
data
:
any
)
{
const
apiKey
=
'sk-f1a6f0a7013241de8393cb2cb108e777'
const
resp
=
await
axios
.
post
(
'/api/deepseek/chat/completions'
,
{
model
:
'deepseek-chat'
,
messages
:
[{
role
:
'user'
,
content
:
data
.
content
}],
},
{
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
}
)
if
(
resp
.
data
)
{
const
[
choice
=
{}]
=
resp
.
data
.
choices
setMessages
((
prevMessages
)
=>
[...
prevMessages
,
{
role
:
'assistant'
,
content
:
choice
.
message
.
content
}])
}
}
async
function
qwen
(
data
:
any
)
{
const
apiKey
=
'sk-afd0fcdb53bf4058b2068b8548820150'
const
resp
=
await
axios
.
post
(
'/api/qwen/compatible-mode/v1/chat/completions'
,
{
model
:
'qwen-max'
,
messages
:
[{
role
:
'user'
,
content
:
data
.
content
}],
},
{
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
}
)
if
(
resp
.
data
)
{
const
[
choice
=
{}]
=
resp
.
data
.
choices
setMessages
((
prevMessages
)
=>
[...
prevMessages
,
{
role
:
'assistant'
,
content
:
choice
.
message
.
content
}])
}
}
async
function
tiangong
(
data
:
any
)
{
const
appKey
=
'a8701b73637562d33a53c668a90ee3be'
const
appSecret
=
'e191593f486bb88a39c634f46926762dddc97b9082e192af'
const
timestamp
=
Math
.
floor
(
Date
.
now
()
/
1000
).
toString
()
const
sign
=
md5
(
`
${
appKey
}${
appSecret
}${
timestamp
}
`
)
return
await
fetchEventSource
(
'/api/tiangong/sky-saas-writing/api/v1/chat'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
app_key
:
appKey
,
sign
,
timestamp
,
stream
:
'true'
},
body
:
JSON
.
stringify
({
chat_history
:
[{
role
:
'user'
,
content
:
data
.
content
}],
stream_resp_type
:
'update'
,
}),
onmessage
(
res
)
{
console
.
log
(
res
.
data
)
const
message
=
JSON
.
parse
(
res
.
data
)
if
(
message
.
type
!==
1
)
return
setMessages
((
prevMessages
)
=>
{
const
messageId
=
message
.
conversation_id
const
messageIndex
=
prevMessages
.
findIndex
((
message
)
=>
message
.
id
===
messageId
)
const
content
=
message
?.
arguments
?.[
0
]?.
messages
?.[
0
]?.
text
||
''
if
(
messageIndex
===
-
1
)
{
return
[...
prevMessages
,
{
id
:
messageId
,
role
:
'assistant'
,
content
}]
}
else
{
return
prevMessages
.
map
((
msg
)
=>
(
msg
.
id
===
messageId
?
{
...
msg
,
content
}
:
msg
))
}
})
setIsLoading
(
false
)
},
onerror
(
err
)
{
setIsLoading
(
false
)
throw
err
},
})
}
return
{
ai
,
setAI
,
options
,
post
,
messages
,
isLoading
}
return
{
ai
,
setAI
,
options
:
AI_OPTIONS
,
post
,
messages
,
isLoading
}
}
src/hooks/useQuery.ts
浏览文件 @
7dae00a4
...
...
@@ -44,6 +44,11 @@ export function useDataQuery() {
return
{
data
:
{
total
:
0
,
list
:
[],
title
:
[]
}
}
},
})
// useEffect(() => {
// if (query.data) {
// localStorage.setItem('dataset', JSON.stringify(query.data))
// }
// }, [query.data])
return
query
}
...
...
src/main.tsx
浏览文件 @
7dae00a4
...
...
@@ -5,7 +5,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import
{
ConfigProvider
}
from
'antd'
import
zhCN
from
'antd/locale/zh_CN'
import
App
from
'./App.tsx'
import
{
ReactQueryDevtools
}
from
'@tanstack/react-query-devtools'
//
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
const
queryClient
=
new
QueryClient
()
const
antdTheme
=
{
...
...
@@ -19,7 +19,7 @@ const antdTheme = {
ReactDOM
.
createRoot
(
document
.
getElementById
(
'root'
)
!
).
render
(
<
React
.
StrictMode
>
<
QueryClientProvider
client=
{
queryClient
}
>
<
ReactQueryDevtools
/>
{
/* <ReactQueryDevtools /> */
}
<
ConfigProvider
theme=
{
antdTheme
}
locale=
{
zhCN
}
>
<
BrowserRouter
>
<
App
/>
...
...
src/modules/data/read/exploration/components/NullButtonModal.tsx
浏览文件 @
7dae00a4
import
{
useState
}
from
'react'
import
{
use
Effect
,
use
State
}
from
'react'
import
{
Button
,
Checkbox
,
Flex
,
Modal
}
from
'antd'
import
type
{
CheckboxProps
}
from
'antd'
import
{
useNavigate
}
from
'react-router'
import
AppSteps
from
'@/components/AppSteps'
import
{
useAI
}
from
'@/hooks/useAI'
const
CheckboxGroup
=
Checkbox
.
Group
const
plainOptions
=
[
'会员情况'
,
'客户满意度'
,
'出生日期'
,
'访问页面时长'
,
'交易状态'
,
'商品状态'
,
'快递反馈'
]
const
defaultCheckedList
=
[
'会员情况'
,
'客户满意度'
]
export
default
function
ButtonModal
()
{
const
{
isLoading
,
messages
,
post
}
=
useAI
()
const
lastMessage
=
messages
[
messages
.
length
-
1
]
const
[
fields
,
setFields
]
=
useState
<
any
>
([])
const
fieldsOptions
=
fields
.
map
((
field
:
any
)
=>
({
label
:
field
.
name
,
value
:
field
.
english_name
,
}))
useEffect
(()
=>
{
if
(
lastMessage
?.
role
===
'assistant'
&&
lastMessage
?.
content
)
{
try
{
const
parse
=
JSON
.
parse
(
lastMessage
.
content
)
setFields
(
parse
.
fields
||
[])
}
catch
(
error
)
{
console
.
error
(
error
)
}
}
},
[
lastMessage
])
const
handleSearch
=
()
=>
{
post
({
response_format
:
{
type
:
'json_object'
,
},
messages
:
[
{
role
:
'user'
,
content
:
`找出数据集中有缺失值的字段,并返回 JSON 格式的结果。返回内容仅包含缺失字段的名称(name)及其对应的英文名(english_name)该字段有缺失值的数据有哪些,字段像主键的数据(desc)。如果没有缺失值,则返回空数组 []。
返回格式示例:
json
{
"fields": [
{"name": "字段1", "english_name": "字段英文名", "desc": "A000001、A000002..."},
{"name": "字段2", "english_name": "字段英文名2", "desc": "A000001、A000002..."}
]
}
要求:
仅包含缺失值的字段(为空、null、undefined 等视为缺失)。
确保字段名称和英文名准确无误。
如果所有字段都有值,则返回 { "fields": [] }。`
,
},
],
})
}
const
navigate
=
useNavigate
()
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
current
,
setCurrent
]
=
useState
(
0
)
const
[
checkedList
,
setCheckedList
]
=
useState
<
string
[]
>
(
defaultCheckedList
)
const
[
checkedList
,
setCheckedList
]
=
useState
<
string
[]
>
(
[]
)
const
checkAll
=
plain
Options
.
length
===
checkedList
.
length
const
indeterminate
=
checkedList
.
length
>
0
&&
checkedList
.
length
<
plain
Options
.
length
const
checkAll
=
fields
Options
.
length
===
checkedList
.
length
const
indeterminate
=
checkedList
.
length
>
0
&&
checkedList
.
length
<
fields
Options
.
length
const
onChange
=
(
list
:
string
[])
=>
{
setCheckedList
(
list
)
}
const
onCheckAllChange
:
CheckboxProps
[
'onChange'
]
=
(
e
)
=>
{
setCheckedList
(
e
.
target
.
checked
?
plain
Options
:
[])
setCheckedList
(
e
.
target
.
checked
?
fields
Options
:
[])
}
const
next
=
()
=>
{
...
...
@@ -47,7 +88,7 @@ export default function ButtonModal() {
全选
</
Checkbox
>
</
Flex
>
<
CheckboxGroup
options=
{
plain
Options
}
value=
{
checkedList
}
onChange=
{
onChange
}
/>
<
CheckboxGroup
options=
{
fields
Options
}
value=
{
checkedList
}
onChange=
{
onChange
}
/>
</>
),
},
...
...
@@ -58,11 +99,11 @@ export default function ButtonModal() {
<
Flex
justify=
"space-between"
style=
{
{
marginBottom
:
20
}
}
>
探索结果:
</
Flex
>
<
p
>
字段1:该字段有缺失值的数据有:A000001、A000002...
</
p
>
<
p
>
字段1:该字段有缺失值的数据有:A000001、A000002...
</
p
>
<
p
>
字段1:该字段有缺失值的数据有:A000001、A000002...
</
p
>
<
p
>
字段1:该字段有缺失值的数据有:A000001、A000002...
</
p
>
<
p
>
字段1:该字段有缺失值的数据有:A000001、A000002...
</
p
>
{
fields
.
map
((
item
:
any
)
=>
(
<
p
key=
{
item
.
english_name
}
>
{
item
.
name
}
:
{
item
.
desc
}
</
p
>
))
}
</>
),
},
...
...
@@ -76,7 +117,7 @@ export default function ButtonModal() {
全选
</
Checkbox
>
</
Flex
>
<
CheckboxGroup
options=
{
plain
Options
}
value=
{
checkedList
}
onChange=
{
onChange
}
/>
<
CheckboxGroup
options=
{
fields
Options
}
value=
{
checkedList
}
onChange=
{
onChange
}
/>
</>
),
},
...
...
@@ -92,7 +133,11 @@ export default function ButtonModal() {
open=
{
open
}
footer=
{
<
Flex
justify=
"center"
gap=
{
20
}
>
{
current
===
0
&&
<
Button
type=
"primary"
>
智能探索缺失值字段
</
Button
>
}
{
current
===
0
&&
(
<
Button
type=
"primary"
loading=
{
isLoading
}
onClick=
{
handleSearch
}
>
智能探索缺失值字段
</
Button
>
)
}
{
current
===
0
&&
(
<
Button
type=
"primary"
disabled=
{
!
checkedList
.
length
}
onClick=
{
()
=>
next
()
}
>
进一步探索字段缺失值情况
...
...
src/modules/data/read/understanding/views/Index.tsx
浏览文件 @
7dae00a4
import
DataWrap
from
'@/components/data/DataWrap'
import
{
Button
}
from
'antd'
import
{
useDataQuery
}
from
'@/hooks/useQuery'
import
{
useAIStore
}
from
'@/stores/ai'
export
default
function
DataWriteUpload
()
{
const
{
post
,
isLoading
}
=
useAIStore
(
)
const
{
data
}
=
useDataQuery
(
)
const
post
=
useAIStore
((
state
)
=>
state
.
post
)
const
isLoading
=
useAIStore
((
state
)
=>
state
.
isLoading
)
function
handleClick
(
type
:
number
)
{
if
(
type
==
1
)
{
post
({
content
:
'请仔细阅读我给你的数据集里面的数据,然后帮助我详细解释一下数据集里面每一个字段的含义。'
,
extra
:
JSON
.
stringify
(
data
.
list
),
messages
:
[
{
role
:
'user'
,
content
:
'请仔细阅读我给你的数据集里面的数据,然后帮助我详细解释一下数据集里面每一个字段的含义。'
,
},
],
})
}
else
{
post
({
content
:
'请仔细阅读我给你的数据集里面的数据,然后帮助我梳理数据集里面字段与字段之间的业务逻辑关系。'
,
extra
:
JSON
.
stringify
(
data
.
list
),
messages
:
[
{
role
:
'user'
,
content
:
'请仔细阅读我给你的数据集里面的数据,然后帮助我梳理数据集里面字段与字段之间的业务逻辑关系。'
,
},
],
})
}
}
...
...
src/modules/data/write/upload/views/Index.tsx
浏览文件 @
7dae00a4
...
...
@@ -36,6 +36,8 @@ export default function DataWriteUpload() {
const
jsonData
=
utils
.
sheet_to_json
(
worksheet
)
setDataset
(
jsonData
)
localStorage
.
setItem
(
'dataset'
,
JSON
.
stringify
(
jsonData
))
return
false
},
}
...
...
src/stores/ai.ts
浏览文件 @
7dae00a4
import
{
create
}
from
'zustand'
import
md5
from
'blueimp-md5'
import
axios
from
'axios'
import
{
fetchEventSource
}
from
'@fortaine/fetch-event-source'
export
interface
AIOption
{
label
:
string
value
:
string
}
export
interface
AIMessage
{
id
?:
string
role
:
'user'
|
'assistant'
content
:
string
}
import
aiService
,
{
AIOption
,
AIMessage
,
AIData
,
AI_OPTIONS
}
from
'@/utils/ai'
interface
AIState
{
ai
:
string
...
...
@@ -20,144 +7,56 @@ interface AIState {
messages
:
AIMessage
[]
isLoading
:
boolean
setAI
:
(
ai
:
string
)
=>
void
post
:
(
data
:
{
content
:
string
}
)
=>
Promise
<
void
>
post
:
(
data
:
AIData
)
=>
Promise
<
void
>
}
export
const
useAIStore
=
create
<
AIState
>
((
set
,
get
)
=>
({
ai
:
localStorage
.
getItem
(
'ai'
)
||
'qwen'
,
options
:
[
{
label
:
'文心一言'
,
value
:
'yiyan'
},
{
label
:
'DeepSeek'
,
value
:
'deepseek'
},
{
label
:
'通义千问'
,
value
:
'qwen'
},
{
label
:
'天工'
,
value
:
'tiangong'
},
],
options
:
AI_OPTIONS
,
messages
:
[],
isLoading
:
false
,
setAI
:
(
ai
)
=>
{
localStorage
.
setItem
(
'ai'
,
ai
)
set
({
ai
})
},
post
:
async
(
data
)
=>
{
const
{
ai
,
messages
}
=
get
()
set
({
isLoading
:
true
,
messages
:
[...
messages
,
{
role
:
'user'
,
content
:
data
.
content
}]
})
set
({
isLoading
:
true
,
messages
:
[...
messages
,
...
data
.
messages
.
filter
((
item
)
=>
item
.
role
!==
'system'
)]
})
try
{
switch
(
ai
)
{
case
'yiyan'
:
await
yiyan
(
data
)
break
case
'deepseek'
:
await
deepseek
(
data
)
break
case
'qwen'
:
await
qwen
(
data
)
break
case
'tiangong'
:
await
tiangong
(
data
)
break
default
:
throw
new
Error
(
'未找到对应的 AI 配置'
)
}
await
aiService
.
post
(
ai
,
data
,
{
onUpdate
:
(
response
)
=>
{
set
((
state
)
=>
{
const
messageIndex
=
state
.
messages
.
findIndex
((
msg
)
=>
msg
.
id
===
response
.
id
)
if
(
messageIndex
===
-
1
)
{
return
{
messages
:
[...
state
.
messages
,
{
id
:
response
.
id
,
role
:
'assistant'
,
content
:
response
.
content
}],
}
}
else
{
return
{
messages
:
state
.
messages
.
map
((
msg
)
=>
msg
.
id
===
response
.
id
?
{
...
msg
,
content
:
msg
.
content
+
response
.
content
}
:
msg
),
}
}
})
},
onError
:
(
err
)
=>
{
console
.
error
(
'AI 请求失败:'
,
err
)
set
({
isLoading
:
false
})
},
onComplete
:
()
=>
{
set
({
isLoading
:
false
})
},
})
}
catch
(
err
)
{
console
.
error
(
'AI 请求失败:'
,
err
)
}
finally
{
set
({
isLoading
:
false
})
}
},
}))
async
function
yiyan
(
data
:
any
)
{
const
getAccessToken
=
async
()
=>
{
const
AK
=
'wY7bvMpkWeZbDVq9w3EDvpjU'
const
SK
=
'XJwpiJWxs5HXkOtbo6tQrvYPZFJAWdAy'
const
resp
=
await
axios
.
post
(
`/api/qianfan/oauth/2.0/token?grant_type=client_credentials&client_id=
${
AK
}
&client_secret=
${
SK
}
`
)
return
resp
.
data
.
access_token
}
const
resp
=
await
axios
.
post
(
`/api/qianfan/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant?access_token=
${
await
getAccessToken
()}
`
,
{
messages
:
[{
role
:
'user'
,
content
:
data
.
content
}],
}
)
useAIStore
.
setState
((
state
)
=>
({
messages
:
[...
state
.
messages
,
{
role
:
'assistant'
,
content
:
resp
.
data
.
result
}],
}))
}
async
function
deepseek
(
data
:
any
)
{
const
apiKey
=
'sk-f1a6f0a7013241de8393cb2cb108e777'
const
resp
=
await
axios
.
post
(
'/api/deepseek/chat/completions'
,
{
model
:
'deepseek-chat'
,
messages
:
[{
role
:
'user'
,
content
:
data
.
content
}],
},
{
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
}
)
if
(
resp
.
data
)
{
const
[
choice
=
{}]
=
resp
.
data
.
choices
useAIStore
.
setState
((
state
)
=>
({
messages
:
[...
state
.
messages
,
{
role
:
'assistant'
,
content
:
choice
.
message
.
content
}],
}))
}
}
async
function
qwen
(
data
:
any
)
{
const
apiKey
=
'sk-afd0fcdb53bf4058b2068b8548820150'
const
resp
=
await
axios
.
post
(
'/api/qwen/compatible-mode/v1/chat/completions'
,
{
model
:
'qwen-max'
,
messages
:
[{
role
:
'user'
,
content
:
data
.
content
}],
},
{
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
}
)
if
(
resp
.
data
)
{
const
[
choice
=
{}]
=
resp
.
data
.
choices
useAIStore
.
setState
((
state
)
=>
({
messages
:
[...
state
.
messages
,
{
role
:
'assistant'
,
content
:
choice
.
message
.
content
}],
}))
}
}
async
function
tiangong
(
data
:
any
)
{
const
appKey
=
'a8701b73637562d33a53c668a90ee3be'
const
appSecret
=
'e191593f486bb88a39c634f46926762dddc97b9082e192af'
const
timestamp
=
Math
.
floor
(
Date
.
now
()
/
1000
).
toString
()
const
sign
=
md5
(
`
${
appKey
}${
appSecret
}${
timestamp
}
`
)
return
await
fetchEventSource
(
'/api/tiangong/sky-saas-writing/api/v1/chat'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
app_key
:
appKey
,
sign
,
timestamp
,
stream
:
'true'
},
body
:
JSON
.
stringify
({
chat_history
:
[{
role
:
'user'
,
content
:
data
.
content
}],
stream_resp_type
:
'update'
,
}),
onmessage
(
res
)
{
console
.
log
(
res
.
data
)
const
message
=
JSON
.
parse
(
res
.
data
)
if
(
message
.
type
!==
1
)
return
useAIStore
.
setState
((
state
)
=>
{
const
messageId
=
message
.
conversation_id
const
messageIndex
=
state
.
messages
.
findIndex
((
msg
)
=>
msg
.
id
===
messageId
)
const
content
=
message
?.
arguments
?.[
0
]?.
messages
?.[
0
]?.
text
||
''
if
(
messageIndex
===
-
1
)
{
return
{
messages
:
[...
state
.
messages
,
{
id
:
messageId
,
role
:
'assistant'
,
content
}]
}
}
else
{
return
{
messages
:
state
.
messages
.
map
((
msg
)
=>
(
msg
.
id
===
messageId
?
{
...
msg
,
content
}
:
msg
)),
}
}
})
},
onerror
(
err
)
{
useAIStore
.
setState
({
isLoading
:
false
})
throw
err
},
})
}
export
type
{
AIMessage
}
src/utils/ai.ts
0 → 100644
浏览文件 @
7dae00a4
import
md5
from
'blueimp-md5'
import
axios
from
'axios'
import
{
fetchEventSource
}
from
'@fortaine/fetch-event-source'
export
interface
AIOption
{
label
:
string
value
:
string
}
export
interface
AIMessage
{
id
?:
string
role
:
'user'
|
'assistant'
|
'system'
content
:
string
}
export
interface
AIData
{
model
?:
string
messages
:
AIMessage
[]
}
export
interface
AIResponse
{
content
:
string
id
?:
string
isStream
?:
boolean
}
export
interface
AIStreamHandlers
{
onUpdate
:
(
response
:
AIResponse
)
=>
void
onError
:
(
error
:
any
)
=>
void
onComplete
?:
()
=>
void
}
// Available AI options for different implementations
export
const
AI_OPTIONS
:
AIOption
[]
=
[
// { label: '文心一言', value: 'yiyan' },
{
label
:
'DeepSeek'
,
value
:
'siliconflow'
},
{
label
:
'通义千问'
,
value
:
'qwen'
},
// { label: '天工', value: 'tiangong' },
]
// Individual AI service functions
export
async
function
getYiyanAccessToken
()
{
const
AK
=
'wY7bvMpkWeZbDVq9w3EDvpjU'
const
SK
=
'XJwpiJWxs5HXkOtbo6tQrvYPZFJAWdAy'
const
resp
=
await
axios
.
post
(
`/api/qianfan/oauth/2.0/token?grant_type=client_credentials&client_id=
${
AK
}
&client_secret=
${
SK
}
`
)
return
resp
.
data
.
access_token
}
// https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Fm2vrveyu
export
async
function
yiyan
(
data
:
AIData
,
handlers
:
AIStreamHandlers
):
Promise
<
void
>
{
const
accessToken
=
await
getYiyanAccessToken
()
const
params
=
{
stream
:
true
,
...
data
}
await
fetchEventSource
(
`/api/qianfan/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant?access_token=
${
accessToken
}
`
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
},
body
:
JSON
.
stringify
(
params
),
async
onopen
(
response
)
{
if
(
response
.
ok
)
{
return
}
else
{
throw
response
}
},
onmessage
(
res
)
{
try
{
const
message
=
JSON
.
parse
(
res
.
data
)
handlers
.
onUpdate
({
id
:
message
.
id
,
content
:
message
.
result
||
''
})
}
catch
(
error
)
{
console
.
error
(
error
)
}
},
onerror
(
err
)
{
handlers
.
onError
(
err
)
},
onclose
()
{
if
(
handlers
.
onComplete
)
{
handlers
.
onComplete
()
}
},
}
)
}
// https://api-docs.deepseek.com/zh-cn/api/create-chat-completion
export
async
function
deepseek
(
data
:
AIData
,
handlers
:
AIStreamHandlers
):
Promise
<
void
>
{
const
apiKey
=
'sk-f1a6f0a7013241de8393cb2cb108e777'
const
params
=
{
model
:
'deepseek-chat'
,
stream
:
true
,
...
data
}
await
fetchEventSource
(
'/api/deepseek/chat/completions'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
body
:
JSON
.
stringify
(
params
),
async
onopen
(
response
)
{
if
(
response
.
ok
)
{
return
}
else
{
throw
response
}
},
onmessage
(
res
)
{
try
{
const
message
=
JSON
.
parse
(
res
.
data
)
if
(
message
.
choices
&&
message
.
choices
.
length
>
0
)
{
handlers
.
onUpdate
({
id
:
message
.
id
,
content
:
message
.
choices
[
0
].
delta
?.
content
||
''
,
})
}
}
catch
(
error
)
{
console
.
error
(
error
)
}
},
onerror
(
err
)
{
handlers
.
onError
(
err
)
},
onclose
()
{
if
(
handlers
.
onComplete
)
{
handlers
.
onComplete
()
}
},
})
}
// https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions
export
async
function
siliconflow
(
data
:
AIData
,
handlers
:
AIStreamHandlers
):
Promise
<
void
>
{
const
apiKey
=
'sk-bivnwauskdbvpspvmdorrgkrpwlyfxbfcezqsfsevowzubdj'
const
params
=
{
model
:
'deepseek-ai/DeepSeek-V3'
,
stream
:
true
,
...
data
}
await
fetchEventSource
(
'/api/siliconflow/v1/chat/completions'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
body
:
JSON
.
stringify
(
params
),
async
onopen
(
response
)
{
if
(
response
.
ok
)
{
return
}
else
{
throw
response
}
},
onmessage
(
res
)
{
try
{
const
message
=
JSON
.
parse
(
res
.
data
)
if
(
message
.
choices
&&
message
.
choices
.
length
>
0
)
{
handlers
.
onUpdate
({
id
:
message
.
id
,
content
:
message
.
choices
[
0
].
delta
?.
content
||
''
,
})
}
}
catch
(
error
)
{
console
.
error
(
error
)
}
},
onerror
(
err
)
{
handlers
.
onError
(
err
)
},
onclose
()
{
if
(
handlers
.
onComplete
)
{
handlers
.
onComplete
()
}
},
})
}
// https://help.aliyun.com/zh/model-studio/developer-reference/use-qwen-by-calling-api
export
async
function
qwen
(
data
:
AIData
,
handlers
:
AIStreamHandlers
):
Promise
<
void
>
{
const
apiKey
=
'sk-afd0fcdb53bf4058b2068b8548820150'
const
params
=
{
model
:
'qwen-max'
,
stream
:
true
,
...
data
}
await
fetchEventSource
(
'/api/qwen/compatible-mode/v1/chat/completions'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
body
:
JSON
.
stringify
(
params
),
async
onopen
(
response
)
{
if
(
response
.
ok
)
{
return
}
else
{
throw
response
}
},
onmessage
(
res
)
{
try
{
const
message
=
JSON
.
parse
(
res
.
data
)
if
(
message
.
choices
&&
message
.
choices
.
length
>
0
)
{
handlers
.
onUpdate
({
id
:
message
.
id
,
content
:
message
.
choices
[
0
].
delta
?.
content
||
''
,
})
}
}
catch
(
error
)
{
console
.
error
(
error
)
}
},
onerror
(
err
)
{
handlers
.
onError
(
err
)
},
onclose
()
{
if
(
handlers
.
onComplete
)
{
handlers
.
onComplete
()
}
},
})
}
export
async
function
tiangong
(
data
:
AIData
,
handlers
:
AIStreamHandlers
):
Promise
<
void
>
{
const
appKey
=
'a8701b73637562d33a53c668a90ee3be'
const
appSecret
=
'e191593f486bb88a39c634f46926762dddc97b9082e192af'
const
timestamp
=
Math
.
floor
(
Date
.
now
()
/
1000
).
toString
()
const
sign
=
md5
(
`
${
appKey
}${
appSecret
}${
timestamp
}
`
)
await
fetchEventSource
(
'/api/tiangong/sky-saas-writing/api/v1/chat'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
app_key
:
appKey
,
sign
,
timestamp
,
stream
:
'true'
,
},
body
:
JSON
.
stringify
({
chat_history
:
data
.
messages
,
stream_resp_type
:
'delta'
,
}),
async
onopen
(
response
)
{
if
(
response
.
ok
)
{
return
}
else
{
throw
response
}
},
onmessage
(
res
)
{
try
{
const
message
=
JSON
.
parse
(
res
.
data
)
if
(
message
.
type
!==
1
)
return
const
messageId
=
message
.
conversation_id
const
messageContent
=
message
?.
arguments
?.[
0
]?.
messages
?.[
0
]?.
text
||
''
handlers
.
onUpdate
({
id
:
messageId
,
content
:
messageContent
,
})
}
catch
(
error
)
{
console
.
error
(
error
)
}
},
onerror
(
err
)
{
handlers
.
onError
(
err
)
},
onclose
()
{
if
(
handlers
.
onComplete
)
{
handlers
.
onComplete
()
}
},
})
}
// Core AI API implementation
const
aiService
=
{
yiyan
,
deepseek
,
siliconflow
,
qwen
,
tiangong
,
async
post
(
type
:
string
,
data
:
AIData
,
handlers
:
AIStreamHandlers
):
Promise
<
void
>
{
const
messages
:
AIMessage
[]
=
[]
const
dataset
=
localStorage
.
getItem
(
'dataset'
)
if
(
dataset
)
{
const
datasetInfo
=
JSON
.
parse
(
dataset
)
messages
.
push
({
role
:
'system'
,
content
:
`这是一个数据集:
${
JSON
.
stringify
(
datasetInfo
)}
`
})
}
data
.
messages
=
[...
messages
,
...
data
.
messages
]
switch
(
type
)
{
case
'yiyan'
:
return
yiyan
(
data
,
handlers
)
case
'deepseek'
:
return
deepseek
(
data
,
handlers
)
case
'siliconflow'
:
return
siliconflow
(
data
,
handlers
)
case
'qwen'
:
return
qwen
(
data
,
handlers
)
case
'tiangong'
:
return
tiangong
(
data
,
handlers
)
default
:
throw
new
Error
(
`未找到对应的 AI 配置:
${
type
}
`
)
}
},
}
export
default
aiService
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论