Files
sure/mobile/lib/models/message.dart
Juan José Mata ad3087f1dd Improvements to Flutter client (#1042)
* Chat improvements

* Delete/reset account via API for Flutter app

* Fix tests.

* Add "contact us" to settings

* Update mobile/lib/screens/chat_conversation_screen.dart

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Juan José Mata <jjmata@jjmata.com>

* Improve LLM special token detection

* Deactivated user shouldn't have API working

* Fix tests

* API-Key usage

* Flutter app launch failure on no network

* Handle deletion/reset delays

* Local cached data may become stale

* Use X-Api-Key correctly!

---------

Signed-off-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-22 21:22:32 -05:00

86 lines
2.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'tool_call.dart';
class Message {
/// Known LLM special tokens that may leak into responses (strip from display).
/// Includes ASCII ChatML (<|...|>) and DeepSeek full-width variants (<...>).
static const _llmTokenPatterns = [
'<|start_of_sentence|>',
'<|im_start|>',
'<|im_end|>',
'<|endoftext|>',
'</s>',
// DeepSeek full-width pipe variants (U+FF5C )
'<\uFF5Cstart_of_sentence\uFF5C>',
'<\uFF5Cim_start\uFF5C>',
'<\uFF5Cim_end\uFF5C>',
'<\uFF5Cendoftext\uFF5C>',
];
/// Removes LLM tokens and trims trailing whitespace from assistant content.
static String sanitizeContent(String content) {
var out = content;
for (final token in _llmTokenPatterns) {
out = out.replaceAll(token, '');
}
out = out.replaceAll(RegExp(r'<\|[^|]*\|>'), '');
out = out.replaceAll(RegExp('<\u{FF5C}[^\u{FF5C}]*\u{FF5C}>'), '');
return out.trim();
}
final String id;
final String type;
final String role;
final String content;
final String? model;
final DateTime createdAt;
final DateTime updatedAt;
final List<ToolCall>? toolCalls;
Message({
required this.id,
required this.type,
required this.role,
required this.content,
this.model,
required this.createdAt,
required this.updatedAt,
this.toolCalls,
});
factory Message.fromJson(Map<String, dynamic> json) {
final rawContent = json['content'] as String;
final role = json['role'] as String;
final content = role == 'assistant' ? sanitizeContent(rawContent) : rawContent;
return Message(
id: json['id'].toString(),
type: json['type'] as String,
role: role,
content: content,
model: json['model'] as String?,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
toolCalls: json['tool_calls'] != null
? (json['tool_calls'] as List)
.map((tc) => ToolCall.fromJson(tc as Map<String, dynamic>))
.toList()
: null,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'type': type,
'role': role,
'content': content,
'model': model,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
'tool_calls': toolCalls?.map((tc) => tc.toJson()).toList(),
};
}
bool get isUser => role == 'user';
bool get isAssistant => role == 'assistant';
}