Skip to content

Commit 077a064

Browse files
committed
feat(renderer): show task execution time on completion
Display the total execution time when marking a task as complete across all renderers. This enhances user feedback by indicating how long each task took to finish.
1 parent ce48f7c commit 077a064

File tree

13 files changed

+101
-29
lines changed

13 files changed

+101
-29
lines changed

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ class CodingAgentExecutor(
6262
private val recentToolCalls = mutableListOf<String>()
6363
private val MAX_REPEAT_COUNT = 3
6464

65+
// Track task execution time
66+
private var taskStartTime: Long = 0L
67+
6568
/**
6669
* 执行 Agent 任务
6770
*/
@@ -71,13 +74,17 @@ class CodingAgentExecutor(
7174
onProgress: (String) -> Unit = {}
7275
): AgentResult {
7376
resetExecution()
77+
78+
// Start tracking execution time
79+
taskStartTime = Platform.getCurrentTimestamp()
80+
7481
conversationManager = ConversationManager(llmService, systemPrompt)
75-
82+
7683
// Set up token tracking callback to update renderer
7784
conversationManager?.onTokenUpdate = { tokenInfo ->
7885
renderer.updateTokenInfo(tokenInfo)
7986
}
80-
87+
8188
val initialUserMessage = buildInitialUserMessage(task)
8289

8390
onProgress("🚀 CodingAgent started")
@@ -103,7 +110,8 @@ class CodingAgentExecutor(
103110

104111
val allToolCalls = toolCallParser.parseToolCalls(llmResponse.toString())
105112
if (allToolCalls.isEmpty()) {
106-
renderer.renderTaskComplete()
113+
val executionTimeMs = Platform.getCurrentTimestamp() - taskStartTime
114+
renderer.renderTaskComplete(executionTimeMs)
107115
break
108116
}
109117

@@ -122,7 +130,8 @@ class CodingAgentExecutor(
122130
conversationManager!!.addToolResults(toolResultsText)
123131

124132
if (isTaskComplete(llmResponse.toString())) {
125-
renderer.renderTaskComplete()
133+
val executionTimeMs = Platform.getCurrentTimestamp() - taskStartTime
134+
renderer.renderTaskComplete(executionTimeMs)
126135
break
127136
}
128137

@@ -140,6 +149,7 @@ class CodingAgentExecutor(
140149
steps.clear()
141150
edits.clear()
142151
recentToolCalls.clear()
152+
taskStartTime = 0L
143153
}
144154

145155
private fun buildInitialUserMessage(task: AgentTask): String {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ interface CodingAgentRenderer {
3535
metadata: Map<String, String> = emptyMap()
3636
)
3737

38-
fun renderTaskComplete()
38+
/**
39+
* Render task completion message with execution time.
40+
*
41+
* @param executionTimeMs Total execution time in milliseconds from task start to completion
42+
*/
43+
fun renderTaskComplete(executionTimeMs: Long = 0L)
3944
fun renderFinalResult(success: Boolean, message: String, iterations: Int)
4045
fun renderError(message: String)
4146
fun renderRepeatWarning(toolName: String, count: Int)

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

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

71-
override fun renderTaskComplete() {
72-
println("✓ Task marked as complete\n")
71+
override fun renderTaskComplete(executionTimeMs: Long) {
72+
val timeStr = if (executionTimeMs > 0) {
73+
val seconds = executionTimeMs / 1000.0
74+
val rounded = (seconds * 100).toLong() / 100.0
75+
" (${rounded}s)"
76+
} else {
77+
""
78+
}
79+
println("✓ Task marked as complete$timeStr\n")
7380
}
7481

7582
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()
105+
fun renderTaskComplete(executionTimeMs: Double = 0.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() {
159-
jsRenderer.renderTaskComplete()
158+
override fun renderTaskComplete(executionTimeMs: Long) {
159+
jsRenderer.renderTaskComplete(executionTimeMs.toDouble())
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: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,23 @@ class JewelRenderer : BaseRenderer() {
407407
}
408408
}
409409

410-
override fun renderTaskComplete() {
410+
override fun renderTaskComplete(executionTimeMs: Long) {
411411
_taskCompleted.value = true
412412
_isProcessing.value = false
413+
414+
// Add a completion message with execution time to the timeline
415+
if (executionTimeMs > 0) {
416+
val seconds = executionTimeMs / 1000.0
417+
val rounded = (seconds * 100).toLong() / 100.0
418+
_timeline.update { items ->
419+
items + TimelineItem.MessageItem(
420+
message = Message(
421+
role = MessageRole.ASSISTANT,
422+
content = "✓ Task marked as complete (${rounded}s)"
423+
)
424+
)
425+
}
426+
}
413427
}
414428

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

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() {
46+
override fun renderTaskComplete(executionTimeMs: Long) {
4747
// Will be handled by renderFinalResult
4848
}
4949

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ class ComposeRenderer : BaseRenderer() {
5555
val errorMessage: String? get() = _errorMessage
5656

5757
private var _taskCompleted by mutableStateOf(false)
58-
val taskCompleted: Boolean get() = _taskCompleted
5958

6059
private var _executionStartTime by mutableStateOf(0L)
6160
val executionStartTime: Long get() = _executionStartTime
@@ -73,7 +72,6 @@ class ComposeRenderer : BaseRenderer() {
7372
private var _currentViewingFile by mutableStateOf<String?>(null)
7473
val currentViewingFile: String? get() = _currentViewingFile
7574

76-
// Task tracking from task-boundary tool
7775
private val _tasks = mutableStateListOf<TaskInfo>()
7876
val tasks: List<TaskInfo> = _tasks
7977

@@ -465,9 +463,23 @@ class ComposeRenderer : BaseRenderer() {
465463
}
466464
}
467465

468-
override fun renderTaskComplete() {
466+
override fun renderTaskComplete(executionTimeMs: Long) {
469467
_taskCompleted = true
470468
_isProcessing = false
469+
470+
// Add a completion message with execution time to the timeline
471+
if (executionTimeMs > 0) {
472+
val seconds = executionTimeMs / 1000.0
473+
val rounded = (seconds * 100).toLong() / 100.0
474+
_timeline.add(
475+
TimelineItem.MessageItem(
476+
message = Message(
477+
role = MessageRole.ASSISTANT,
478+
content = "✓ Task marked as complete (${rounded}s)"
479+
)
480+
)
481+
)
482+
}
471483
}
472484

473485
override fun renderFinalResult(

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(): void;
118+
abstract renderTaskComplete(executionTimeMs?: 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: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,13 @@ export class CliRenderer extends BaseRenderer {
524524
return null;
525525
}
526526

527-
renderTaskComplete(): void {
528-
console.log(semanticChalk.successBold('\n✓ Task marked as complete\n'));
527+
renderTaskComplete(executionTimeMs: number = 0): void {
528+
if (executionTimeMs > 0) {
529+
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'));
533+
}
529534
}
530535

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

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,14 @@ export class ServerRenderer extends BaseRenderer {
6868
}
6969
}
7070

71-
renderTaskComplete(): void {
71+
renderTaskComplete(executionTimeMs: number = 0): void {
7272
console.log('');
73-
console.log(semanticChalk.success('✅ Task marked as complete'));
73+
if (executionTimeMs > 0) {
74+
const seconds = (executionTimeMs / 1000).toFixed(2);
75+
console.log(semanticChalk.success(`✅ Task marked as complete (${seconds}s)`));
76+
} else {
77+
console.log(semanticChalk.success('✅ Task marked as complete'));
78+
}
7479
}
7580

7681
renderRepeatWarning(toolName: string, count: number): void {
@@ -308,12 +313,12 @@ export class ServerRenderer extends BaseRenderer {
308313
const docPath = paramsObj.documentPath;
309314
const maxResults = paramsObj.maxResults;
310315
const reranker = paramsObj.rerankerType;
311-
316+
312317
let details = `Query: "${query}"`;
313318
if (docPath) details += ` | Doc: ${docPath}`;
314319
if (maxResults) details += ` | Max: ${maxResults}`;
315320
if (reranker) details += ` | Reranker: ${reranker}`;
316-
321+
317322
return {
318323
name: 'DocQL',
319324
description: 'document query',

0 commit comments

Comments
 (0)