fix: streaming 2
This commit is contained in:
@@ -2648,9 +2648,11 @@ class ApiService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Add only essential parameters
|
// Add only essential parameters
|
||||||
|
if (conversationId != null) {
|
||||||
if (conversationId != null) {
|
if (conversationId != null) {
|
||||||
data['chat_id'] = conversationId;
|
data['chat_id'] = conversationId;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add feature flags if enabled
|
// Add feature flags if enabled
|
||||||
if (enableWebSearch) {
|
if (enableWebSearch) {
|
||||||
@@ -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;
|
||||||
|
if (conversationId != null) {
|
||||||
data['chat_id'] = conversationId;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user