Skip to content

Commit 9158bf8

Browse files
committed
1 parent 968a1a1 commit 9158bf8

File tree

13 files changed

+611
-155
lines changed

13 files changed

+611
-155
lines changed

agentscope-examples/agui/src/main/java/io/agentscope/examples/agui/config/AgentConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ private Agent createDefaultAgent() {
8787
.model(
8888
DashScopeChatModel.builder().apiKey(apiKey).modelName("qwen-plus").stream(
8989
true)
90-
.enableThinking(true)
90+
.enableThinking(false)
9191
.formatter(new DashScopeChatFormatter())
9292
.build())
9393
.toolkit(toolkit)

agentscope-examples/agui/src/main/resources/application.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ agentscope:
4444
server-side-memory: true
4545
max-thread-sessions: 1000
4646
session-timeout-minutes: 30
47+
enable-reasoning: false
4748

4849
# Logging
4950
logging:

agentscope-examples/agui/src/main/resources/static/index.html

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,19 @@
117117
.message.tool {
118118
background: rgba(255, 158, 100, 0.1);
119119
border-left: 3px solid var(--accent-orange);
120-
margin: 0 60px;
120+
margin: 0 60px 16px 60px;
121121
font-size: 0.85rem;
122122
}
123123

124+
.message.reasoning {
125+
background: rgba(158, 206, 106, 0.08);
126+
border-left: 3px solid var(--accent-green);
127+
margin: 0 60px 16px 60px;
128+
font-size: 0.85rem;
129+
font-style: italic;
130+
opacity: 0.9;
131+
}
132+
124133
.message.error {
125134
background: rgba(247, 118, 142, 0.1);
126135
border-left: 3px solid var(--accent-red);
@@ -137,6 +146,7 @@
137146
.message.user .message-role { color: var(--text-secondary); }
138147
.message.assistant .message-role { color: var(--accent-blue); }
139148
.message.tool .message-role { color: var(--accent-orange); }
149+
.message.reasoning .message-role { color: var(--accent-green); }
140150

141151
.message-content {
142152
white-space: pre-wrap;
@@ -327,6 +337,14 @@ <h1>AgentScope AG-UI Demo</h1>
327337
return;
328338
}
329339

340+
if (append && role === 'reasoning' && currentReasoningDiv) {
341+
// Append to current reasoning message
342+
const contentEl = currentReasoningDiv.querySelector('.message-content');
343+
contentEl.textContent += content;
344+
messages.scrollTop = messages.scrollHeight;
345+
return;
346+
}
347+
330348
const div = document.createElement('div');
331349
div.className = `message ${role}`;
332350
div.innerHTML = `
@@ -338,6 +356,8 @@ <h1>AgentScope AG-UI Demo</h1>
338356

339357
if (role === 'assistant') {
340358
currentAssistantDiv = div;
359+
} else if (role === 'reasoning') {
360+
currentReasoningDiv = div;
341361
}
342362
}
343363

@@ -380,6 +400,9 @@ <h1>AgentScope AG-UI Demo</h1>
380400

381401
let assistantContent = '';
382402
let currentMessageId = null;
403+
let reasoningContent = '';
404+
let currentReasoningMessageId = null;
405+
let currentReasoningDiv = null;
383406

384407
try {
385408
await client.run({
@@ -390,6 +413,29 @@ <h1>AgentScope AG-UI Demo</h1>
390413
onRunStarted: () => {
391414
console.log('Run started');
392415
currentAssistantDiv = null;
416+
currentReasoningDiv = null;
417+
reasoningContent = '';
418+
},
419+
onReasoningMessageStart: (messageId, role) => {
420+
console.log('Reasoning message start:', messageId, role);
421+
hideTypingIndicator();
422+
currentReasoningMessageId = messageId;
423+
reasoningContent = '';
424+
currentReasoningDiv = null;
425+
},
426+
onReasoningContent: (delta, messageId) => {
427+
console.log('Reasoning content delta:', delta);
428+
if (reasoningContent === '') {
429+
appendMessage('reasoning', delta);
430+
} else {
431+
appendMessage('reasoning', delta, true);
432+
}
433+
reasoningContent += delta;
434+
},
435+
onReasoningMessageEnd: (messageId) => {
436+
console.log('Reasoning message end:', messageId);
437+
currentReasoningDiv = null;
438+
reasoningContent = '';
393439
},
394440
onTextMessageStart: (messageId, role) => {
395441
console.log('Text message start:', messageId, role);
@@ -463,6 +509,8 @@ <h1>AgentScope AG-UI Demo</h1>
463509
stopBtn.style.display = 'none';
464510
hideTypingIndicator();
465511
currentAssistantDiv = null;
512+
currentReasoningDiv = null;
513+
reasoningContent = '';
466514
if (statusText.textContent === 'Running...') {
467515
setStatus('ready', 'Ready');
468516
}

agentscope-examples/agui/src/main/resources/static/js/agui-client.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* messages: [{ id: 'msg-1', role: 'user', content: 'Hello!' }]
2828
* }, {
2929
* onTextContent: (delta) => console.log(delta),
30+
* onReasoningContent: (delta) => console.log('Reasoning:', delta),
3031
* onRunFinished: () => console.log('Done')
3132
* });
3233
*/
@@ -71,6 +72,9 @@ class AguiClient {
7172
* @param {Object} [input.state] - Optional state
7273
* @param {Object} [input.forwardedProps] - Optional forwarded properties
7374
* @param {Object} callbacks - Event callbacks
75+
* @param {Function} [callbacks.onReasoningMessageStart] - Called when reasoning message starts
76+
* @param {Function} [callbacks.onReasoningContent] - Called with reasoning content delta
77+
* @param {Function} [callbacks.onReasoningMessageEnd] - Called when reasoning message ends
7478
* @returns {Promise} Resolves when the run completes
7579
*/
7680
async run(input, callbacks = {}) {
@@ -220,6 +224,22 @@ class AguiClient {
220224
callbacks.onTextMessageEnd?.(event.messageId);
221225
break;
222226

227+
case 'REASONING_MESSAGE_START':
228+
callbacks.onReasoningMessageStart?.(event.messageId, event.role);
229+
break;
230+
231+
case 'REASONING_MESSAGE_CONTENT':
232+
// Ensure delta is not null/undefined
233+
const reasoningDelta = event.delta || '';
234+
if (reasoningDelta) {
235+
callbacks.onReasoningContent?.(reasoningDelta, event.messageId);
236+
}
237+
break;
238+
239+
case 'REASONING_MESSAGE_END':
240+
callbacks.onReasoningMessageEnd?.(event.messageId);
241+
break;
242+
223243
case 'TOOL_CALL_START':
224244
callbacks.onToolCallStart?.(event.toolCallId, event.toolCallName);
225245
break;

agentscope-extensions/agentscope-extensions-agui/src/main/java/io/agentscope/core/agui/adapter/AguiAdapterConfig.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ public class AguiAdapterConfig {
2929
private final ToolMergeMode toolMergeMode;
3030
private final boolean emitStateEvents;
3131
private final boolean emitToolCallArgs;
32+
private final boolean enableReasoning;
3233
private final Duration runTimeout;
3334
private final String defaultAgentId;
3435

3536
private AguiAdapterConfig(Builder builder) {
3637
this.toolMergeMode = builder.toolMergeMode;
3738
this.emitStateEvents = builder.emitStateEvents;
3839
this.emitToolCallArgs = builder.emitToolCallArgs;
40+
this.enableReasoning = builder.enableReasoning;
3941
this.runTimeout = builder.runTimeout;
4042
this.defaultAgentId = builder.defaultAgentId;
4143
}
@@ -67,6 +69,19 @@ public boolean isEmitToolCallArgs() {
6769
return emitToolCallArgs;
6870
}
6971

72+
/**
73+
* Check if reasoning/thinking content should be emitted.
74+
*
75+
* <p>When enabled, ThinkingBlock content will be converted to REASONING_* events
76+
* according to the AG-UI Reasoning draft specification. When disabled (default),
77+
* ThinkingBlock content is ignored and no reasoning events are emitted.
78+
*
79+
* @return true if reasoning events should be emitted
80+
*/
81+
public boolean isEnableReasoning() {
82+
return enableReasoning;
83+
}
84+
7085
/**
7186
* Get the run timeout duration.
7287
*
@@ -111,6 +126,7 @@ public static class Builder {
111126
private ToolMergeMode toolMergeMode = ToolMergeMode.MERGE_FRONTEND_PRIORITY;
112127
private boolean emitStateEvents = true;
113128
private boolean emitToolCallArgs = true;
129+
private boolean enableReasoning = false;
114130
private Duration runTimeout = Duration.ofMinutes(10);
115131
private String defaultAgentId;
116132

@@ -147,6 +163,21 @@ public Builder emitToolCallArgs(boolean emitToolCallArgs) {
147163
return this;
148164
}
149165

166+
/**
167+
* Set whether to enable reasoning/thinking content output.
168+
*
169+
* <p>When enabled, ThinkingBlock content will be converted to REASONING_* events
170+
* according to the AG-UI Reasoning draft specification. Default is false to ensure
171+
* backward compatibility and privacy compliance.
172+
*
173+
* @param enableReasoning true to enable reasoning events
174+
* @return This builder
175+
*/
176+
public Builder enableReasoning(boolean enableReasoning) {
177+
this.enableReasoning = enableReasoning;
178+
return this;
179+
}
180+
150181
/**
151182
* Set the run timeout duration.
152183
*

agentscope-extensions/agentscope-extensions-agui/src/main/java/io/agentscope/core/agui/adapter/AguiAgentAdapter.java

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,18 @@
4747
*
4848
* <p><b>Event Mapping:</b>
4949
* <ul>
50-
* <li>AgentScope REASONING events → AG-UI TEXT_MESSAGE_* events</li>
50+
* <li>AgentScope REASONING events → AG-UI TEXT_MESSAGE_* events (for TextBlock)</li>
51+
* <li>AgentScope REASONING events → AG-UI REASONING_* events (for ThinkingBlock, when enabled)</li>
5152
* <li>AgentScope TOOL_RESULT events → AG-UI TOOL_CALL_END events</li>
5253
* <li>ToolUseBlock content → AG-UI TOOL_CALL_START events</li>
5354
* </ul>
55+
*
56+
* <p><b>Reasoning Support:</b>
57+
* <ul>
58+
* <li>ThinkingBlock content is converted to REASONING_* events according to AG-UI Reasoning draft</li>
59+
* <li>Reasoning output is disabled by default (enableReasoning=false) for backward compatibility</li>
60+
* <li>Set enableReasoning=true in AguiAdapterConfig to enable reasoning events</li>
61+
* </ul>
5462
*/
5563
public class AguiAgentAdapter {
5664

@@ -158,38 +166,40 @@ private List<AguiEvent> convertEvent(Event event, EventConversionState state) {
158166
}
159167
}
160168
} else if (block instanceof ThinkingBlock thinkingBlock) {
161-
// Handle thinking blocks - convert to text messages with special messageId
162-
String thinking = thinkingBlock.getThinking();
163-
if (thinking != null && !thinking.isEmpty()) {
164-
String thinkingMessageId = msg.getId() + "-thinking";
165-
166-
// Start message if not started
167-
if (!state.hasStartedMessage(thinkingMessageId)) {
168-
events.add(
169-
new AguiEvent.TextMessageStart(
170-
state.threadId,
171-
state.runId,
172-
thinkingMessageId,
173-
"assistant"));
174-
state.startMessage(thinkingMessageId);
175-
}
176-
177-
if (!event.isLast()) {
178-
// In incremental mode, thinking is already the delta
179-
events.add(
180-
new AguiEvent.TextMessageContent(
181-
state.threadId,
182-
state.runId,
183-
thinkingMessageId,
184-
thinking));
185-
} else {
186-
// End message if this is the last event
187-
events.add(
188-
new AguiEvent.TextMessageEnd(
189-
state.threadId, state.runId, thinkingMessageId));
190-
state.endMessage(thinkingMessageId);
169+
// Handle thinking blocks - convert to REASONING_* events (only if enabled)
170+
// According to AG-UI Reasoning draft: https://docs.ag-ui.com/drafts/reasoning
171+
if (config.isEnableReasoning()) {
172+
String thinking = thinkingBlock.getThinking();
173+
if (thinking != null && !thinking.isEmpty()) {
174+
String messageId = msg.getId();
175+
176+
// Start reasoning message if not started
177+
if (!state.hasStartedReasoningMessage(messageId)) {
178+
events.add(
179+
new AguiEvent.ReasoningMessageStart(
180+
state.threadId,
181+
state.runId,
182+
messageId,
183+
"assistant"));
184+
state.startReasoningMessage(messageId);
185+
}
186+
187+
if (!event.isLast()) {
188+
// In incremental mode, thinking is already the delta
189+
events.add(
190+
new AguiEvent.ReasoningMessageContent(
191+
state.threadId, state.runId, messageId, thinking));
192+
} else {
193+
// End reasoning message if this is the last event
194+
events.add(
195+
new AguiEvent.ReasoningMessageEnd(
196+
state.threadId, state.runId, messageId));
197+
state.endReasoningMessage(messageId);
198+
}
191199
}
192200
}
201+
// If reasoning is disabled, ThinkingBlock content is ignored (backward
202+
// compatibility)
193203
} else if (block instanceof ToolUseBlock toolUse) {
194204
// End any active text message before starting tool call
195205
if (state.hasActiveTextMessage()) {
@@ -276,6 +286,14 @@ private Flux<AguiEvent> finishRun(EventConversionState state) {
276286
}
277287
}
278288

289+
// End any reasoning messages that weren't properly ended
290+
for (String messageId : state.getStartedReasoningMessages()) {
291+
if (!state.hasEndedReasoningMessage(messageId)) {
292+
events.add(
293+
new AguiEvent.ReasoningMessageEnd(state.threadId, state.runId, messageId));
294+
}
295+
}
296+
279297
// Emit RUN_FINISHED
280298
events.add(new AguiEvent.RunFinished(state.threadId, state.runId));
281299

@@ -334,6 +352,8 @@ private static class EventConversionState {
334352
private final Set<String> endedMessages = new LinkedHashSet<>();
335353
private final Set<String> startedToolCalls = new LinkedHashSet<>();
336354
private final Set<String> endedToolCalls = new LinkedHashSet<>();
355+
private final Set<String> startedReasoningMessages = new LinkedHashSet<>();
356+
private final Set<String> endedReasoningMessages = new LinkedHashSet<>();
337357
private String currentTextMessageId = null;
338358

339359
EventConversionState(String threadId, String runId) {
@@ -392,5 +412,25 @@ boolean hasEndedToolCall(String toolCallId) {
392412
Set<String> getStartedToolCalls() {
393413
return startedToolCalls;
394414
}
415+
416+
boolean hasStartedReasoningMessage(String messageId) {
417+
return startedReasoningMessages.contains(messageId);
418+
}
419+
420+
void startReasoningMessage(String messageId) {
421+
startedReasoningMessages.add(messageId);
422+
}
423+
424+
void endReasoningMessage(String messageId) {
425+
endedReasoningMessages.add(messageId);
426+
}
427+
428+
boolean hasEndedReasoningMessage(String messageId) {
429+
return endedReasoningMessages.contains(messageId);
430+
}
431+
432+
Set<String> getStartedReasoningMessages() {
433+
return startedReasoningMessages;
434+
}
395435
}
396436
}

0 commit comments

Comments
 (0)