feat: AG-UI 协议对齐与路由导航功能
- 前端: 添加 SSE 流式支持、stateSnapshot 事件、路由导航工具 - 前端: 实现工具调用审批流程,支持 pending 状态展示 - 后端: Agent 状态管理与会话持久化相关重构 - 文档: 新增 agent-agui-full-alignance 设计文档 - 测试: 补充相关单元测试和集成测试
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'tool_result.dart';
|
||||
|
||||
@@ -15,6 +17,7 @@ class AgUiEventTypeWire {
|
||||
static const toolCallEnd = 'TOOL_CALL_END';
|
||||
static const toolCallResult = 'TOOL_CALL_RESULT';
|
||||
static const toolCallError = 'TOOL_CALL_ERROR';
|
||||
static const stateSnapshot = 'STATE_SNAPSHOT';
|
||||
static const messagesSnapshot = 'MESSAGES_SNAPSHOT';
|
||||
}
|
||||
|
||||
@@ -30,6 +33,7 @@ enum AgUiEventType {
|
||||
toolCallEnd,
|
||||
toolCallResult,
|
||||
toolCallError,
|
||||
stateSnapshot,
|
||||
messagesSnapshot,
|
||||
unknown,
|
||||
}
|
||||
@@ -47,6 +51,7 @@ const _wireToTypeMap = {
|
||||
AgUiEventTypeWire.toolCallEnd: AgUiEventType.toolCallEnd,
|
||||
AgUiEventTypeWire.toolCallResult: AgUiEventType.toolCallResult,
|
||||
AgUiEventTypeWire.toolCallError: AgUiEventType.toolCallError,
|
||||
AgUiEventTypeWire.stateSnapshot: AgUiEventType.stateSnapshot,
|
||||
AgUiEventTypeWire.messagesSnapshot: AgUiEventType.messagesSnapshot,
|
||||
};
|
||||
|
||||
@@ -63,6 +68,7 @@ const _typeToWireMap = {
|
||||
AgUiEventType.toolCallEnd: AgUiEventTypeWire.toolCallEnd,
|
||||
AgUiEventType.toolCallResult: AgUiEventTypeWire.toolCallResult,
|
||||
AgUiEventType.toolCallError: AgUiEventTypeWire.toolCallError,
|
||||
AgUiEventType.stateSnapshot: AgUiEventTypeWire.stateSnapshot,
|
||||
AgUiEventType.messagesSnapshot: AgUiEventTypeWire.messagesSnapshot,
|
||||
AgUiEventType.unknown: '',
|
||||
};
|
||||
@@ -85,6 +91,7 @@ final _typeToFactory = {
|
||||
AgUiEventType.toolCallEnd: ToolCallEndEvent.fromJson,
|
||||
AgUiEventType.toolCallResult: ToolCallResultEvent.fromJson,
|
||||
AgUiEventType.toolCallError: ToolCallErrorEvent.fromJson,
|
||||
AgUiEventType.stateSnapshot: StateSnapshotEvent.fromJson,
|
||||
AgUiEventType.messagesSnapshot: MessagesSnapshotEvent.fromJson,
|
||||
AgUiEventType.unknown: UnknownAgUiEvent.fromJson,
|
||||
};
|
||||
@@ -255,25 +262,61 @@ class ToolCallEndEvent extends AgUiEvent {
|
||||
Map<String, dynamic> toJson() => _$ToolCallEndEventToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
@JsonSerializable(createFactory: false, createToJson: false)
|
||||
class ToolCallResultEvent extends AgUiEvent {
|
||||
final String messageId;
|
||||
final String toolCallId;
|
||||
final Map<String, dynamic> result;
|
||||
final UiCard? ui;
|
||||
final String content;
|
||||
|
||||
ToolCallResultEvent({
|
||||
required this.messageId,
|
||||
required this.toolCallId,
|
||||
required this.result,
|
||||
this.ui,
|
||||
required this.content,
|
||||
}) : super(type: AgUiEventType.toolCallResult);
|
||||
|
||||
factory ToolCallResultEvent.fromJson(Map<String, dynamic> json) =>
|
||||
_$ToolCallResultEventFromJson(json);
|
||||
Map<String, dynamic> get payload {
|
||||
try {
|
||||
final decoded = jsonDecode(content);
|
||||
if (decoded is Map<String, dynamic>) {
|
||||
return decoded;
|
||||
}
|
||||
} catch (_) {}
|
||||
return {'content': content};
|
||||
}
|
||||
|
||||
Map<String, dynamic> get result {
|
||||
final rawResult = payload['result'];
|
||||
if (rawResult is Map<String, dynamic>) {
|
||||
return rawResult;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
UiCard? get ui {
|
||||
final rawUi = payload['ui'];
|
||||
if (rawUi is Map<String, dynamic>) {
|
||||
return UiCard.fromJson(rawUi);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
factory ToolCallResultEvent.fromJson(Map<String, dynamic> json) {
|
||||
final rawContent = json['content'];
|
||||
final content = rawContent is String ? rawContent : '';
|
||||
return ToolCallResultEvent(
|
||||
messageId: json['messageId'] as String,
|
||||
toolCallId: json['toolCallId'] as String,
|
||||
content: content,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$ToolCallResultEventToJson(this);
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': agUiEventTypeToWire(type),
|
||||
'messageId': messageId,
|
||||
'toolCallId': toolCallId,
|
||||
'content': content,
|
||||
};
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
@@ -292,6 +335,29 @@ class ToolCallErrorEvent extends AgUiEvent {
|
||||
Map<String, dynamic> toJson() => _$ToolCallErrorEventToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable(createFactory: false, createToJson: false)
|
||||
class StateSnapshotEvent extends AgUiEvent {
|
||||
final Map<String, dynamic> snapshot;
|
||||
|
||||
StateSnapshotEvent({required this.snapshot})
|
||||
: super(type: AgUiEventType.stateSnapshot);
|
||||
|
||||
factory StateSnapshotEvent.fromJson(Map<String, dynamic> json) {
|
||||
final rawSnapshot = json['snapshot'];
|
||||
return StateSnapshotEvent(
|
||||
snapshot: rawSnapshot is Map<String, dynamic>
|
||||
? rawSnapshot
|
||||
: <String, dynamic>{},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => {
|
||||
'type': agUiEventTypeToWire(type),
|
||||
'snapshot': snapshot,
|
||||
};
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class MessagesSnapshotEvent extends AgUiEvent {
|
||||
final List<SnapshotMessage> messages;
|
||||
|
||||
@@ -121,10 +121,7 @@ ToolCallResultEvent _$ToolCallResultEventFromJson(Map<String, dynamic> json) =>
|
||||
ToolCallResultEvent(
|
||||
messageId: json['messageId'] as String,
|
||||
toolCallId: json['toolCallId'] as String,
|
||||
result: json['result'] as Map<String, dynamic>,
|
||||
ui: json['ui'] == null
|
||||
? null
|
||||
: UiCard.fromJson(json['ui'] as Map<String, dynamic>),
|
||||
content: json['content'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ToolCallResultEventToJson(
|
||||
@@ -132,8 +129,7 @@ Map<String, dynamic> _$ToolCallResultEventToJson(
|
||||
) => <String, dynamic>{
|
||||
'messageId': instance.messageId,
|
||||
'toolCallId': instance.toolCallId,
|
||||
'result': instance.result,
|
||||
'ui': instance.ui,
|
||||
'content': instance.content,
|
||||
};
|
||||
|
||||
ToolCallErrorEvent _$ToolCallErrorEventFromJson(Map<String, dynamic> json) =>
|
||||
|
||||
Reference in New Issue
Block a user