refactor: pre seed responses
This commit is contained in:
@@ -1130,7 +1130,7 @@ class ApiService {
|
||||
if (msg.role == 'assistant' && msg.model != null)
|
||||
'modelName': msg.model,
|
||||
if (msg.role == 'assistant') 'modelIdx': 0,
|
||||
if (msg.role == 'assistant') 'done': true,
|
||||
if (msg.role == 'assistant') 'done': !msg.isStreaming,
|
||||
if (msg.role == 'user' && model != null) 'models': [model],
|
||||
if (combinedFilesMap.isNotEmpty) 'files': combinedFilesMap,
|
||||
};
|
||||
@@ -1166,7 +1166,7 @@ class ApiService {
|
||||
if (msg.role == 'assistant' && msg.model != null)
|
||||
'modelName': msg.model,
|
||||
if (msg.role == 'assistant') 'modelIdx': 0,
|
||||
if (msg.role == 'assistant') 'done': true,
|
||||
if (msg.role == 'assistant') 'done': !msg.isStreaming,
|
||||
if (msg.role == 'user' && model != null) 'models': [model],
|
||||
if (combinedFilesArray.isNotEmpty) 'files': combinedFilesArray,
|
||||
});
|
||||
@@ -2652,11 +2652,14 @@ class ApiService {
|
||||
String? sessionIdOverride,
|
||||
List<Map<String, dynamic>>? toolServers,
|
||||
Map<String, dynamic>? backgroundTasks,
|
||||
String? responseMessageId,
|
||||
}) {
|
||||
final streamController = StreamController<String>();
|
||||
|
||||
// Generate unique IDs
|
||||
final messageId = const Uuid().v4();
|
||||
final messageId = (responseMessageId != null && responseMessageId.isNotEmpty)
|
||||
? responseMessageId
|
||||
: const Uuid().v4();
|
||||
final sessionId =
|
||||
(sessionIdOverride != null && sessionIdOverride.isNotEmpty)
|
||||
? sessionIdOverride
|
||||
@@ -2809,6 +2812,8 @@ class ApiService {
|
||||
// Always use task-based background flow for unified pipeline.
|
||||
// When a dynamic channel (session_id) is not provided, this method falls
|
||||
// back to polling and streams deltas to the UI.
|
||||
// Always use background task flow (matches web client) to ensure
|
||||
// server maintains correct history with pre-seeded assistant id.
|
||||
final bool useBackgroundTasks = true;
|
||||
|
||||
// Use background flow only when required; otherwise prefer SSE even with chat_id.
|
||||
|
||||
@@ -236,6 +236,71 @@ class ToolCallsParser {
|
||||
return raw.length > max ? '${raw.substring(0, max)}…' : raw;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sanitize assistant/user content before sending to the API, mirroring
|
||||
/// the web client's `processDetails` behavior:
|
||||
/// - Remove <details type="reasoning"> and <details type="code_interpreter"> blocks
|
||||
/// - Replace <details type="tool_calls" ...>...</details> blocks with the
|
||||
/// JSON-serialized `result` attribute (as a quoted string) when available;
|
||||
/// otherwise replace with an empty string.
|
||||
static String sanitizeForApi(String content) {
|
||||
if (content.isEmpty) return content;
|
||||
|
||||
// Remove blocks we never want to include in conversation context
|
||||
final removeTypes = ['reasoning', 'code_interpreter'];
|
||||
for (final t in removeTypes) {
|
||||
content = content.replaceAll(
|
||||
RegExp(
|
||||
'<details\\s+type=\"${t}\"[^>]*>[\\s\\S]*?<\\/details>',
|
||||
multiLine: true,
|
||||
dotAll: true,
|
||||
),
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
if (!content.contains('<details')) return content.trim();
|
||||
|
||||
// Replace tool_calls blocks in-order with their results
|
||||
final segs = segments(content);
|
||||
if (segs == null || segs.isEmpty) return content.trim();
|
||||
|
||||
final buf = StringBuffer();
|
||||
for (final seg in segs) {
|
||||
if (seg.isToolCall && seg.entry != null) {
|
||||
final entry = seg.entry!;
|
||||
dynamic res = entry.result;
|
||||
String out;
|
||||
if (res == null) {
|
||||
out = '';
|
||||
} else {
|
||||
try {
|
||||
out = json.encode(res);
|
||||
} catch (_) {
|
||||
out = res.toString();
|
||||
}
|
||||
}
|
||||
// Match web behavior: wrap in quotes so it's clearly a string payload
|
||||
if (out.isNotEmpty && !(out.startsWith('"') && out.endsWith('"'))) {
|
||||
out = '"$out"';
|
||||
}
|
||||
buf.write(out);
|
||||
} else {
|
||||
// Keep the raw text, but also remove any stray non-tool_calls details blocks
|
||||
final t = (seg.text ?? '').replaceAll(
|
||||
RegExp(
|
||||
r'<details(?!\s+type=\"tool_calls\")[^>]*>[\s\S]*?<\/details>',
|
||||
multiLine: true,
|
||||
dotAll: true,
|
||||
),
|
||||
'',
|
||||
);
|
||||
if (t.isNotEmpty) buf.write(t);
|
||||
}
|
||||
}
|
||||
|
||||
return buf.toString().trim();
|
||||
}
|
||||
}
|
||||
|
||||
/// Ordered piece of content: either plain text or a tool-call entry
|
||||
|
||||
Reference in New Issue
Block a user