Commit bc0509b3 by Liu

feat:引入sdk方法

parent 6992b396
......@@ -8,6 +8,10 @@ import { envConf } from './src/config/env'
const mode = process.env.MODE as EnvConfKey || 'dev'
const apiUrl = envConf[mode].apiUrl || ''
const proxyUrl = envConf[mode].proxyUrl || ''
const sdkCode = envConf[mode].sdkCode || ''
const sdkChannel = envConf[mode].sdkChannel || ''
const sdkMock = envConf[mode].sdkMock || false
const sdkApiBaseUrl = envConf[mode].sdkApiBaseUrl || ''
// 处理 VITE_ 前缀的环境变量
const viteEnvVars: Record<string, string> = {}
......@@ -50,10 +54,20 @@ export default defineConfig({
font: 'assets',
media: 'assets',
},
copy: [
{
from: './src/sdk/',
to: './sdk/',
},
],
},
source: {
define: {
'import.meta.env.MODE': JSON.stringify(mode),
'import.meta.env.VITE_SDK_CODE': JSON.stringify(sdkCode),
'import.meta.env.VITE_SDK_CHANNEL': JSON.stringify(sdkChannel),
'import.meta.env.VITE_SDK_MOCK': JSON.stringify(sdkMock),
'import.meta.env.VITE_SDK_API_BASE_URL': JSON.stringify(sdkApiBaseUrl),
...viteEnvVars,
},
},
......
export function fetchStreamResponse(
url: string,
body: Record<string, any>,
onMessage: (msg: any) => void,
signal?: AbortSignal,
): Promise<void> {
const ChatSDK = (window as any).ChatSDK
if (ChatSDK && typeof ChatSDK.fetchStreamResponse === 'function') {
return ChatSDK.fetchStreamResponse(url, body, onMessage, signal)
}
const STREAM_TIMEOUT_MS = 30 * 60 * 1000
body.stream = true
const decoder = new TextDecoder('utf-8')
let buffer = ''
let dataMsgBuffer = ''
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), STREAM_TIMEOUT_MS)
if (signal) {
signal.addEventListener('abort', () => controller.abort())
}
const effectiveSignal = controller.signal
const cleanup = () => clearTimeout(timeoutId)
return new Promise<void>((resolve, reject) => {
function processMessage(reader: any) {
reader.read().then((content: any) => {
if (effectiveSignal.aborted) {
cleanup()
resolve()
return
}
buffer += decoder.decode(content.value, { stream: !content.done })
const lines = buffer.split('\n')
buffer = lines.pop() as string
lines.forEach((line) => {
if (line === '') {
try {
onMessage({
type: 'DATA',
content: JSON.parse(dataMsgBuffer),
})
}
catch {
}
dataMsgBuffer = ''
return
}
const [type] = line.split(':', 1)
const content = line.substring(type.length + 1)
if (type === 'data') {
dataMsgBuffer += content.trim()
}
else if (type === '' && content !== '') {
onMessage({
type: 'COMMENT',
content: content.trim(),
})
}
else {
onMessage({
type,
content: content.trim(),
})
}
})
if (!content.done) {
processMessage(reader)
}
else {
cleanup()
resolve()
}
}).catch((error: unknown) => {
if (error instanceof Error && error.name === 'AbortError') {
cleanup()
resolve()
return
}
onMessage({ type: 'ERROR', content: error })
cleanup()
reject(error)
})
}
const tokenStr = window.localStorage.getItem('__TOKEN__') || '""'
let token = ''
try {
token = JSON.parse(tokenStr)
}
catch {
token = ''
}
fetch(url, {
headers: {
'accept': 'text/event-stream',
'Content-Type': 'application/json',
'X-Token': token,
},
method: 'POST',
body: JSON.stringify(body),
signal: effectiveSignal,
})
.then((response) => {
return response.body?.getReader()
})
.then((reader) => {
if (!reader) {
cleanup()
reject(new Error('Failed to get reader from response'))
return
}
return processMessage(reader)
})
.catch((error: unknown) => {
if (error instanceof Error && error.name === 'AbortError') {
cleanup()
resolve()
return
}
onMessage({ type: 'ERROR', content: error })
cleanup()
reject(error)
})
})
}
......@@ -2,14 +2,26 @@ export const envConf = {
dev: {
apiUrl: '/api',
proxyUrl: 'http://43.143.240.58:19002/dev-sdream-api',
sdkCode: 'dev_code',
sdkChannel: 'dev_channel',
sdkMock: true,
sdkApiBaseUrl: 'http://43.143.240.58:19002/dev-sdream-api',
},
sit: {
apiUrl: '/api',
proxyUrl: 'http://43.143.240.58:19002/dev-sdream-api',
sdkCode: 'sit_code',
sdkChannel: 'sit_channel',
sdkMock: false,
sdkApiBaseUrl: 'http://43.143.240.58:19002/dev-sdream-api',
},
prod: {
apiUrl: 'http://43.143.240.58:19002/dev-sdream-api',
proxyUrl: 'http://43.143.240.58:19002/dev-sdream-api',
sdkCode: 'prod_code',
sdkChannel: 'prod_channel',
sdkMock: false,
sdkApiBaseUrl: 'http://43.143.240.58:19002/dev-sdream-api',
},
}
......
......@@ -10,7 +10,6 @@ const rootEl = document.getElementById('root')
document.documentElement.classList.add('light')
// 添加 Object.hasOwn 的 polyfill
if (!Object.hasOwn) {
Object.defineProperty(Object, 'hasOwn', {
value(object: any, property: PropertyKey) {
......@@ -24,13 +23,62 @@ if (!Object.hasOwn) {
writable: true,
})
}
// printText2Console()
if (rootEl) {
async function loadSDK(): Promise<void> {
return new Promise((resolve) => {
const checkAndInitSDK = () => {
// const ChatSDK = (window as any).ChatSDK
// if (ChatSDK && ChatSDK.createChatSDK) {
// const toolId = typeof window !== 'undefined' ? sessionStorage.getItem('currentToolId') : null
// console.log('[SDK] toolId:', toolId)
// const config = {
// code: import.meta.env.VITE_SDK_CODE || 'default_code',
// channel: import.meta.env.VITE_SDK_CHANNEL || 'default_channel',
// toolId,
// mock: import.meta.env.VITE_SDK_MOCK === 'true',
// apiBaseUrl: import.meta.env.VITE_SDK_API_BASE_URL,
// }
// try {
// console.error('[SDK] 初始化')
// } catch (error) {
// console.error('[SDK] 初始化失败:', error)
// }
// resolve()
// return true
// }
return false
}
if (checkAndInitSDK()) {
return
}
const script = document.createElement('script')
script.src = '/sdream-ai/sdk/index.js'
script.type = 'text/javascript'
script.onload = () => {
checkAndInitSDK()
}
script.onerror = (event) => {
console.error('[SDK] ChatSDK 脚本加载失败:', event)
console.warn('[SDK] 将使用原生实现')
resolve()
}
document.body.appendChild(script)
})
}
async function initApp() {
await loadSDK()
if (rootEl) {
const root = ReactDOM.createRoot(rootEl)
root.render(
<HeroUIProvider className="h-full">
<App />
</HeroUIProvider>,
)
}
}
initApp()
......@@ -13,7 +13,8 @@ import { trimSpacesOnly } from './components/ChatItem/markdownFormatter'
import { ChatEditor } from '@/components/ChatEditor'
import type { Answer, ChatRecord, OriginalRecord } from '@/types/chat'
import { fetchSessionConversationId, fetchUserQaRecordPage } from '@/api/conversation'
import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat'
import { fetchCheckTokenApi } from '@/api/chat'
import { fetchStreamResponse } from '@/api/sdkChat'
import { clearCurrentToolId, clearShouldSendQuestion, exitBusinessInsightSubWorkflow, fetchConversations, setBusinessInsightMainWorkflow, setBusinessInsightPendingQuestion, setBusinessInsightSubWorkflow, setCurrentToolId, updateBusinessInsightParamMap } from '@/store/conversationSlice'
import { getUserRolesForApi, safeSessionStorageGetItem, waitForToken } from '@/lib/utils'
import { findToolRowBySessionToolId, getFirstToolListRow, readToolListFromSessionStorage, resolveSubmitQuestionToolPair } from '@/lib/toolRoleRecordTypes'
......
interface ChatSDKConfig {
code: string
channel: string
toolId?: string
apiBaseUrl?: string
mock?: boolean
isDesktop?: boolean
}
interface ChatSDKInstance {
speak: (text: string) => void
stopSpeak: () => void
destroy: () => void
}
let sdkInstance: ChatSDKInstance | null = null
let sdkLoadingPromise: Promise<void> | null = null
export async function loadChatSDK(config: ChatSDKConfig): Promise<ChatSDKInstance | null> {
if (sdkInstance) {
return sdkInstance
}
if (sdkLoadingPromise) {
await sdkLoadingPromise
return sdkInstance
}
sdkLoadingPromise = new Promise((resolve) => {
const script = document.createElement('script')
script.src = '/sdream-ai/sdk/index.js'
script.onload = () => {
try {
const ChatSDK = (window as any).ChatSDK
if (ChatSDK && ChatSDK.createChatSDK) {
sdkInstance = ChatSDK.createChatSDK(config)
console.log('[SDK] toolId:', config.toolId)
}
}
catch (error) {
console.error('[SDK] 初始化失败:', error)
}
resolve()
}
script.onerror = () => {
console.error('[SDK] ChatSDK 脚本加载失败')
resolve()
}
document.body.appendChild(script)
})
await sdkLoadingPromise
return sdkInstance
}
export function getSDKInstance(): ChatSDKInstance | null {
return sdkInstance
}
export function destroySDK(): void {
if (sdkInstance) {
sdkInstance.destroy()
sdkInstance = null
}
sdkLoadingPromise = null
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment