The-Agency/lib/src/session/conversation_history.dart

132 lines
3.7 KiB
Dart

import "dart:convert";
import "session_types.dart";
// in-memory conversation history for the current session
// does not auto-persist - caller has to call SessionStore.saveSession
class ConversationHistory {
ConversationHistory({ConversationSession? session}) : _session = session;
ConversationSession? _session;
ConversationSession? get session => _session;
bool get hasSession => _session != null;
List<Message> getMessages() {
return _session?.messages ?? <Message>[];
}
void setSession(ConversationSession s) {
_session = s;
}
void addMessage(String role, String content, {int? tokens, int? contextTokens, List<MessageAttachment>? attachments, double? cost}) {
if (_session == null) return;
final msg = Message(role: role, content: content, tokens: tokens, contextTokens: contextTokens, attachments: attachments, cost: cost);
_session!.messages.add(msg);
_session!.updated = DateTime.now().toUtc();
}
void appendToLastMessage(String text) {
if (_session == null || _session!.messages.isEmpty || text.isEmpty) {
return;
}
final lastMessage = _session!.messages.last;
_session!.messages[_session!.messages.length - 1] = Message(
role: lastMessage.role,
content: "${lastMessage.content}$text",
timestamp: lastMessage.timestamp,
tokens: lastMessage.tokens,
contextTokens: lastMessage.contextTokens,
);
_session!.updated = DateTime.now().toUtc();
}
void setLastMessageContextTokens(int? contextTokens) {
if (_session == null || _session!.messages.isEmpty) return;
final last = _session!.messages.last;
_session!.messages[_session!.messages.length - 1] = Message(
role: last.role,
content: last.content,
timestamp: last.timestamp,
tokens: last.tokens,
contextTokens: contextTokens ?? last.contextTokens,
cost: last.cost,
);
}
void setLastMessageCost(double? cost) {
if (_session == null || _session!.messages.isEmpty) return;
final last = _session!.messages.last;
_session!.messages[_session!.messages.length - 1] = Message(
role: last.role,
content: last.content,
timestamp: last.timestamp,
tokens: last.tokens,
contextTokens: last.contextTokens,
cost: cost,
);
}
void removeLastMessage() {
if (_session == null || _session!.messages.isEmpty) {
return;
}
_session!.messages.removeLast();
_session!.updated = DateTime.now().toUtc();
}
void truncateMessages(int length) {
if (_session == null) {
return;
}
final targetLength = length.clamp(0, _session!.messages.length);
_session!.messages.removeRange(targetLength, _session!.messages.length);
_session!.updated = DateTime.now().toUtc();
}
// remove all messages but keep the session metadata
void clear() {
if (_session == null) return;
_session!.messages.clear();
_session!.updated = DateTime.now().toUtc();
}
// export as plain text - one [role]: content block per message
String exportToText() {
final buf = StringBuffer();
if (_session != null) {
buf.writeln("Session: ${_session!.name}");
buf.writeln("ID: ${_session!.id}");
buf.writeln("Created: ${_session!.created.toIso8601String()}");
buf.writeln("Messages: ${_session!.messageCount}");
buf.writeln("---");
buf.writeln();
}
for (final msg in getMessages()) {
buf.writeln("[${msg.role}]");
buf.writeln(msg.content);
buf.writeln();
}
return buf.toString();
}
String exportToJson() {
if (_session == null) {
return const JsonEncoder.withIndent(
" ",
).convert(<String, dynamic>{"messages": <dynamic>[]});
}
return const JsonEncoder.withIndent(" ").convert(_session!.toJson());
}
}