import type { RecommendationResponse, TaskStatus, ConversationSummary, Conversation, ConversationMessage } from './types' // 智能检测环境:生产环境使用相对路径(前后端同域),开发环境使用localhost const BASE_URL = import.meta.env.VITE_API_BASE_URL || (import.meta.env.PROD ? '' : 'http://localhost:8000') // 处理用户请求的统一接口 - 融合了意图识别、偏好提取、确认流程 // 这个接口会自动处理: // - 使用 GPT-4 进行意图识别 // - 如果是推荐餐厅请求:触发推荐流程 // - 如果是普通对话:返回 GPT-4 的回复 export async function recommend( query: string, userId: string = "default", conversationHistory?: Array<{ role: string; content: string }>, conversationId?: string, useOnlineAgent: boolean = false ): Promise { const url = `${BASE_URL}/api/process` try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, user_id: userId, conversation_history: conversationHistory, conversation_id: conversationId, use_online_agent: useOnlineAgent }), }) if (!res.ok) { const text = await res.text().catch(() => '') let errorMessage = `HTTP ${res.status} ${res.statusText}` try { const errorData = JSON.parse(text) errorMessage += `: ${errorData.detail || text}` } catch { errorMessage += `: ${text || 'Unknown error'}` } console.error('[API] recommend error:', { status: res.status, statusText: res.statusText, error: errorMessage, query, useOnlineAgent }) throw new Error(errorMessage) } const response = (await res.json()) as RecommendationResponse console.log('[API] recommend response:', { hasLlmReply: !!response.llm_reply, hasConfirmationRequest: !!response.confirmation_request, hasThinkingSteps: !!response.thinking_steps, thinkingStepsCount: response.thinking_steps?.length || 0, hasRestaurants: !!response.restaurants, restaurantsCount: response.restaurants?.length || 0, intent: response.intent, preferences: response.preferences, fullResponse: response }) return response } catch (error: any) { // 处理网络错误(如连接失败、CORS等) if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(`Network error: Cannot connect to backend at ${BASE_URL}. Please ensure the backend server is running.`) } throw error } } // 流式处理用户请求(用于逐字显示回复) export async function recommendStream( query: string, userId: string = "default", conversationHistory?: Array<{ role: string; content: string }>, onChunk?: (chunk: string) => void, onComplete?: (fullText: string) => void, useOnlineAgent: boolean = false ): Promise { const url = `${BASE_URL}/api/process/stream` return new Promise((resolve, reject) => { try { fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, user_id: userId, conversation_history: conversationHistory, use_online_agent: useOnlineAgent }), }) .then(async (res) => { if (!res.ok) { const text = await res.text().catch(() => '') throw new Error(`HTTP ${res.status} ${res.statusText}: ${text}`) } const reader = res.body?.getReader() const decoder = new TextDecoder() let fullText = '' if (!reader) { throw new Error('Response body is not readable') } while (true) { const { done, value } = await reader.read() if (done) { if (onComplete) { onComplete(fullText) } resolve(fullText) break } const chunk = decoder.decode(value, { stream: true }) const lines = chunk.split('\n') for (const line of lines) { if (line.startsWith('data: ')) { try { const data = JSON.parse(line.slice(6)) if (data.error) { reject(new Error(data.content)) return } if (data.content) { fullText += data.content if (onChunk) { onChunk(data.content) } } if (data.done) { if (onComplete) { onComplete(fullText) } resolve(fullText) return } } catch (e) { // 忽略 JSON 解析错误 } } } } }) .catch(reject) } catch (error: any) { if (error instanceof TypeError && error.message.includes('fetch')) { reject(new Error(`Network error: Cannot connect to backend at ${BASE_URL}`)) } else { reject(error) } } }) } // 获取任务状态 export async function getTaskStatus( taskId: string, userId?: string, conversationId?: string ): Promise { // 构建查询参数 const params = new URLSearchParams() if (userId) params.append('user_id', userId) if (conversationId) params.append('conversation_id', conversationId) const queryString = params.toString() const url = queryString ? `${BASE_URL}/api/status/${taskId}?${queryString}` : `${BASE_URL}/api/status/${taskId}` try { const res = await fetch(url) if (!res.ok) { const text = await res.text().catch(() => '') let errorMessage = `HTTP ${res.status} ${res.statusText}` try { const errorData = JSON.parse(text) errorMessage += `: ${errorData.detail || text}` } catch { errorMessage += `: ${text || 'Unknown error'}` } console.error('[API] getTaskStatus error:', { taskId, status: res.status, error: errorMessage }) throw new Error(errorMessage) } const status = (await res.json()) as TaskStatus console.log('[API] getTaskStatus response:', { taskId, status: status.status, progress: status.progress, message: status.message, hasResult: !!status.result, hasError: !!status.error, resultRestaurantsCount: status.result?.restaurants?.length || 0, resultThinkingStepsCount: status.result?.thinking_steps?.length || 0, fullStatus: status }) return status } catch (error: any) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(`Network error: Cannot connect to backend at ${BASE_URL}`) } throw error } } // 健康检查 - 用于测试后端连接 export async function healthCheck(): Promise<{ status: string; timestamp: string }> { const url = `${BASE_URL}/health` try { const res = await fetch(url) if (!res.ok) { throw new Error(`Health check failed: ${res.status} ${res.statusText}`) } return (await res.json()) as { status: string; timestamp: string } } catch (error: any) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(`Cannot connect to backend at ${BASE_URL}. Please ensure the backend server is running on port 8000.`) } throw error } } // 更新偏好设置 export async function updatePreferences(preferences: Record): Promise<{ preferences: Record }> { const url = `${BASE_URL}/api/update-preferences` const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(preferences), }) if (!res.ok) { const text = await res.text().catch(() => '') throw new Error(`HTTP ${res.status} ${res.statusText} ${text}`) } return (await res.json()) as { preferences: Record } } // 获取用户偏好设置 export async function getUserPreferences(userId: string = "default"): Promise<{ preferences: Record }> { const url = `${BASE_URL}/api/user-preferences/${userId}` const res = await fetch(url) if (!res.ok) { const text = await res.text().catch(() => '') throw new Error(`HTTP ${res.status} ${res.statusText} ${text}`) } return (await res.json()) as { preferences: Record } } // 获取对话的偏好设置 export async function getConversationPreferences( userId: string, conversationId: string ): Promise<{ preferences: Record }> { const url = `${BASE_URL}/api/conversations/${userId}/${conversationId}/preferences` const res = await fetch(url) if (!res.ok) { const text = await res.text().catch(() => '') throw new Error(`HTTP ${res.status} ${res.statusText} ${text}`) } return (await res.json()) as { preferences: Record } } // 更新对话的偏好设置 export async function updateConversationPreferences( userId: string, conversationId: string, preferences: Record ): Promise<{ preferences: Record }> { const url = `${BASE_URL}/api/conversations/${userId}/${conversationId}/preferences` const res = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(preferences), }) if (!res.ok) { const text = await res.text().catch(() => '') throw new Error(`HTTP ${res.status} ${res.statusText} ${text}`) } return (await res.json()) as { preferences: Record } } // ==================== 对话历史API ==================== // 获取用户的所有对话列表 export async function getConversations(userId: string): Promise { const url = `${BASE_URL}/api/conversations/${userId}` try { const res = await fetch(url) if (!res.ok) { const text = await res.text().catch(() => '') let errorMessage = `HTTP ${res.status} ${res.statusText}` try { const errorData = JSON.parse(text) errorMessage += `: ${errorData.detail || text}` } catch { errorMessage += `: ${text || 'Unknown error'}` } throw new Error(errorMessage) } return (await res.json()) as ConversationSummary[] } catch (error: any) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(`Network error: Cannot connect to backend at ${BASE_URL}`) } throw error } } // 获取单个对话的完整信息 export async function getConversation(userId: string, conversationId: string): Promise { const url = `${BASE_URL}/api/conversations/${userId}/${conversationId}` try { const res = await fetch(url) if (!res.ok) { const text = await res.text().catch(() => '') let errorMessage = `HTTP ${res.status} ${res.statusText}` try { const errorData = JSON.parse(text) errorMessage += `: ${errorData.detail || text}` } catch { errorMessage += `: ${text || 'Unknown error'}` } throw new Error(errorMessage) } return (await res.json()) as Conversation } catch (error: any) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(`Network error: Cannot connect to backend at ${BASE_URL}`) } throw error } } // 创建新对话 export async function createConversation( userId: string, options?: { title?: string; model?: string } ): Promise { const url = `${BASE_URL}/api/conversations/${userId}` try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: options?.title, model: options?.model || 'RestRec', }), }) if (!res.ok) { const text = await res.text().catch(() => '') let errorMessage = `HTTP ${res.status} ${res.statusText}` try { const errorData = JSON.parse(text) errorMessage += `: ${errorData.detail || text}` } catch { errorMessage += `: ${text || 'Unknown error'}` } throw new Error(errorMessage) } return (await res.json()) as Conversation } catch (error: any) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(`Network error: Cannot connect to backend at ${BASE_URL}`) } throw error } } // 更新对话信息 export async function updateConversation( userId: string, conversationId: string, updates: { title?: string; model?: string } ): Promise { const url = `${BASE_URL}/api/conversations/${userId}/${conversationId}` try { const res = await fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates), }) if (!res.ok) { const text = await res.text().catch(() => '') let errorMessage = `HTTP ${res.status} ${res.statusText}` try { const errorData = JSON.parse(text) errorMessage += `: ${errorData.detail || text}` } catch { errorMessage += `: ${text || 'Unknown error'}` } throw new Error(errorMessage) } return (await res.json()) as Conversation } catch (error: any) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(`Network error: Cannot connect to backend at ${BASE_URL}`) } throw error } } // 向对话添加消息 export async function addMessage( userId: string, conversationId: string, role: 'user' | 'assistant', content: string, metadata?: Record ): Promise<{ success: boolean; message: string }> { const url = `${BASE_URL}/api/conversations/${userId}/${conversationId}/messages` try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ role, content, metadata, }), }) if (!res.ok) { const text = await res.text().catch(() => '') let errorMessage = `HTTP ${res.status} ${res.statusText}` try { const errorData = JSON.parse(text) errorMessage += `: ${errorData.detail || text}` } catch { errorMessage += `: ${text || 'Unknown error'}` } throw new Error(errorMessage) } return (await res.json()) as { success: boolean; message: string } } catch (error: any) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(`Network error: Cannot connect to backend at ${BASE_URL}`) } throw error } } // 删除对话 export async function deleteConversation( userId: string, conversationId: string ): Promise<{ success: boolean; message: string }> { const url = `${BASE_URL}/api/conversations/${userId}/${conversationId}` try { const res = await fetch(url, { method: 'DELETE', }) if (!res.ok) { const text = await res.text().catch(() => '') let errorMessage = `HTTP ${res.status} ${res.statusText}` try { const errorData = JSON.parse(text) errorMessage += `: ${errorData.detail || text}` } catch { errorMessage += `: ${text || 'Unknown error'}` } throw new Error(errorMessage) } return (await res.json()) as { success: boolean; message: string } } catch (error: any) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error(`Network error: Cannot connect to backend at ${BASE_URL}`) } throw error } }