Files
social-app/apps/lib/features/chat/data/models/ag_ui_event.dart
T

399 lines
12 KiB
Dart

class AgUiEventTypeWire {
static const runStarted = 'RUN_STARTED';
static const runFinished = 'RUN_FINISHED';
static const runError = 'RUN_ERROR';
static const stepStarted = 'STEP_STARTED';
static const stepFinished = 'STEP_FINISHED';
static const textMessageEnd = 'TEXT_MESSAGE_END';
static const toolCallStart = 'TOOL_CALL_START';
static const toolCallArgs = 'TOOL_CALL_ARGS';
static const toolCallEnd = 'TOOL_CALL_END';
static const toolCallResult = 'TOOL_CALL_RESULT';
static const toolCallError = 'TOOL_CALL_ERROR';
}
enum AgUiEventType {
runStarted,
runFinished,
runError,
stepStarted,
stepFinished,
textMessageEnd,
toolCallStart,
toolCallArgs,
toolCallEnd,
toolCallResult,
toolCallError,
unknown,
}
const _wireToTypeMap = {
AgUiEventTypeWire.runStarted: AgUiEventType.runStarted,
AgUiEventTypeWire.runFinished: AgUiEventType.runFinished,
AgUiEventTypeWire.runError: AgUiEventType.runError,
AgUiEventTypeWire.stepStarted: AgUiEventType.stepStarted,
AgUiEventTypeWire.stepFinished: AgUiEventType.stepFinished,
AgUiEventTypeWire.textMessageEnd: AgUiEventType.textMessageEnd,
AgUiEventTypeWire.toolCallStart: AgUiEventType.toolCallStart,
AgUiEventTypeWire.toolCallArgs: AgUiEventType.toolCallArgs,
AgUiEventTypeWire.toolCallEnd: AgUiEventType.toolCallEnd,
AgUiEventTypeWire.toolCallResult: AgUiEventType.toolCallResult,
AgUiEventTypeWire.toolCallError: AgUiEventType.toolCallError,
};
const _typeToWireMap = {
AgUiEventType.runStarted: AgUiEventTypeWire.runStarted,
AgUiEventType.runFinished: AgUiEventTypeWire.runFinished,
AgUiEventType.runError: AgUiEventTypeWire.runError,
AgUiEventType.stepStarted: AgUiEventTypeWire.stepStarted,
AgUiEventType.stepFinished: AgUiEventTypeWire.stepFinished,
AgUiEventType.textMessageEnd: AgUiEventTypeWire.textMessageEnd,
AgUiEventType.toolCallStart: AgUiEventTypeWire.toolCallStart,
AgUiEventType.toolCallArgs: AgUiEventTypeWire.toolCallArgs,
AgUiEventType.toolCallEnd: AgUiEventTypeWire.toolCallEnd,
AgUiEventType.toolCallResult: AgUiEventTypeWire.toolCallResult,
AgUiEventType.toolCallError: AgUiEventTypeWire.toolCallError,
AgUiEventType.unknown: '',
};
AgUiEventType agUiEventTypeFromWire(String wire) =>
_wireToTypeMap[wire] ?? AgUiEventType.unknown;
String agUiEventTypeToWire(AgUiEventType type) => _typeToWireMap[type] ?? '';
abstract class AgUiEvent {
const AgUiEvent({required this.type});
final AgUiEventType type;
factory AgUiEvent.fromJson(Map<String, dynamic> json) {
final wireType = json['type'];
final type = wireType is String
? agUiEventTypeFromWire(wireType)
: AgUiEventType.unknown;
return switch (type) {
AgUiEventType.runStarted => RunStartedEvent.fromJson(json),
AgUiEventType.runFinished => RunFinishedEvent.fromJson(json),
AgUiEventType.runError => RunErrorEvent.fromJson(json),
AgUiEventType.stepStarted => StepStartedEvent.fromJson(json),
AgUiEventType.stepFinished => StepFinishedEvent.fromJson(json),
AgUiEventType.textMessageEnd => TextMessageEndEvent.fromJson(json),
AgUiEventType.toolCallStart => ToolCallStartEvent.fromJson(json),
AgUiEventType.toolCallArgs => ToolCallArgsEvent.fromJson(json),
AgUiEventType.toolCallEnd => ToolCallEndEvent.fromJson(json),
AgUiEventType.toolCallResult => ToolCallResultEvent.fromJson(json),
AgUiEventType.toolCallError => ToolCallErrorEvent.fromJson(json),
AgUiEventType.unknown => UnknownAgUiEvent(rawJson: json),
};
}
}
class UnknownAgUiEvent extends AgUiEvent {
const UnknownAgUiEvent({required this.rawJson})
: super(type: AgUiEventType.unknown);
final Map<String, dynamic> rawJson;
}
class RunStartedEvent extends AgUiEvent {
RunStartedEvent({required this.threadId, required this.runId})
: super(type: AgUiEventType.runStarted);
final String threadId;
final String runId;
factory RunStartedEvent.fromJson(Map<String, dynamic> json) =>
RunStartedEvent(
threadId: _asString(json['threadId']),
runId: _asString(json['runId']),
);
}
class RunFinishedEvent extends AgUiEvent {
RunFinishedEvent({required this.threadId, required this.runId})
: super(type: AgUiEventType.runFinished);
final String threadId;
final String runId;
factory RunFinishedEvent.fromJson(Map<String, dynamic> json) =>
RunFinishedEvent(
threadId: _asString(json['threadId']),
runId: _asString(json['runId']),
);
}
class RunErrorEvent extends AgUiEvent {
RunErrorEvent({required this.message, this.code})
: super(type: AgUiEventType.runError);
final String message;
final String? code;
factory RunErrorEvent.fromJson(Map<String, dynamic> json) => RunErrorEvent(
message: _asString(json['message'], fallback: 'Unknown error'),
code: json['code'] as String?,
);
}
class StepStartedEvent extends AgUiEvent {
StepStartedEvent({required this.stepName})
: super(type: AgUiEventType.stepStarted);
final String stepName;
factory StepStartedEvent.fromJson(Map<String, dynamic> json) =>
StepStartedEvent(stepName: _asString(json['stepName']));
}
class StepFinishedEvent extends AgUiEvent {
StepFinishedEvent({required this.stepName})
: super(type: AgUiEventType.stepFinished);
final String stepName;
factory StepFinishedEvent.fromJson(Map<String, dynamic> json) =>
StepFinishedEvent(stepName: _asString(json['stepName']));
}
class TextMessageEndEvent extends AgUiEvent {
TextMessageEndEvent({
required this.messageId,
required this.answer,
required this.role,
required this.status,
required this.uiSchema,
}) : super(type: AgUiEventType.textMessageEnd);
final String messageId;
final String answer;
final String role;
final String status;
final Map<String, dynamic>? uiSchema;
factory TextMessageEndEvent.fromJson(Map<String, dynamic> json) =>
TextMessageEndEvent(
messageId: _asString(json['messageId']),
answer: _asString(json['answer']),
role: _asString(json['role'], fallback: 'assistant'),
status: _asString(json['status'], fallback: 'success'),
uiSchema: _asMap(json['ui_schema']) ?? _asMap(json['uiSchema']),
);
}
class ToolCallStartEvent extends AgUiEvent {
ToolCallStartEvent({required this.toolCallId, required this.toolCallName})
: super(type: AgUiEventType.toolCallStart);
final String toolCallId;
final String toolCallName;
factory ToolCallStartEvent.fromJson(Map<String, dynamic> json) =>
ToolCallStartEvent(
toolCallId: _asString(json['toolCallId']),
toolCallName: _asString(json['toolCallName']),
);
}
class ToolCallArgsEvent extends AgUiEvent {
ToolCallArgsEvent({required this.toolCallId, required this.args})
: super(type: AgUiEventType.toolCallArgs);
final String toolCallId;
final Map<String, dynamic> args;
factory ToolCallArgsEvent.fromJson(Map<String, dynamic> json) =>
ToolCallArgsEvent(
toolCallId: _asString(json['toolCallId']),
args: _asMap(json['args']) ?? const {},
);
}
class ToolCallEndEvent extends AgUiEvent {
ToolCallEndEvent({required this.toolCallId})
: super(type: AgUiEventType.toolCallEnd);
final String toolCallId;
factory ToolCallEndEvent.fromJson(Map<String, dynamic> json) =>
ToolCallEndEvent(toolCallId: _asString(json['toolCallId']));
}
class ToolCallResultEvent extends AgUiEvent {
ToolCallResultEvent({
required this.messageId,
required this.toolCallId,
required this.toolName,
required this.resultSummary,
required this.status,
required this.uiSchema,
}) : super(type: AgUiEventType.toolCallResult);
final String messageId;
final String toolCallId;
final String toolName;
final String resultSummary;
final String status;
final Map<String, dynamic>? uiSchema;
factory ToolCallResultEvent.fromJson(Map<String, dynamic> json) =>
ToolCallResultEvent(
messageId: _asString(
json['messageId'],
fallback: 'tool-${_asString(json['tool_call_id'])}',
),
toolCallId: _asString(json['tool_call_id'] ?? json['toolCallId']),
toolName: _asString(json['tool_name'] ?? json['toolName']),
resultSummary: _asString(
json['result_summary'] ?? json['resultSummary'],
),
status: _asString(json['status'], fallback: 'success'),
uiSchema: _asMap(json['ui_schema']) ?? _asMap(json['uiSchema']),
);
}
class ToolCallErrorEvent extends AgUiEvent {
ToolCallErrorEvent({required this.toolCallId, required this.error, this.code})
: super(type: AgUiEventType.toolCallError);
final String toolCallId;
final String error;
final String? code;
factory ToolCallErrorEvent.fromJson(Map<String, dynamic> json) =>
ToolCallErrorEvent(
toolCallId: _asString(json['toolCallId']),
error: _asString(json['error'], fallback: 'Tool call failed'),
code: json['code'] as String?,
);
}
class HistorySnapshot {
const HistorySnapshot({
required this.scope,
required this.threadId,
required this.day,
required this.hasMore,
required this.messages,
});
final String scope;
final String? threadId;
final String? day;
final bool hasMore;
final List<HistoryMessage> messages;
factory HistorySnapshot.fromJson(Map<String, dynamic> json) {
final rawMessages = json['messages'];
final messages = rawMessages is List
? rawMessages
.whereType<Map<String, dynamic>>()
.map(HistoryMessage.fromJson)
.toList()
: const <HistoryMessage>[];
return HistorySnapshot(
scope: _asString(json['scope'], fallback: 'history_day'),
threadId: json['threadId'] as String?,
day: json['day'] as String?,
hasMore: json['hasMore'] == true,
messages: messages,
);
}
}
class HistoryMessage {
const HistoryMessage({
required this.id,
required this.seq,
required this.role,
required this.content,
required this.timestamp,
this.attachments = const <HistoryAttachment>[],
this.uiSchema,
});
final String id;
final int seq;
final String role;
final String content;
final DateTime timestamp;
final List<HistoryAttachment> attachments;
final Map<String, dynamic>? uiSchema;
factory HistoryMessage.fromJson(Map<String, dynamic> json) => HistoryMessage(
id: _asString(json['id']),
seq: _asInt(json['seq']),
role: _asString(json['role']),
content: _asString(json['content']),
timestamp:
DateTime.tryParse(_asString(json['timestamp'])) ?? DateTime.now(),
attachments: _parseHistoryAttachments(json['attachments']),
uiSchema: _asMap(json['ui_schema']) ?? _asMap(json['uiSchema']),
);
}
class HistoryAttachment {
const HistoryAttachment({required this.url, required this.mimeType});
final String url;
final String mimeType;
factory HistoryAttachment.fromJson(Map<String, dynamic> json) {
return HistoryAttachment(
url: _asString(json['url']),
mimeType: _asString(json['mimeType']),
);
}
}
String _asString(Object? value, {String fallback = ''}) {
if (value is String) {
return value;
}
return fallback;
}
int _asInt(Object? value) {
if (value is int) {
return value;
}
if (value is double) {
return value.toInt();
}
if (value is String) {
return int.tryParse(value) ?? 0;
}
return 0;
}
Map<String, dynamic>? _asMap(Object? value) {
if (value is Map<String, dynamic>) {
return value;
}
if (value is Map) {
final result = <String, dynamic>{};
for (final entry in value.entries) {
final key = entry.key;
if (key is String) {
result[key] = entry.value;
}
}
return result;
}
return null;
}
List<HistoryAttachment> _parseHistoryAttachments(Object? value) {
if (value is! List) {
return const <HistoryAttachment>[];
}
return value
.whereType<Map<String, dynamic>>()
.map(HistoryAttachment.fromJson)
.where(
(attachment) =>
attachment.url.isNotEmpty && attachment.mimeType.isNotEmpty,
)
.toList();
}