Skip to content

feat(streaming): add semantic text output disposition events - #3013

Open
dargoner wants to merge 20 commits into
agentscope-ai:mainfrom
dargoner:codex/pr-stream-events-text-disposition
Open

feat(streaming): add semantic text output disposition events#3013
dargoner wants to merge 20 commits into
agentscope-ai:mainfrom
dargoner:codex/pr-stream-events-text-disposition

Conversation

@dargoner

@dargoner dargoner commented Sep 6, 2026

Copy link
Copy Markdown

AgentScope-Java Version

2.0.3-SNAPSHOT

Description

Background

TextDeltaEvent preserves provider-level token streaming, but it does not tell a consumer whether a text segment belongs to an intermediate model turn that will invoke tools or to the last user-visible reply. Today, adapters have to infer that intent from neighboring tool and lifecycle events. That inference is easy to implement differently across AG-UI, Agent Protocol, Web UI, and custom consumers, especially when subagents or concurrent tasks are involved.

Waiting for AgentResultEvent and then emitting the whole answer is simpler, but it removes the real-time preview that streaming consumers expect.

This PR introduces an opt-in semantic layer that keeps the existing token stream intact while classifying each completed text lifecycle:

AgentEventStreams.withTextOutputDisposition(agent.streamEvents(...))

The wrapper emits TextOutputDispositionEvent with one of two dispositions:

  • INTERMEDIATE: the referenced text belongs to a non-terminal model turn and can be presented as progress or commentary.
  • TERMINAL: the last user-visible reply candidate has completed its streaming lifecycle.

The disposition event classifies text that has already been streamed; it does not replace or delay the original TextDeltaEvent. TERMINAL is also not the authoritative answer. AgentResultEvent remains the authoritative invocation result and can reconcile the preview with the final message, structured output, or an empty result.

For a normally completed invocation, the closing order is:

AgentResultEvent
-> TextOutputDispositionEvent(TERMINAL)
-> AgentEndEvent

This ordering lets consumers keep token-level preview latency while committing only the authoritative result.

Correlation and isolation

  • replyId associates a disposition with the text lifecycle it classifies.
  • source and metadata.taskId isolate top-level, subagent, and concurrent invocations, including concurrent calls from the same source.
  • Each subscription owns independent tracking state.
  • Cancellation and error paths do not synthesize a terminal classification for an invocation that did not complete.

Compatibility

  • ReActAgent#streamEvents() keeps its existing default event sequence.
  • The semantic wrapper and adapter integrations are disabled by default.
  • Existing consumers continue receiving the original delta, result, lifecycle, and raw provider events.
  • Consumers that do not recognize TextOutputDispositionEvent can ignore it without changing their current behavior.

Adapter behavior

AG-UI can opt in with:

AguiAdapterConfig.builder()
        .textOutputDispositionEnabled(true)
        .build();

When enabled, intermediate text is exposed as a semantic custom event rather than being presented as model reasoning. On completion, the authoritative AgentResultEvent produces a standard MESSAGES_SNAPSHOT to reconcile provisional text. When disabled, the legacy AG-UI sequence and message IDs are preserved.

The managed Web flow treats text deltas as non-persistent preview updates. INTERMEDIATE can downgrade a preview to commentary, TERMINAL closes the preview lifecycle, and AgentEndEvent commits the message derived from the buffered authoritative result. Empty authoritative results can therefore remove stale preview text, and late subscribers do not receive obsolete preview frames.

Local and remote subagents preserve the same semantics. Remote transport continues using AGENT_EVENT with its JSON string payload, while local subagent forwarding now includes the authoritative AgentResultEvent before AgentEndEvent.

Why this shape

The framework is the only layer that reliably knows the model-turn and invocation lifecycle. Publishing a small semantic envelope there avoids duplicating heuristics in every protocol adapter, while keeping provider events available for observability and leaving presentation policy to consumers. It also allows consumers to choose either a full-trace projection or a final-answer projection without forcing buffering into the core token stream.

Verification

  • Targeted reactor tests covering core disposition semantics, subagent forwarding, AG-UI conversion, and managed Web preview reconciliation: 233 tests, 0 failures.
  • Full reactor compile/package/Javadoc/frontend verification with tests skipped: 89/89 modules successful.
  • mvn spotless:check and git diff --check are clean.

A complete Windows test run is not marked as passing because the repository contains symlink tests that require Windows symlink privileges and an existing order-sensitive baseline test. These are unrelated to this change; the affected streaming suites pass independently.

Related to #2872.
Related to #2975.

Checklist

  • Code has been formatted with mvn spotless:check
  • All tests are passing (mvn test)
  • Javadoc comments are complete and follow project conventions
  • Related documentation has been updated (e.g. links, examples, etc.)
  • Code is ready for review

@CLAassistant

CLAassistant commented Sep 6, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

按来源和任务关闭最后模型回复,并区分子代理成功、失败与取消结束。

补齐协议、AG-UI、前端和数据面的回归验证与最终修复报告。
透传子智能体生命周期、思考、文本处置、工具参数结果及任务标识。
修正 acting 中间件事件发布位置,避免事件遗漏或重复。
补齐本地子智能体的 AgentResultEvent 转发,避免只收到结束事件而缺少权威结果。
AG-UI 仅按当前运行实际生成的分段消息清理临时文本,避免误删合法历史消息。
Web 预览总线不再重放订阅前事件,并串行化并发发送以避免丢帧。
@dargoner
dargoner force-pushed the codex/pr-stream-events-text-disposition branch from f0f2f08 to 76413d6 Compare September 6, 2026 13:20
@dargoner
dargoner marked this pull request as ready for review September 6, 2026 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants