10 files changed
+57
No files matched your search
@@ -495,6 +495,13 @@ private fun ExpandedToolContent(
|
||||
}
|
||||
}
|
||||
|
||||
view.outputCut?.let { omitted ->
|
||||
Text(
|
||||
text = stringResource(R.string.chat_tool_output_omitted, omitted),
|
||||
style = MaterialTheme.typography.labelSmall.copy(color = statusColors.warning),
|
||||
)
|
||||
}
|
||||
|
||||
// ── Duration footer ──
|
||||
view.durationLabel?.let {
|
||||
Text(
|
||||
|
||||
@@ -41,6 +41,8 @@ internal interface ToolRenderer {
|
||||
*/
|
||||
internal data class ToolViewExtras(
|
||||
val detailLabel: String? = null,
|
||||
/** Process output characters omitted by the backend. */
|
||||
val outputCut: Long? = null,
|
||||
val stdout: String? = null,
|
||||
val stderr: String? = null,
|
||||
val exitCode: Int? = null,
|
||||
|
||||
@@ -35,6 +35,8 @@ data class ToolView(
|
||||
val detail: String = "",
|
||||
/** Optional section label above [detail] (e.g. "Search results"). */
|
||||
val detailLabel: String? = null,
|
||||
/** Process output characters omitted by the backend. */
|
||||
val outputCut: Long? = null,
|
||||
/** Compact count, e.g. "3 results". */
|
||||
val countLabel: String? = null,
|
||||
/** Human duration, e.g. "1.2s". */
|
||||
|
||||
@@ -106,6 +106,7 @@ object ToolViewBuilder {
|
||||
subtitle = subtitle,
|
||||
detail = clampedDetail,
|
||||
detailLabel = extras.detailLabel,
|
||||
outputCut = extras.outputCut,
|
||||
countLabel = countLabel?.let { (count, noun) -> "$count ${ToolCounts.pluralizeNoun(noun, count)}" },
|
||||
durationLabel = ToolJson.durationLabel(call.result),
|
||||
stdout = clampedStdout,
|
||||
|
||||
@@ -3,9 +3,12 @@ package com.m57.hermescontrol.ui.chat.tool.render
|
||||
import com.m57.hermescontrol.ui.chat.tool.ToolCall
|
||||
import com.m57.hermescontrol.ui.chat.tool.ToolJson
|
||||
import com.m57.hermescontrol.ui.chat.tool.ToolRenderer
|
||||
import com.m57.hermescontrol.ui.chat.tool.ToolViewExtras
|
||||
import com.m57.hermescontrol.ui.chat.tool.ToolViewStatus
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.longOrNull
|
||||
|
||||
/** `cronjob`: job listings with schedules, or the mutated job's key fields. */
|
||||
internal object CronjobRenderer : ToolRenderer {
|
||||
@@ -214,6 +217,15 @@ internal object SessionSearchRenderer : ToolRenderer {
|
||||
|
||||
/** `process`: background session listings and lifecycle acks. */
|
||||
internal object ProcessRenderer : ToolRenderer {
|
||||
override fun extras(
|
||||
call: ToolCall,
|
||||
status: ToolViewStatus,
|
||||
): ToolViewExtras {
|
||||
if (status == ToolViewStatus.RUNNING) return ToolViewExtras.NONE
|
||||
val count = (call.result?.get("output_cut") as? JsonPrimitive)?.takeUnless { it.isString }?.longOrNull
|
||||
return ToolViewExtras(outputCut = count?.takeIf { it > 0 })
|
||||
}
|
||||
|
||||
override fun subtitle(call: ToolCall): String {
|
||||
val action = ToolJson.firstString(call.args, listOf("action"))
|
||||
val procId = ToolJson.firstString(call.args, listOf("session_id"))
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
<string name="chat_input_placeholder_type_message">اكتب رسالة\u2026</string>
|
||||
<string name="chat_tool_execution_error">خطأ في التنفيذ: %1$s</string>
|
||||
<string name="chat_tool_exit_code">رمز الخروج: %1$d</string>
|
||||
<string name="chat_tool_output_omitted">تم حذف %1$d حرف من مخرجات العملية</string>
|
||||
<string name="chat_tool_fallback">أداة</string>
|
||||
<string name="chat_tool_show_raw">عرض ملف JSON الأولي</string>
|
||||
<string name="chat_tool_show_parsed">عرض المخرجات المعالجة</string>
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
<string name="chat_input_placeholder_type_message">메시지 입력…</string>
|
||||
<string name="chat_tool_execution_error">실행 오류: %1$s</string>
|
||||
<string name="chat_tool_exit_code">종료 코드: %1$d</string>
|
||||
<string name="chat_tool_output_omitted">프로세스 출력에서 %1$d자 생략됨</string>
|
||||
<string name="chat_tool_fallback">도구</string>
|
||||
<string name="chat_tool_show_raw">Raw JSON 보기</string>
|
||||
<string name="chat_tool_show_parsed">파싱된 결과 보기</string>
|
||||
|
||||
@@ -165,6 +165,7 @@
|
||||
<string name="chat_input_placeholder_type_message">输入消息…</string>
|
||||
<string name="chat_tool_execution_error">执行错误:%1$s</string>
|
||||
<string name="chat_tool_exit_code">退出码:%1$d</string>
|
||||
<string name="chat_tool_output_omitted">进程输出省略了 %1$d 个字符</string>
|
||||
<string name="chat_tool_fallback">工具</string>
|
||||
<string name="chat_tool_show_raw">显示原始 JSON</string>
|
||||
<string name="chat_tool_show_parsed">显示解析输出</string>
|
||||
|
||||
@@ -196,6 +196,7 @@
|
||||
<string name="chat_input_placeholder_type_message">Type a message\u2026</string>
|
||||
<string name="chat_tool_execution_error">Execution Error: %1$s</string>
|
||||
<string name="chat_tool_exit_code">Exit Code: %1$d</string>
|
||||
<string name="chat_tool_output_omitted">%1$d characters omitted from process output</string>
|
||||
<string name="chat_tool_fallback">Tool</string>
|
||||
<string name="chat_tool_show_raw">Show Raw JSON</string>
|
||||
<string name="chat_tool_show_parsed">Show Parsed Output</string>
|
||||
|
||||
@@ -308,6 +308,35 @@ class ToolViewBuilderTest {
|
||||
assertEquals("poll: proc-1", view.subtitle)
|
||||
assertTrue(view.detail.contains("Status: running"))
|
||||
assertTrue(view.detail.contains("listening on port 8080"))
|
||||
assertEquals(null, view.outputCut)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process output reports backend omitted character count for each action`() {
|
||||
listOf("poll", "wait", "log", "kill").forEach { action ->
|
||||
val outputKey = if (action == "poll") "output_preview" else "output"
|
||||
val view =
|
||||
build(
|
||||
"process_manage",
|
||||
"""{"action":"$action","session_id":"proc-1"}""",
|
||||
"""{"status":"exited","$outputKey":"partial output","output_cut":42}""",
|
||||
)
|
||||
|
||||
assertTrue(view.detail.contains("partial output"))
|
||||
assertEquals(42L, view.outputCut)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process output without positive numeric omission has no notice`() {
|
||||
listOf("{}", """{"output_cut":0}""", """{"output_cut":"unknown"}""").forEach { metadata ->
|
||||
val fields = metadata.removePrefix("{").removeSuffix("}")
|
||||
val result = """{"output":"complete"${if (fields.isNotEmpty()) ",$fields" else ""}}"""
|
||||
val view = build("process", """{"action":"wait"}""", result)
|
||||
|
||||
assertEquals("complete", view.detail)
|
||||
assertEquals(null, view.outputCut)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in new issue
Block a user