first commit

This commit is contained in:
zch1234qq
2025-10-16 21:24:18 +08:00
commit b9d03d05cc
35 changed files with 10191 additions and 0 deletions

2
app/service/api.ts Normal file
View File

@ -0,0 +1,2 @@
// Coze API服务文件
// 注意:测试函数和未使用的接口已移除,以避免类型冲突问题

259
app/service/cozeService.ts Normal file
View File

@ -0,0 +1,259 @@
import type { Message } from '../types/types';
import { CozeAPI } from '@coze/api';
// 定义Coze事件数据接口
interface DeltaEventData {
content?: string;
delta?: { content?: string };
}
interface CompletedEventData {
conversation_id?: string;
usage?: { token_count?: number };
token_count?: number;
messages?: { content?: string }[];
}
// 错误事件数据接口 - 备用
// interface ErrorEventData {
// code?: string;
// message?: string;
// type?: string;
// }
class CozeService {
private apiKey: string | null = null;
private botId: string | null = null;
private userId: string = `user_${Date.now()}`; // 生成一个临时用户ID
private apiClient: CozeAPI | null = null;
private conversationId: string | null = null; // 存储对话ID用于后续调用
constructor() {
// 初始化服务实际使用时需要设置API密钥和Bot ID
}
/**
* 设置API密钥和Bot ID
* @param apiKey Coze API密钥
* @param botId Bot ID
*/
setCredentials(apiKey: string, botId: string): void {
this.apiKey = apiKey;
this.botId = botId;
// 初始化Coze API客户端 - 严格按照官方示例实现
this.apiClient = new CozeAPI({
token: apiKey,
baseURL: 'https://api.coze.cn'
});
console.log('Coze API client initialized with official SDK');
}
/**
* 发送消息给Coze agent
* 使用官方SDK的stream方法实现流式响应
* 严格按照api.ts示例实现使用for await...of循环处理流式响应
* @param message 用户消息内容
* @param onStream 流式响应回调函数
* @returns Promise<Message> 完整的响应消息
*/
async sendMessage(
message: string,
onStream?: (partialText: string, isDone: boolean) => void
): Promise<Message> {
// 检查初始化状态
if (!this.isInitialized()) {
console.warn('Coze服务尚未初始化或凭据不完整。使用模拟回复。');
return this.getMockResponse(message);
}
try {
console.log('正在使用官方SDK发送消息到Coze服务...');
console.log('Bot ID:', this.botId);
console.log('User ID:', this.userId);
// 严格按照api.ts示例调用chat.stream方法
const res = await this.apiClient!.chat.stream({
bot_id: this.botId!,
user_id: this.userId,
conversation_id: this.conversationId || undefined, // 如果有保存的对话ID则使用
additional_messages: [
{
"content": message,
"content_type": "text",
"role": "user",
"type": "question"
}
] as any // eslint-disable-line @typescript-eslint/no-explicit-any,
});
let fullContent = '';
const messageId = Date.now().toString();
console.log('开始处理Coze流式响应...');
// 严格按照api.ts的实现方式使用for await...of循环处理流式响应
for await (const event of res) {
try {
console.log('接收到Coze事件:', event.event || 'unknown');
// 1. 处理消息增量事件
if (event.event === 'conversation.message.delta') {
// 根据TypeScript类型定义content可能直接在data上
const deltaData = event.data as DeltaEventData;
if (deltaData && deltaData.content) {
const deltaContent = deltaData.content;
fullContent += deltaContent;
console.log('增量内容:', fullContent.substring(0, 100) + '...');
// 通知流式更新
if (onStream) {
onStream(fullContent, false);
}
} else if (deltaData && deltaData.delta && deltaData.delta.content) {
// 兼容另一种可能的数据结构
const deltaContent = deltaData.delta.content;
fullContent += deltaContent;
console.log('增量内容(delta路径):', fullContent.substring(0, 100) + '...');
if (onStream) {
onStream(fullContent, false);
}
}
}
// 2. 处理聊天完成事件
if (event.event === 'conversation.chat.completed') {
console.log(); // 输出换行
// 尝试从data中获取conversation_id和usage信息
const dataObj = event.data as CompletedEventData;
// 打印conversation_id并保存
if (dataObj && dataObj.conversation_id) {
console.log('Conversation ID:', dataObj.conversation_id);
this.conversationId = dataObj.conversation_id; // 保存对话ID供下次使用
}
// 打印token使用量
if (dataObj && dataObj.usage && dataObj.usage.token_count) {
console.log('token usage:', dataObj.usage.token_count);
} else if (dataObj && dataObj.token_count) {
console.log('token usage:', dataObj.token_count);
}
// 尝试从data中获取完整内容如果有的话
if (dataObj && dataObj.messages && dataObj.messages[0] && dataObj.messages[0].content) {
fullContent = dataObj.messages[0].content || '';
console.log('使用完成事件中的完整内容');
}
}
// 3. 处理错误事件
if (event.event === 'error') {
console.error('错误事件:', event.data);
throw new Error(`Coze服务错误: ${JSON.stringify(event.data)}`);
}
} catch (err) {
console.error('处理事件时出错:', err);
console.log('错误事件完整数据:', JSON.stringify(event, null, 2));
}
}
// 流式处理完成,检查是否有内容
console.log('Coze流式响应处理完成总内容长度:', fullContent.length);
if (fullContent.trim()) {
const botMessage: Message = {
id: messageId,
content: fullContent,
sender: 'bot',
timestamp: new Date()
};
// 通知流式更新完成
if (onStream) {
onStream(fullContent, true);
}
return botMessage;
} else {
throw new Error('Coze服务返回空内容请检查API配置和网络连接');
}
} catch (error) {
console.error('Error sending message to Coze with official SDK:', error);
// 获取具体的错误信息
const errorMsg = error instanceof Error ? error.message : '未知错误';
// 即使出错也返回模拟回复
return this.getMockResponse(message, errorMsg);
}
}
/**
* 获取模拟回复当Coze API调用失败时使用
*/
private getMockResponse(message: string, error?: string): Message {
let mockContent = '';
// 根据用户消息内容生成更有针对性的模拟回复
if (message.includes('你好') || message.includes('hello') || message.includes('hi')) {
mockContent = '你好!我是你的爆款文案策划师。为了给你提供更有针对性的方案,请告诉我:\n1. 你的行业/产品/服务\n2. 你的目标用户群体\n有了这些信息我就能帮你打造更精准的文案了';
} else if (message.includes('文案') || message.includes('内容') || message.includes('写作')) {
mockContent = '关于文案创作,我有一些专业建议:\n1. 明确目标受众和核心卖点\n2. 使用吸引人的标题和开头\n3. 突出产品/服务的独特价值\n4. 加入社会证明(案例、数据)\n5. 包含明确的行动号召\n需要针对特定行业的文案模板吗';
} else if (message.includes('价格') || message.includes('费用') || message.includes('多少钱')) {
mockContent = '文案服务的价格因需求复杂度而异。我们提供:\n- 基础文案套餐¥500-1000\n- 营销策划套餐¥1000-3000\n- 品牌全案套餐¥3000+\n欢迎告诉我你的具体需求我可以为你提供更精准的报价。';
} else {
mockContent = '感谢你的提问!作为文案策划师,我可以帮你:\n- 撰写吸引人的营销文案\n- 设计爆款标题\n- 优化产品描述\n- 制定内容营销策略\n请告诉我你具体需要哪方面的帮助';
}
// 如果有错误信息,可以选择性地在开头添加提示
if (error) {
console.log('使用模拟回复,原因:', error);
// 不直接显示错误,只显示友好提示
mockContent = '【提示:当前使用的是智能助手的推荐回复】\n\n' + mockContent;
}
return {
id: Date.now().toString(),
content: mockContent,
sender: 'bot',
timestamp: new Date()
};
}
/**
* 重置对话状态
*/
resetConversation(): void {
this.conversationId = null; // 清除保存的对话ID
console.log('Conversation reset');
}
/**
* 检查客户端是否已初始化
*/
isInitialized(): boolean {
return !!this.apiClient && !!this.apiKey && !!this.botId;
}
/**
* 设置用户ID
*/
setUserId(userId: string): void {
this.userId = userId;
}
/**
* 获取用户ID
*/
getUserId(): string {
return this.userId;
}
}
// 导出单例实例
// 创建并导出单例实例
const cozeServiceInstance = new CozeService();
export default cozeServiceInstance;

