Skip to content

Commit 0ae3e4a

Browse files
committed
refactor(renderer): unify renderer interface across platforms
Standardize renderer implementations to use a consistent interface for improved maintainability and cross-platform support.
1 parent 077a064 commit 0ae3e4a

File tree

16 files changed

+222
-122
lines changed

16 files changed

+222
-122
lines changed

AGENTS.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ Rest:
3131
- For WASM platform, we should not use emoji and utf8 in code.
3232
- Use ./gradlew :mpp-ui:generateI18n4kFiles for i18n
3333

34+
## CodingAgentRenderer Implementations
35+
36+
When modifying `CodingAgentRenderer` interface, **ALL** renderer implementations must be updated:
37+
- **Kotlin**: `DefaultCodingAgentRenderer`, `ComposeRenderer`, `JewelRenderer`, `ServerSideRenderer`, `JsRendererAdapter`
38+
- **TypeScript**: `BaseRenderer.ts`, `CliRenderer.ts`, `ServerRenderer.ts`, `TuiRenderer.ts`
39+
- **VSCode**: `mpp-vscode/src/bridge/mpp-core.ts` (VSCodeRenderer), `mpp-vscode/src/providers/chat-view.ts`
40+
- **JVM CLI**: `CodingCliRenderer` (mpp-ui/src/jvmMain/.../CodingCli.kt), `ConsoleRenderer` (mpp-ui/src/jvmMain/.../DocumentCli.kt)
41+
3442
## Design System \(Color & Theme\)
3543

