提交 7dae00a4 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 cadb59e8
......@@ -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={100}></Select>
popupMatchSelectWidth={110}></Select>
<Button
type="primary"
shape="circle"
......
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 }
}
......@@ -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
}
......
......@@ -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 />
......
import { useState } from 'react'
import { useEffect, useState } 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 = plainOptions.length === checkedList.length
const indeterminate = checkedList.length > 0 && checkedList.length < plainOptions.length
const checkAll = fieldsOptions.length === checkedList.length
const indeterminate = checkedList.length > 0 && checkedList.length < fieldsOptions.length
const onChange = (list: string[]) => {
setCheckedList(list)
}
const onCheckAllChange: CheckboxProps['onChange'] = (e) => {
setCheckedList(e.target.checked ? plainOptions : [])
setCheckedList(e.target.checked ? fieldsOptions : [])
}
const next = () => {
......@@ -47,7 +88,7 @@ export default function ButtonModal() {
全选
</Checkbox>
</Flex>
<CheckboxGroup options={plainOptions} value={checkedList} onChange={onChange} />
<CheckboxGroup options={fieldsOptions} 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={plainOptions} value={checkedList} onChange={onChange} />
<CheckboxGroup options={fieldsOptions} 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()}>
进一步探索字段缺失值情况
......
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: '请仔细阅读我给你的数据集里面的数据,然后帮助我梳理数据集里面字段与字段之间的业务逻辑关系。',
},
],
})
}
}
......
......@@ -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
},
}
......
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 }
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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论