coze-dev / coze-dev/coze-js

Bug: @coze/uniapp-api 0.3.1版本流式请求返回内容过长时,流式请求没结束,流式接口直接返回 空 error了

Open
#333 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
292
Forks
99
PR merge metrics
No merged PRs in 30d

Description

在uni-app项目中使用 @coze/uniapp-api 0.3.1 版本,编译成微信小程序
遇到的问题:
流式接口返回内容时间过长时,接口直接 走了下面方法中的 catch, error 内容为空。但是我这边测试发现,内容还是在返回中的,并且再次进入页面,这条内容能完整的呈现出来。

期望行为:
1、增加流式接口响应的时长;
2、能否返回真正的流式接口底层错误码。

流式接口相关代码:
import { RoleType, ChatStatus, ChatEventType } from '@coze/api';
import { CozeAPI } from '@coze/uniapp-api';
import { parseJwt, getTokenFromStore } from "@/utils/jwt";

interface StreamChatOptions {
onMessage?: (content: string, data?: any) => void
onError?: (error: any) => void
onComplete?: (data: any) => void
onStatusChange?: (status: ChatStatus) => void
additionalMessages?: any[]
onEmptyError?: () => void
onChatInProgress?: (data: { conversationId: string; chatId: string }) => void
}

// Token 管理(简化版,仅保留 streamChat 依赖的部分)
async function getCozeToken(): Promise {
// 此处保留 token 获取逻辑,streamChat 依赖此函数
// 实际实现参考原文件的 tokenInfo.isTokenExpired()
return '';
}

// 按需创建带最新 token 的 CozeAPI 客户端
export async function getCozeClient(): Promise {
const token = await getCozeToken();
return new CozeAPI({
token,
baseURL: COZE_CN_BASE_URL,
allowPersonalAccessTokenInBrowser: import.meta.env.NODE_ENV !== 'production'
});
}

// ==================== 流式聊天核心接口 ====================

// 流式请求聊天
async function streamChat(
message: string,
conversation_id: string | null = null,
parameters: Record | null = null,
options: StreamChatOptions = {}
) {
const botId = import.meta.env.VITE_COZE_BOT_ID;
if (!botId) {
const error = { msg: '缺少配置: cozeBotId', code: -1 };
console.error('[Chat Config Error]', error);
throw error;
}

const {
onMessage = (content: string, _data?: any) => console.log(content),
onError = (error: { msg: string; code?: number }) => console.error('聊天错误:', error),
onComplete = (data: any) => console.log('聊天完成', data),
onStatusChange = (status: ChatStatus) => console.log('聊天状态变化:', status),
additionalMessages = [],
onEmptyError,
onChatInProgress
} = options;

try {
const client = await getCozeClient();

const messages = [
{ role: RoleType.User, content: message, content_type: 'text' },
...additionalMessages
];

const obj: any = {
bot_id: botId,
additional_messages: messages,
auto_save_history: true
};

if (conversation_id) obj.conversation_id = conversation_id;
if (parameters) obj.parameters = parameters;

const payload = parseJwt(getTokenFromStore());

if (payload && payload.sub) {
obj.user_id = payload.sub;
}

console.log('[Chat Request]', {
bot_id: botId,
conversation_id: conversation_id || 'new',
user_id: obj.user_id || 'none',
message_length: message.length,
has_parameters: !!parameters
});

const stream = await client.chat.stream(obj);

let isCompleted = false;
let receivedTerminalEvent = false;

for await (const part of stream) {
console.log('[Chat Stream]', part);
if (!part?.event) {
console.error('[Chat Stream Protocol Error]', part);
throw part || new Error('流式响应格式异常');
}

if (part.event === ChatEventType.CONVERSATION_MESSAGE_DELTA) {
const deltaContent = typeof part?.data?.content === 'string' ? part.data.content : '';
if (deltaContent !== '') {
onMessage(deltaContent);
}
} else if (part.event === ChatEventType.CONVERSATION_CHAT_IN_PROGRESS) {
console.log('[Chat In Progress]', part.data);
onChatInProgress?.({
conversationId: part.data?.conversation_id,
chatId: part.data?.id
});
} else if (part.event === ChatEventType.CONVERSATION_CHAT_COMPLETED) {
receivedTerminalEvent = true;
console.log('[Chat Completed]', {
conversation_id: part.data?.conversation_id
});
onStatusChange?.(ChatStatus.COMPLETED);
onComplete?.(part.data);
isCompleted = true;
} else if (part.event === ChatEventType.CONVERSATION_CHAT_FAILED) {
receivedTerminalEvent = true;
console.error('[Chat Failed]', part.data);
throw part.data;
} else if (part.event === ChatEventType.ERROR) {
receivedTerminalEvent = true;
console.error('[Chat Stream Error]', part.data);
throw part.data;
} else if (part.event === ChatEventType.DONE) {
receivedTerminalEvent = true;
console.log('[Chat Stream Done]');
if (!isCompleted) {
throw new Error('流式响应异常结束');
}
}
}

// 检查是否收到终止事件
if (!receivedTerminalEvent) {
throw new Error('流式响应异常结束:未收到终止事件');
}
} catch (error: any) {
console.log('[Chat Error]', error);

// 检测空 Error:可能是流式请求网络中断,但服务端可能已保存数据
const isEmptyError = error instanceof Error &&
(!error.message || error.message.trim() === '');

if (isEmptyError) {
console.warn('[Chat Stream] 检测到空错误,触发空错误处理流程');
onEmptyError?.();
return;
}

console.error('[Chat Error]', error);
onStatusChange?.(ChatStatus.FAILED);
onError?.(error);
throw error;
}
}

Contributor guide

Open the contributing guide

Research direction

Start at the client.chat.stream entry point in @coze/uniapp-api and trace its WeChat mini-program streaming and error handling; the payload names no repository file or test. Reproduce a long streaming response, then verify that the request completes and exposes the underlying error code rather than an empty error.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.