3644
- **CLI/TUI (TypeScript)**: Use `mpp-ui/src/jsMain/typescript/design-system/` → Import `semanticInk` / `semanticChalk`

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/executor/CodingAgentExecutor.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ class CodingAgentExecutor(
131131

132132
if (isTaskComplete(llmResponse.toString())) {
133133
val executionTimeMs = Platform.getCurrentTimestamp() - taskStartTime
134-
renderer.renderTaskComplete(executionTimeMs)
134+
val toolsUsedCount = steps.size
135+
renderer.renderTaskComplete(executionTimeMs, toolsUsedCount)
135136
break
136137
}
137138

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/CodingAgentRenderer.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ interface CodingAgentRenderer {
3636
)
3737

3838
/**
39-
* Render task completion message with execution time.
39+
* Render task completion message with execution time and tool usage statistics.
4040
*
4141
* @param executionTimeMs Total execution time in milliseconds from task start to completion
42+
* @param toolsUsedCount Number of tools used during execution
4243
*/
43-
fun renderTaskComplete(executionTimeMs: Long = 0L)
44+
fun renderTaskComplete(executionTimeMs: Long = 0L, toolsUsedCount: Int = 0)
4445
fun renderFinalResult(success: Boolean, message: String, iterations: Int)
4546
fun renderError(message: String)
4647
fun renderRepeatWarning(toolName: String, count: Int)

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/render/DefaultCodingAgentRenderer.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,21 @@ class DefaultCodingAgentRenderer : BaseRenderer() {
6868
println()
6969
}
7070

71-
override fun renderTaskComplete(executionTimeMs: Long) {
72-
val timeStr = if (executionTimeMs > 0) {
71+
override fun renderTaskComplete(executionTimeMs: Long, toolsUsedCount: Int) {
72+
val parts = mutableListOf<String>()
73+
74+
if (executionTimeMs > 0) {
7375
val seconds = executionTimeMs / 1000.0
7476
val rounded = (seconds * 100).toLong() / 100.0
75-
" (${rounded}s)"
76-
} else {
77-
""
77+
parts.add("${rounded}s")
78+
}
79+
80+
if (toolsUsedCount > 0) {
81+
parts.add("$toolsUsedCount tools")
7882
}
79-
println("✓ Task marked as complete$timeStr\n")
83+
84+
val info = if (parts.isNotEmpty()) " (${parts.joinToString(", ")})" else ""
85+
println("✓ Task marked as complete$info\n")
8086
}
8187

8288
override fun renderFinalResult(success: Boolean, message: String, iterations: Int) {

mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/RendererExports.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ interface JsCodingAgentRenderer {
102102
fun renderToolResult(toolName: String, success: Boolean, output: String?, fullOutput: String?)
103103

104104
// Status and completion methods
105-
fun renderTaskComplete(executionTimeMs: Double = 0.0)
105+
fun renderTaskComplete(executionTimeMs: Double = 0.0, toolsUsedCount: Int = 0)
106106
fun renderFinalResult(success: Boolean, message: String, iterations: Int)
107107
fun renderError(message: String)
108108
fun renderRepeatWarning(toolName: String, count: Int)
@@ -155,8 +155,8 @@ class JsRendererAdapter(private val jsRenderer: JsCodingAgentRenderer) : CodingA
155155
jsRenderer.renderToolResult(toolName, success, output, fullOutput)
156156
}
157157

158-
override fun renderTaskComplete(executionTimeMs: Long) {
159-
jsRenderer.renderTaskComplete(executionTimeMs.toDouble())
158+
override fun renderTaskComplete(executionTimeMs: Long, toolsUsedCount: Int) {
159+
jsRenderer.renderTaskComplete(executionTimeMs.toDouble(), toolsUsedCount)
160160
}
161161

162162
override fun renderFinalResult(success: Boolean, message: String, iterations: Int) {

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/renderer/JewelRenderer.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,19 +407,29 @@ class JewelRenderer : BaseRenderer() {
407407
}
408408
}
409409

410-
override fun renderTaskComplete(executionTimeMs: Long) {
410+
override fun renderTaskComplete(executionTimeMs: Long, toolsUsedCount: Int) {
411411
_taskCompleted.value = true
412412
_isProcessing.value = false
413413

414-
// Add a completion message with execution time to the timeline
414+
// Add a completion message with execution time and tool usage to the timeline
415+
val parts = mutableListOf<String>()
416+
415417
if (executionTimeMs > 0) {
416418
val seconds = executionTimeMs / 1000.0
417419
val rounded = (seconds * 100).toLong() / 100.0
420+
parts.add("${rounded}s")
421+
}
422+
423+
if (toolsUsedCount > 0) {
424+
parts.add("$toolsUsedCount tools")
425+
}
426+
427+
if (parts.isNotEmpty()) {
418428
_timeline.update { items ->
419429
items + TimelineItem.MessageItem(
420430
message = Message(
421431
role = MessageRole.ASSISTANT,
422-
content = "✓ Task marked as complete (${rounded}s)"
432+
content = "✓ Task marked as complete (${parts.joinToString(", ")})"
423433
)
424434
)
425435
}

mpp-server/src/main/kotlin/cc/unitmesh/server/render/ServerSideRenderer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class ServerSideRenderer : CodingAgentRenderer {
4343
eventChannel.trySend(AgentEvent.ToolResult(toolName, success, output))
4444
}
4545

46-
override fun renderTaskComplete(executionTimeMs: Long) {
46+
override fun renderTaskComplete(executionTimeMs: Long, toolsUsedCount: Int) {
4747
// Will be handled by renderFinalResult
4848
}
4949

mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/agent/ComposeRenderer.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,19 +463,29 @@ class ComposeRenderer : BaseRenderer() {
463463
}
464464
}
465465

466-
override fun renderTaskComplete(executionTimeMs: Long) {
466+
override fun renderTaskComplete(executionTimeMs: Long, toolsUsedCount: Int) {
467467
_taskCompleted = true
468468
_isProcessing = false
469469

470-
// Add a completion message with execution time to the timeline
470+
// Add a completion message with execution time and tool usage to the timeline
471+
val parts = mutableListOf<String>()
472+
471473
if (executionTimeMs > 0) {
472474
val seconds = executionTimeMs / 1000.0
473475
val rounded = (seconds * 100).toLong() / 100.0
476+
parts.add("${rounded}s")
477+
}
478+
479+
if (toolsUsedCount > 0) {
480+
parts.add("$toolsUsedCount tools")
481+
}
482+
483+
if (parts.isNotEmpty()) {
474484
_timeline.add(
475485
TimelineItem.MessageItem(
476486
message = Message(
477487
role = MessageRole.ASSISTANT,
478-
content = "✓ Task marked as complete (${rounded}s)"
488+
content = "✓ Task marked as complete (${parts.joinToString(", ")})"
479489
)
480490
)
481491
)

mpp-ui/src/jsMain/typescript/agents/render/BaseRenderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export abstract class BaseRenderer implements JsCodingAgentRenderer {
115115
abstract renderLLMResponseEnd(): void;
116116
abstract renderToolCall(toolName: string, paramsStr: string): void;
117117
abstract renderToolResult(toolName: string, success: boolean, output: string | null, fullOutput?: string | null, metadata?: Record<string, string>): void;
118-
abstract renderTaskComplete(executionTimeMs?: number): void;
118+
abstract renderTaskComplete(executionTimeMs?: number, toolsUsedCount?: number): void;
119119
abstract renderFinalResult(success: boolean, message: string, iterations: number): void;
120120
abstract renderError(message: string): void;
121121
abstract renderRepeatWarning(toolName: string, count: number): void;

mpp-ui/src/jsMain/typescript/agents/render/CliRenderer.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -524,13 +524,20 @@ export class CliRenderer extends BaseRenderer {
524524
return null;
525525
}
526526

527-
renderTaskComplete(executionTimeMs: number = 0): void {
527+
renderTaskComplete(executionTimeMs: number = 0, toolsUsedCount: number = 0): void {
528+
const parts: string[] = [];
529+
528530
if (executionTimeMs > 0) {
529531
const seconds = (executionTimeMs / 1000).toFixed(2);
530-
console.log(semanticChalk.successBold(`\n✓ Task marked as complete (${seconds}s)\n`));
531-
} else {
532-
console.log(semanticChalk.successBold('\n✓ Task marked as complete\n'));
532+
parts.push(`${seconds}s`);
533533
}
534+
535+
if (toolsUsedCount > 0) {
536+
parts.push(`${toolsUsedCount} tools`);
537+
}
538+
539+
const info = parts.length > 0 ? ` (${parts.join(', ')})` : '';
540+
console.log(semanticChalk.successBold(`\n✓ Task marked as complete${info}\n`));
534541
}
535542

536543
renderFinalResult(success: boolean, message: string, iterations: number): void {

0 commit comments

Comments
 (0)