feat: 标准化 Agent AG-UI 与审批运行时

- 新增 AG-UI 事件投影与协议编码模块

- 支持 Turn 级审批作用域和受信任动态审批策略
This commit is contained in:
2026-08-19 21:50:58 +08:00
parent a34ca9271e
commit 49a7de34bb
14 changed files with 1043 additions and 13 deletions

View File

@@ -0,0 +1,90 @@
package com.easyagents.agui;
import com.easyagents.agent.runtime.event.AgentRuntimeEvent;
import com.easyagents.agent.runtime.event.AgentRuntimeEventType;
import io.agentscope.core.agui.event.AguiEvent;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* {@link AguiRuntimeEventProjector} 的协议顺序与终态测试。
*/
public class AguiRuntimeEventProjectorTest {
/**
* 验证文本、推理、工具和成功终态按 AG-UI 顺序投影。
*/
@Test
public void shouldProjectSuccessfulRunInProtocolOrder() {
AguiRuntimeEventProjector projector = new AguiRuntimeEventProjector("thread-1", "run-1");
List<Object> events = new ArrayList<>();
events.addAll(projector.project(event(AgentRuntimeEventType.STARTED, null, null, Map.of())));
events.addAll(projector.project(event(
AgentRuntimeEventType.REASONING_STARTED, "reasoning-1", null, Map.of())));
events.addAll(projector.project(event(
AgentRuntimeEventType.REASONING_DELTA, "reasoning-1", null, Map.of("reasoning", "分析"))));
events.addAll(projector.project(event(
AgentRuntimeEventType.REASONING_COMPLETED, "reasoning-1", null, Map.of())));
events.addAll(projector.project(event(
AgentRuntimeEventType.MESSAGE_DELTA, "message-1", null, Map.of("text", "你好"))));
events.addAll(projector.project(event(
AgentRuntimeEventType.TOOL_CALL, null, "tool-1",
Map.of("toolName", "search", "input", Map.of("q", "AG-UI")))));
events.addAll(projector.project(event(
AgentRuntimeEventType.TOOL_RESULT, null, "tool-1", Map.of("text", "done"))));
events.addAll(projector.project(event(AgentRuntimeEventType.COMPLETED, null, null, Map.of())));
Assert.assertEquals(List.of(
"RunStarted",
"ReasoningMessageStart",
"ReasoningMessageContent",
"ReasoningMessageEnd",
"TextMessageStart",
"TextMessageContent",
"ToolCallStart",
"ToolCallArgs",
"ToolCallEnd",
"ToolCallResult",
"TextMessageEnd",
"RunFinished"), events.stream().map(value -> value.getClass().getSimpleName()).toList());
Assert.assertEquals(
"reasoning", ((AguiEvent.ReasoningMessageStart) events.get(1)).role());
Assert.assertTrue(projector.isTerminated());
}
/**
* 验证失败终态不会追加成功事件,终态后的迟到事件会被丢弃。
*/
@Test
public void shouldEmitSingleErrorTerminalAndIgnoreLateEvents() {
AguiRuntimeEventProjector projector = new AguiRuntimeEventProjector("thread-1", "run-1");
List<Object> failed = projector.project(event(
AgentRuntimeEventType.FAILED, null, null, Map.of("message", "boom")));
List<Object> late = projector.project(event(AgentRuntimeEventType.COMPLETED, null, null, Map.of()));
Assert.assertEquals(2, failed.size());
Assert.assertTrue(failed.get(0) instanceof AguiEvent.RunStarted);
Assert.assertEquals(
new AguiExtendedEvent.RunError("thread-1", "run-1", "boom", "AGENT_RUNTIME_FAILED"),
failed.get(1));
Assert.assertTrue(late.isEmpty());
}
private static AgentRuntimeEvent event(
AgentRuntimeEventType type,
String messageId,
String toolCallId,
Map<String, Object> payload) {
AgentRuntimeEvent event = AgentRuntimeEvent.of(type);
event.setMessageId(messageId);
event.setToolCallId(toolCallId);
event.setPayload(payload);
return event;
}
}