View File

@ -0,0 +1,85 @@
import cozeService from './cozeService';
import type { CozeConfig } from '../types/types';
/**
* 初始化Coze服务
* 这个函数应该在客户端组件中调用因为API密钥需要在客户端安全管理
*
* @param config Coze配置信息包含API密钥和Bot ID
* @returns 是否初始化成功
*/
export const initializeCoze = (config: CozeConfig): boolean => {
try {
// 验证配置
if (!config.apiKey || !config.botId) {
console.error('Missing Coze API key or Bot ID');
return false;
}
// 设置凭证并初始化客户端
cozeService.setCredentials(config.apiKey, config.botId);
console.log('Coze service initialized successfully');
return true;
} catch (error) {
console.error('Failed to initialize Coze service:', error);
return false;
}
};
/**
* 从环境变量或localStorage加载Coze配置
* 在实际应用中建议使用更安全的方式存储API密钥
*/
export const loadCozeConfig = (): CozeConfig | null => {
try {
// 在实际应用中,你可能会从环境变量或安全存储中获取这些值
// 这里我们从localStorage中读取作为示例
const storedConfig = localStorage.getItem('cozeConfig');
if (storedConfig) {
return JSON.parse(storedConfig);
}
return null;
} catch (error) {
console.error('Failed to load Coze configuration:', error);
return null;
}
};
/**
* 保存Coze配置到localStorage
* 在实际应用中建议使用更安全的方式存储API密钥
*/
export const saveCozeConfig = (config: CozeConfig): boolean => {
try {
localStorage.setItem('cozeConfig', JSON.stringify(config));
return true;
} catch (error) {
console.error('Failed to save Coze configuration:', error);
return false;
}
};
/**
* 获取已初始化的Coze服务实例
* @returns CozeService实例
*/
export function getCozeService() {
return cozeService;
}
/**
* 检查Coze服务是否已初始化
* @returns boolean 是否已初始化
*/
export function isCozeInitialized(): boolean {
return cozeService.isInitialized();
}
/**
* 重置Coze对话
*/
export function resetCozeConversation(): void {
cozeService.resetConversation();
}