diff --git a/http/api/assistant.js b/http/api/assistant.js index 56bb9d4..03e37bc 100644 --- a/http/api/assistant.js +++ b/http/api/assistant.js @@ -1,65 +1,213 @@ /** - * AI助手相关API接口模块 + * 宠物助手相关API接口模块 + * 对接后端宠物助手服务接口 */ import BaseRequest from '../utils/request-helper.js' import { LOADING_TEXTS } from '../config/constants.js' +// ==================== 宠物助手聊天接口 ==================== + /** - * 发送消息给AI助手 + * 向宠物助手提问(非流式) + * @description 发送消息给宠物助手,获取专业的宠物护理建议 + * @param {Object} messageData 消息数据对象 + * @param {string} messageData.message 用户消息内容,必填,1-2000字符 + * @param {string} [messageData.sessionId] 会话ID,可选,UUID格式 + * @param {boolean} [messageData.stream=false] 是否流式响应 + * @param {number} [messageData.temperature=0.7] 温度参数,控制随机性,范围0-2 + * @param {number} [messageData.maxTokens=1000] 最大生成token数,范围1-4000 + * @param {string} [messageData.model] 模型名称,可选 + * @param {Object} [config={}] 自定义请求配置 + * @returns {Promise} 返回聊天响应对象 + * @example + * // 基本用法 + * const response = await askPetAssistant({ + * message: '我的狗狗不吃饭怎么办?' + * }) + * + * // 指定会话和参数 + * const response = await askPetAssistant({ + * message: '还有其他建议吗?', + * sessionId: 'uuid-session-id', + * temperature: 0.8, + * maxTokens: 1500 + * }) + */ +export const askPetAssistant = (messageData, config = {}) => { + return BaseRequest.post('/pet/user/assistant/ask', messageData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.AI_THINKING, config) +} + +/** + * 向宠物助手流式提问 + * @description 发送消息给宠物助手,获取流式响应(实时显示回复过程) + * @param {Object} messageData 消息数据对象 + * @param {string} messageData.message 用户消息内容 + * @param {string} [messageData.sessionId] 会话ID,可选 + * @param {number} [messageData.temperature=0.7] 温度参数 + * @param {number} [messageData.maxTokens=1000] 最大token数 + * @param {Object} [config={}] 自定义请求配置 + * @returns {Promise} 返回流式响应对象 + * @example + * const response = await streamAskPetAssistant({ + * message: '我的猫咪呕吐是什么原因?', + * sessionId: 'uuid-session-id' + * }) + */ +export const streamAskPetAssistant = (messageData, config = {}) => { + // 强制设置为流式响应 + const streamData = { ...messageData, stream: true } + return BaseRequest.post('/pet/user/assistant/stream-ask', streamData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.AI_THINKING, config) +} + +/** + * 获取宠物助手对话历史 + * @description 获取用户与宠物助手的对话历史记录(单一会话) + * @param {Object} [params={}] 查询参数 + * @param {number} [params.page=1] 页码,默认第1页 + * @param {number} [params.pageSize=20] 每页数量,默认20条 + * @param {Object} [config={}] 自定义请求配置 + * @returns {Promise} 返回历史记录列表 + * @example + * // 获取历史记录 + * const history = await getAssistantHistory({ + * page: 1, + * pageSize: 50 + * }) + */ +export const getAssistantHistory = (params = {}, config = {}) => { + return BaseRequest.get('/pet/user/assistant/history', params, 'AUTHENTICATED_QUERY', config) +} + +/** + * 清空宠物助手对话历史 + * @description 清空用户与宠物助手的对话历史记录 + * @param {Object} [config={}] 自定义请求配置 + * @returns {Promise} 返回操作结果 + * @example + * // 清空历史记录 + * await clearAssistantHistory() + */ +export const clearAssistantHistory = (config = {}) => { + return BaseRequest.delete('/pet/user/assistant/clear-history', {}, 'AUTHENTICATED_DELETE', '正在清除历史记录...', config) +} + + + +// ==================== 兼容性接口(保持向后兼容) ==================== + +/** + * 发送消息给AI助手(兼容旧版本) + * @deprecated 请使用 askPetAssistant 替代 * @param {Object} messageData 消息数据 * @param {Object} config 自定义配置 * @returns {Promise} */ export const sendMessage = (messageData, config = {}) => { - return BaseRequest.post('/ai/chat', messageData, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.AI_THINKING, config) + console.warn('sendMessage is deprecated, please use askPetAssistant instead') + return askPetAssistant(messageData, config) } /** - * 获取AI知识库 - * @param {Object} params 查询参数 - * @param {Object} config 自定义配置 - * @returns {Promise} - */ -export const getKnowledgeBase = (params = {}, config = {}) => { - return BaseRequest.get('/ai/knowledge', params, 'SILENT_REQUEST', config) -} - -/** - * 获取聊天历史 + * 获取聊天历史(兼容旧版本) + * @deprecated 请使用 getAssistantHistory 替代 * @param {Object} params 查询参数 * @param {Object} config 自定义配置 * @returns {Promise} */ export const getChatHistory = (params = {}, config = {}) => { - return BaseRequest.get('/ai/chat/history', params, 'AUTHENTICATED_QUERY', config) + console.warn('getChatHistory is deprecated, please use getAssistantHistory instead') + return getAssistantHistory(params, config) } /** - * 清除聊天历史 + * 清除聊天历史(兼容旧版本) + * @deprecated 请使用 clearAssistantHistory 替代 * @param {Object} config 自定义配置 * @returns {Promise} */ export const clearChatHistory = (config = {}) => { - return BaseRequest.delete('/ai/chat/history', {}, 'AUTHENTICATED_DELETE', '正在清除历史记录...', config) + console.warn('clearChatHistory is deprecated, please use clearAssistantHistory instead') + return clearAssistantHistory({}, config) +} + +// ==================== 工具函数 ==================== + + + +/** + * 验证消息内容 + * @description 验证用户输入的消息是否符合要求 + * @param {string} message 消息内容 + * @returns {Object} 验证结果 { valid: boolean, error?: string } + */ +export const validateMessage = (message) => { + if (!message || typeof message !== 'string') { + return { valid: false, error: '消息内容不能为空' } + } + + const trimmedMessage = message.trim() + if (trimmedMessage.length === 0) { + return { valid: false, error: '消息内容不能为空' } + } + + if (trimmedMessage.length > 2000) { + return { valid: false, error: '消息内容不能超过2000个字符' } + } + + return { valid: true } } /** - * 获取AI建议 - * @param {Object} petData 宠物数据 - * @param {Object} config 自定义配置 - * @returns {Promise} + * 格式化聊天消息 + * @description 将API返回的消息格式化为前端显示格式 + * @param {Object} apiMessage API返回的消息对象 + * @returns {Object} 格式化后的消息对象 */ -export const getAISuggestion = (petData, config = {}) => { - return BaseRequest.post('/ai/suggestion', petData, 'AUTHENTICATED_UPDATE', '正在分析宠物状况...', config) +export const formatChatMessage = (apiMessage) => { + return { + id: apiMessage.id, + type: apiMessage.role === 'user' ? 'user' : 'ai', + content: apiMessage.messageContent || apiMessage.message || '', + time: formatMessageTime(apiMessage.createdAt || apiMessage.created_at), + sessionId: apiMessage.sessionId, + isSensitive: apiMessage.isSensitive || false, + tokenCount: apiMessage.tokenCount || 0, + responseTime: apiMessage.responseTime || 0 + } } /** - * 语音转文字 - * @param {Object} audioData 音频数据 - * @param {Object} config 自定义配置 - * @returns {Promise} + * 格式化消息时间 + * @description 将时间戳或时间字符串格式化为显示格式 + * @param {string|number|Date} timestamp 时间戳或时间对象 + * @returns {string} 格式化后的时间字符串 */ -export const speechToText = (audioData, config = {}) => { - return BaseRequest.upload('/ai/speech-to-text', audioData, LOADING_TEXTS.SPEECH_RECOGNITION, config) +export const formatMessageTime = (timestamp) => { + if (!timestamp) return '' + + const date = new Date(timestamp) + const now = new Date() + const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()) + const messageDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()) + + const timeStr = date.toLocaleTimeString('zh-CN', { + hour: '2-digit', + minute: '2-digit', + hour12: false + }) + + if (messageDate.getTime() === today.getTime()) { + return timeStr + } else if (messageDate.getTime() === today.getTime() - 24 * 60 * 60 * 1000) { + return `昨天 ${timeStr}` + } else { + return date.toLocaleDateString('zh-CN', { + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + hour12: false + }) + } } diff --git a/pages/assistant/assistant.vue b/pages/assistant/assistant.vue index 90c4268..edea443 100644 --- a/pages/assistant/assistant.vue +++ b/pages/assistant/assistant.vue @@ -15,8 +15,13 @@ 在线 - - 📚 + + + 🗑️ + + + 📚 + @@ -114,6 +119,17 @@ @@ -398,18 +527,30 @@ export default { } } - .knowledge-icon { - width: 60rpx; - height: 60rpx; - border-radius: 30rpx; - background: rgba(255, 255, 255, 0.8); + .action-buttons { display: flex; align-items: center; - justify-content: center; - box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1); + gap: 16rpx; - .icon-text { - font-size: 28rpx; + .action-btn { + width: 56rpx; + height: 56rpx; + border-radius: 28rpx; + background: rgba(255, 255, 255, 0.8); + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; + + &:active { + transform: scale(0.95); + background: rgba(255, 255, 255, 0.9); + } + + .icon-text { + font-size: 24rpx; + } } } } diff --git a/utils/sse-test.js b/utils/sse-test.js new file mode 100644 index 0000000..e092f4d --- /dev/null +++ b/utils/sse-test.js @@ -0,0 +1,141 @@ +/** + * SSE工具类测试文件 + * 用于验证SSE功能是否正常工作 + */ + +import { createSSERequest, createPetAssistantSSE, SSE_STATUS } from './sse.js' + +/** + * 测试基础SSE功能 + */ +export function testBasicSSE() { + console.log('开始测试基础SSE功能...') + + const testData = { + message: '测试消息', + temperature: 0.7, + maxTokens: 100 + } + + let receivedChunks = 0 + let totalContent = '' + + return createSSERequest({ + url: `${uni.$u.http.config.baseURL}/pet/user/assistant/stream-ask`, + data: testData, + onChunk: (chunk) => { + receivedChunks++ + totalContent = chunk.totalContent + console.log(`接收到第${receivedChunks}个数据块:`, chunk.content) + }, + onComplete: (result) => { + console.log('SSE响应完成:', { + totalChunks: receivedChunks, + totalContent: totalContent, + metadata: result.data + }) + }, + onError: (error) => { + console.error('SSE测试失败:', error) + } + }) +} + +/** + * 测试宠物助手专用SSE + */ +export function testPetAssistantSSE() { + console.log('开始测试宠物助手SSE功能...') + + const testData = { + message: '你好,请介绍一下自己', + temperature: 0.7, + maxTokens: 200 + } + + let chunkCount = 0 + + return createPetAssistantSSE(testData, { + onChunk: (chunk) => { + chunkCount++ + console.log(`宠物助手SSE - 第${chunkCount}块:`, chunk.content) + }, + onComplete: (result) => { + console.log('宠物助手SSE完成:', { + chunks: chunkCount, + message: result.data.message, + isSensitive: result.data.isSensitive, + tokenCount: result.data.tokenCount + }) + }, + onError: (error) => { + console.error('宠物助手SSE失败:', error) + } + }) +} + +/** + * 运行所有测试 + */ +export async function runAllTests() { + console.log('=== 开始SSE工具类测试 ===') + + try { + // 测试基础SSE + console.log('\n1. 测试基础SSE功能') + await testBasicSSE() + + // 等待一秒 + await new Promise(resolve => setTimeout(resolve, 1000)) + + // 测试宠物助手SSE + console.log('\n2. 测试宠物助手SSE功能') + await testPetAssistantSSE() + + console.log('\n=== SSE工具类测试完成 ===') + return true + + } catch (error) { + console.error('SSE测试过程中发生错误:', error) + return false + } +} + +/** + * 简单的功能验证 + */ +export function validateSSEFunctions() { + console.log('验证SSE工具类函数是否存在...') + + const checks = [ + { name: 'createSSERequest', func: createSSERequest }, + { name: 'createPetAssistantSSE', func: createPetAssistantSSE }, + { name: 'SSE_STATUS', obj: SSE_STATUS } + ] + + let allValid = true + + checks.forEach(check => { + if (typeof check.func === 'function' || typeof check.obj === 'object') { + console.log(`✅ ${check.name} - 正常`) + } else { + console.error(`❌ ${check.name} - 缺失或类型错误`) + allValid = false + } + }) + + if (allValid) { + console.log('✅ 所有SSE工具类函数验证通过') + } else { + console.error('❌ SSE工具类函数验证失败') + } + + return allValid +} + +export default { + testBasicSSE, + testPetAssistantSSE, + runAllTests, + validateSSEFunctions +} diff --git a/utils/sse-usage.md b/utils/sse-usage.md new file mode 100644 index 0000000..57003d1 --- /dev/null +++ b/utils/sse-usage.md @@ -0,0 +1,216 @@ +# SSE工具类使用指南 + +## 概述 + +SSE (Server-Sent Events) 工具类提供了通用的流式响应处理功能,支持实时数据流处理、回调机制和错误处理。 + +## 主要功能 + +### 1. 基础SSE请求 - `createSSERequest()` + +最灵活的底层API,支持完全自定义配置。 + +```javascript +import { createSSERequest } from '@/utils/sse.js' + +const response = await createSSERequest({ + url: 'https://api.example.com/stream', + method: 'POST', + data: { message: '你好' }, + headers: { 'Custom-Header': 'value' }, + onChunk: (chunk) => { + console.log('接收到数据:', chunk.content) + console.log('累积内容:', chunk.totalContent) + }, + onComplete: (result) => { + console.log('流式响应完成:', result.data) + }, + onError: (error) => { + console.error('发生错误:', error) + }, + doneMarker: '[DONE]' // 自定义结束标记 +}) +``` + +### 2. 默认配置SSE请求 - `createDefaultSSERequest()` + +带有默认配置的便捷API。 + +```javascript +import { createDefaultSSERequest } from '@/utils/sse.js' + +const response = await createDefaultSSERequest( + '/api/chat/stream', // API端点 + { message: '你好' }, // 请求数据 + { + onChunk: (chunk) => { + // 处理数据块 + updateUI(chunk.content) + }, + onComplete: (result) => { + // 处理完成 + console.log('完成:', result.data.message) + } + } +) +``` + +### 3. 宠物助手专用SSE - `createPetAssistantSSE()` + +专门为宠物助手功能设计的API。 + +```javascript +import { createPetAssistantSSE } from '@/utils/sse.js' + +const response = await createPetAssistantSSE( + { + message: '我的宠物生病了怎么办?', + temperature: 0.7, + maxTokens: 1000 + }, + { + onChunk: (chunk) => { + // 实时更新AI回复 + aiMessage.content = chunk.totalContent + scrollToBottom() + }, + onComplete: (result) => { + // 更新元数据 + aiMessage.isSensitive = result.data.isSensitive + aiMessage.tokenCount = result.data.tokenCount + } + } +) +``` + +### 4. 可取消的SSE请求 - `createCancellableSSE()` + +支持取消操作的SSE请求。 + +```javascript +import { createCancellableSSE, SSE_STATUS } from '@/utils/sse.js' + +const { promise, cancel, getStatus } = createCancellableSSE({ + url: '/api/stream', + data: { message: '长时间处理的请求' }, + onChunk: (chunk) => { + console.log('状态:', getStatus()) // 'streaming' + updateContent(chunk.content) + } +}) + +// 5秒后取消请求 +setTimeout(() => { + if (getStatus() === SSE_STATUS.STREAMING) { + cancel() + console.log('请求已取消') + } +}, 5000) + +try { + const result = await promise + console.log('请求完成:', result) +} catch (error) { + console.log('请求被取消或失败:', error) +} +``` + +## 回调函数参数 + +### onChunk(chunk) +- `chunk.content` - 当前数据块内容 +- `chunk.totalContent` - 累积的完整内容 +- `chunk.chunk` - 原始解析的数据对象 + +### onComplete(result) +- `result.data.message` - 完整的响应消息 +- `result.data.isSensitive` - 是否包含敏感内容 +- `result.data.tokenCount` - 使用的token数量 +- `result.data.responseTime` - 响应时间 + +### onError(error) +- 网络错误、解析错误或其他异常 + +## 状态枚举 + +```javascript +import { SSE_STATUS } from '@/utils/sse.js' + +SSE_STATUS.PENDING // 等待中 +SSE_STATUS.STREAMING // 流式传输中 +SSE_STATUS.COMPLETED // 已完成 +SSE_STATUS.ERROR // 发生错误 +SSE_STATUS.CANCELLED // 已取消 +``` + +## 在Vue组件中使用 + +```javascript +// 在组件中导入 +import { createPetAssistantSSE } from '@/utils/sse.js' + +export default { + methods: { + async sendMessage() { + // 创建消息对象 + const aiMessage = { + type: 'ai', + content: '', + time: this.getCurrentTime() + } + this.messageList.push(aiMessage) + + // 发起SSE请求 + try { + const result = await createPetAssistantSSE( + { message: this.inputMessage }, + { + onChunk: (chunk) => { + // 实时更新内容 + aiMessage.content = chunk.totalContent + this.$nextTick(() => { + this.scrollToBottom() + }) + }, + onComplete: (result) => { + // 更新元数据 + Object.assign(aiMessage, result.data) + }, + onError: (error) => { + console.error('SSE失败:', error) + // 可以在这里实现降级处理 + } + } + ) + } catch (error) { + // 处理异常 + this.handleError(error) + } + } + } +} +``` + +## 注意事项 + +1. **自动认证**: 工具类会自动添加Authorization头部 +2. **错误处理**: 建议同时实现onError回调和try-catch +3. **内存管理**: 长时间的流式响应注意内存使用 +4. **网络状态**: 考虑网络中断的情况 +5. **降级处理**: SSE失败时可以降级到普通API请求 + +## 扩展使用 + +可以基于基础API创建更多专用函数: + +```javascript +// 创建客服聊天SSE +export function createCustomerServiceSSE(data, callbacks) { + return createDefaultSSERequest('/api/customer/stream', data, callbacks) +} + +// 创建代码生成SSE +export function createCodeGenerationSSE(data, callbacks) { + return createDefaultSSERequest('/api/code/generate', data, callbacks) +} +``` diff --git a/utils/sse.js b/utils/sse.js new file mode 100644 index 0000000..19457ef --- /dev/null +++ b/utils/sse.js @@ -0,0 +1,258 @@ +/** + * SSE (Server-Sent Events) 流式响应工具类 + * 提供通用的SSE数据流处理功能 + */ + +/** + * 创建SSE流式请求 + * @param {Object} options 配置选项 + * @param {string} options.url 请求URL + * @param {string} options.method 请求方法,默认POST + * @param {Object} options.data 请求数据 + * @param {Object} options.headers 请求头,会自动添加SSE相关头部 + * @param {Function} options.onChunk 接收到数据块时的回调 (chunk) => {} + * @param {Function} options.onComplete 流式响应完成时的回调 (result) => {} + * @param {Function} options.onError 发生错误时的回调 (error) => {} + * @param {string} options.doneMarker 结束标记,默认为 '[DONE]' + * @returns {Promise} 返回Promise,resolve时返回完整结果 + */ +export function createSSERequest(options) { + const { + url, + method = 'POST', + data = {}, + headers = {}, + onChunk, + onComplete, + onError, + doneMarker = '[DONE]' + } = options + + return new Promise((resolve, reject) => { + // 构建完整的请求头 + const requestHeaders = { + 'Content-Type': 'application/json', + 'Accept': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Authorization': `Bearer ${uni.getStorageSync('token') || ''}`, + ...headers + } + + // 累积的响应数据 + let accumulatedData = { + content: '', + metadata: {} + } + + // 发起uni.request请求 + const requestTask = uni.request({ + url, + method, + data, + header: requestHeaders, + responseType: 'text', + success: (res) => { + try { + // 处理SSE数据流 + const lines = res.data.split('\n') + + for (const line of lines) { + if (line.startsWith('data: ')) { + const data = line.substring(6).trim() + + // 检查是否为结束标记 + if (data === doneMarker) { + // 流式响应结束 + const result = { + data: { + message: accumulatedData.content, + ...accumulatedData.metadata + } + } + + // 调用完成回调 + if (onComplete) { + onComplete(result) + } + + resolve(result) + return + } + + try { + const parsed = JSON.parse(data) + + // 处理内容数据 + if (parsed.content) { + accumulatedData.content += parsed.content + + // 调用数据块回调 + if (onChunk) { + onChunk({ + content: parsed.content, + totalContent: accumulatedData.content, + chunk: parsed + }) + } + } + + // 更新元数据 + if (parsed.isSensitive !== undefined) { + accumulatedData.metadata.isSensitive = parsed.isSensitive + } + if (parsed.tokenCount !== undefined) { + accumulatedData.metadata.tokenCount = parsed.tokenCount + } + if (parsed.responseTime !== undefined) { + accumulatedData.metadata.responseTime = parsed.responseTime + } + + // 保存其他元数据 + Object.keys(parsed).forEach(key => { + if (!['content', 'isSensitive', 'tokenCount', 'responseTime'].includes(key)) { + accumulatedData.metadata[key] = parsed[key] + } + }) + + } catch (parseError) { + console.warn('解析SSE数据失败:', parseError, data) + // 继续处理其他数据,不中断流程 + } + } + } + + // 如果没有收到结束标记,也认为完成 + const result = { + data: { + message: accumulatedData.content, + ...accumulatedData.metadata + } + } + + if (onComplete) { + onComplete(result) + } + + resolve(result) + + } catch (error) { + console.error('处理SSE响应失败:', error) + + if (onError) { + onError(error) + } + + reject(error) + } + }, + fail: (error) => { + console.error('SSE请求失败:', error) + + if (onError) { + onError(error) + } + + reject(error) + } + }) + + // 返回请求任务,允许外部取消请求 + return requestTask + }) +} + +/** + * 创建带有默认配置的SSE请求 + * @param {string} endpoint API端点路径 + * @param {Object} data 请求数据 + * @param {Object} callbacks 回调函数 {onChunk, onComplete, onError} + * @returns {Promise} SSE请求Promise + */ +export function createDefaultSSERequest(endpoint, data, callbacks = {}) { + const baseURL = uni.$u?.http?.config?.baseURL || '' + + return createSSERequest({ + url: `${baseURL}${endpoint}`, + method: 'POST', + data, + ...callbacks + }) +} + +/** + * 专用于宠物助手的SSE请求 + * @param {Object} data 请求数据 + * @param {Object} callbacks 回调函数 + * @returns {Promise} SSE请求Promise + */ +export function createPetAssistantSSE(data, callbacks = {}) { + return createDefaultSSERequest('/pet/user/assistant/stream-ask', data, callbacks) +} + +/** + * SSE请求状态枚举 + */ +export const SSE_STATUS = { + PENDING: 'pending', + STREAMING: 'streaming', + COMPLETED: 'completed', + ERROR: 'error', + CANCELLED: 'cancelled' +} + +/** + * 创建可取消的SSE请求 + * @param {Object} options 配置选项 + * @returns {Object} 返回包含promise和cancel方法的对象 + */ +export function createCancellableSSE(options) { + let requestTask = null + let status = SSE_STATUS.PENDING + + const promise = new Promise((resolve, reject) => { + const enhancedOptions = { + ...options, + onChunk: (chunk) => { + status = SSE_STATUS.STREAMING + if (options.onChunk) { + options.onChunk(chunk) + } + }, + onComplete: (result) => { + status = SSE_STATUS.COMPLETED + if (options.onComplete) { + options.onComplete(result) + } + resolve(result) + }, + onError: (error) => { + status = SSE_STATUS.ERROR + if (options.onError) { + options.onError(error) + } + reject(error) + } + } + + requestTask = createSSERequest(enhancedOptions) + }) + + return { + promise, + cancel: () => { + if (requestTask && status === SSE_STATUS.STREAMING) { + requestTask.abort?.() + status = SSE_STATUS.CANCELLED + } + }, + getStatus: () => status + } +} + +export default { + createSSERequest, + createDefaultSSERequest, + createPetAssistantSSE, + createCancellableSSE, + SSE_STATUS +}