fix: streaming 2

This commit is contained in:
cogwheel0
2025-09-01 17:34:05 +05:30
parent 856bb8b75a
commit 754ad6d444
2 changed files with 34 additions and 9 deletions

View File

@@ -2649,7 +2649,9 @@ class ApiService {
// Add only essential parameters // Add only essential parameters
if (conversationId != null) { if (conversationId != null) {
data['chat_id'] = conversationId; if (conversationId != null) {
data['chat_id'] = conversationId;
}
} }
// Add feature flags if enabled // Add feature flags if enabled
@@ -2729,14 +2731,26 @@ class ApiService {
debugPrint('DEBUG: session_id value: ${data['session_id']}'); debugPrint('DEBUG: session_id value: ${data['session_id']}');
debugPrint('DEBUG: id value: ${data['id']}'); debugPrint('DEBUG: id value: ${data['id']}');
// If tools are requested, use background task flow to allow server-side execution. // Decide whether to use background task flow.
// Open WebUI executes tools and continues the response outside of the // Only enable background task mode when we actually need socket/dynamic-channel
// provider SSE. That path requires background task mode (session_id + id + chat_id). // behavior (e.g., provider-native tools or explicit background tasks with a session).
if (conversationId != null) { final bool useBackgroundTasks = (
// Use background flow only for provider-native tools or explicit tool servers.
// Pure text generation should prefer SSE even if a socket session exists,
// and background_tasks can still be honored at the end of SSE.
(toolIds != null && toolIds.isNotEmpty) ||
(toolServers != null && toolServers.isNotEmpty)
);
// Use background flow only when required; otherwise prefer SSE even with chat_id.
// SSE must not include session_id/id to avoid server falling back to task mode.
if (useBackgroundTasks) {
// Attach identifiers to trigger background task processing on the server // Attach identifiers to trigger background task processing on the server
data['session_id'] = sessionId; data['session_id'] = sessionId;
data['id'] = messageId; data['id'] = messageId;
data['chat_id'] = conversationId; if (conversationId != null) {
data['chat_id'] = conversationId;
}
// Attach background_tasks if provided // Attach background_tasks if provided
if (backgroundTasks != null && backgroundTasks.isNotEmpty) { if (backgroundTasks != null && backgroundTasks.isNotEmpty) {
@@ -2773,7 +2787,10 @@ class ApiService {
} }
}(); }();
} else { } else {
// Use SSE streaming with proper parser // Ensure we do not leak background-only identifiers into SSE path
data.remove('session_id');
data.remove('id');
// Use SSE streaming with proper parser (chat_id allowed)
_streamSSE(data, streamController, messageId); _streamSSE(data, streamController, messageId);
} }

View File

@@ -1073,7 +1073,13 @@ Future<void> _sendMessageInternal(
// In that case, do NOT suppress socket content. // In that case, do NOT suppress socket content.
// Suppress socket TEXT content when we already have a stream (SSE or polling) // Suppress socket TEXT content when we already have a stream (SSE or polling)
// but DO allow tool_call status via socket to surface tiles immediately. // but DO allow tool_call status via socket to surface tiles immediately.
bool suppressSocketContent = (socketSessionId == null); // text-only suppression // By default we already have an SSE/polling stream for content,
// so suppress socket TEXT chunks to avoid duplicates. We'll still
// surface tool_calls status via socket immediately. If the server
// switches us to a dynamic channel (request:chat:completion), we
// keep suppressing chat-events text but stream from that channel.
bool suppressSocketContent = true; // text-only suppression by default
bool usingDynamicChannel = false; // set true when server provides a channel
if (socketService != null) { if (socketService != null) {
void chatHandler(Map<String, dynamic> ev) { void chatHandler(Map<String, dynamic> ev) {
try { try {
@@ -1281,6 +1287,7 @@ Future<void> _sendMessageInternal(
if (channel is String && channel.isNotEmpty) { if (channel is String && channel.isNotEmpty) {
// Prefer dynamic channel for streaming content; suppress chat-events text to avoid duplicates // Prefer dynamic channel for streaming content; suppress chat-events text to avoid duplicates
suppressSocketContent = true; suppressSocketContent = true;
usingDynamicChannel = true;
if (kSocketVerboseLogging) { if (kSocketVerboseLogging) {
DebugLogger.stream('Socket request:chat:completion channel=$channel'); DebugLogger.stream('Socket request:chat:completion channel=$channel');
} }
@@ -1720,7 +1727,8 @@ Future<void> _sendMessageInternal(
suppressSocketContent = false; suppressSocketContent = false;
// If this path was SSE-driven (no background socket), finish now. // If this path was SSE-driven (no background socket), finish now.
// Otherwise keep streaming state until socket/dynamic channel signals done. // Otherwise keep streaming state until socket/dynamic channel signals done.
if (socketService == null) { // We can safely finish on SSE completion when not using a dynamic channel.
if (!usingDynamicChannel) {
ref.read(chatMessagesProvider.notifier).finishStreaming(); ref.read(chatMessagesProvider.notifier).finishStreaming();
} }