feat: compact UI tree output and separate max_tokens from context_window
Reduce get_ui_tree verbosity by skipping invisible nodes, omitting empty/default fields, and stripping common class prefixes. Lower defaults to max_nodes=300, max_depth=15. Introduce a dedicated context_window config field (default 128000) so summarization threshold is independent of the API response token limit (max_tokens). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c90d2a138f
commit
0524dc7b65
6 changed files with 56 additions and 18 deletions
|
|
@ -183,8 +183,8 @@ class ToolRequestHandler(
|
|||
val index = request.params?.get("index")?.jsonPrimitive?.intOrNull ?: 0
|
||||
val boundsX = request.params?.get("bounds_x")?.jsonPrimitive?.doubleOrNull
|
||||
val boundsY = request.params?.get("bounds_y")?.jsonPrimitive?.doubleOrNull
|
||||
val maxDepth = request.params?.get("max_depth")?.jsonPrimitive?.intOrNull ?: 30
|
||||
val maxNodes = request.params?.get("max_nodes")?.jsonPrimitive?.intOrNull ?: 2000
|
||||
val maxDepth = request.params?.get("max_depth")?.jsonPrimitive?.intOrNull ?: 15
|
||||
val maxNodes = request.params?.get("max_nodes")?.jsonPrimitive?.intOrNull ?: 300
|
||||
|
||||
return try {
|
||||
withContext(Dispatchers.Main) { setOverlayVisibility(false) }
|
||||
|
|
@ -262,20 +262,32 @@ class ToolRequestHandler(
|
|||
nodeCount: IntArray
|
||||
) {
|
||||
if (nodeCount[0] >= maxNodes) return
|
||||
// Skip invisible nodes
|
||||
if (!node.isVisibleToUser) return
|
||||
nodeCount[0]++
|
||||
val indent = " ".repeat(depth)
|
||||
val bounds = Rect()
|
||||
node.getBoundsInScreen(bounds)
|
||||
sb.appendLine(
|
||||
"${indent}[${node.className}] " +
|
||||
"text=${node.text ?: ""} " +
|
||||
"desc=${node.contentDescription ?: ""} " +
|
||||
"bounds=${bounds} " +
|
||||
"clickable=${node.isClickable} " +
|
||||
"enabled=${node.isEnabled} " +
|
||||
"visible=${node.isVisibleToUser} " +
|
||||
"id=${node.viewIdResourceName ?: ""}"
|
||||
)
|
||||
|
||||
// Strip common class name prefixes
|
||||
val className = node.className?.toString() ?: "View"
|
||||
val shortClass = className
|
||||
.removePrefix("android.widget.")
|
||||
.removePrefix("android.view.")
|
||||
|
||||
sb.append("${indent}[${shortClass}]")
|
||||
|
||||
// Only output non-empty fields
|
||||
node.text?.takeIf { it.isNotEmpty() }?.let { sb.append(" text=$it") }
|
||||
node.contentDescription?.takeIf { it.isNotEmpty() }?.let { sb.append(" desc=$it") }
|
||||
sb.append(" bounds=$bounds")
|
||||
// Only output non-default values: clickable=true (default is false), enabled=false (default is true)
|
||||
if (node.isClickable) sb.append(" clickable")
|
||||
if (!node.isEnabled) sb.append(" enabled=false")
|
||||
node.viewIdResourceName?.let { sb.append(" id=$it") }
|
||||
|
||||
sb.appendLine()
|
||||
|
||||
if (depth >= maxDepth) {
|
||||
if (node.childCount > 0) {
|
||||
sb.appendLine("${indent} [truncated: ${node.childCount} children at depth $depth]")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"restrict_to_workspace": true,
|
||||
"model": "glm-4.7",
|
||||
"max_tokens": 8192,
|
||||
"context_window": 128000,
|
||||
"temperature": 0.7,
|
||||
"max_tool_iterations": 20
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ type AgentLoop struct {
|
|||
provider providers.LLMProvider
|
||||
workspace string
|
||||
model string
|
||||
contextWindow int // Maximum context window size in tokens
|
||||
maxTokens int // Maximum tokens for API response
|
||||
contextWindow int // Maximum context window size in tokens (for summarization)
|
||||
maxIterations int
|
||||
sessions *session.SessionManager
|
||||
state *state.Manager
|
||||
|
|
@ -213,7 +214,8 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
|
|||
provider: provider,
|
||||
workspace: workspace,
|
||||
model: cfg.Agents.Defaults.Model,
|
||||
contextWindow: cfg.Agents.Defaults.MaxTokens, // Restore context window for summarization
|
||||
maxTokens: cfg.Agents.Defaults.MaxTokens,
|
||||
contextWindow: cfg.Agents.Defaults.ContextWindow,
|
||||
maxIterations: cfg.Agents.Defaults.MaxToolIterations,
|
||||
sessions: sessionsManager,
|
||||
state: stateManager,
|
||||
|
|
@ -672,7 +674,7 @@ func (al *AgentLoop) runLLMIteration(ctx context.Context, messages []providers.M
|
|||
"model": al.model,
|
||||
"messages_count": len(messages),
|
||||
"tools_count": len(providerToolDefs),
|
||||
"max_tokens": 8192,
|
||||
"max_tokens": al.maxTokens,
|
||||
"temperature": 0.7,
|
||||
"system_prompt_len": len(messages[0].Content),
|
||||
})
|
||||
|
|
@ -692,7 +694,7 @@ func (al *AgentLoop) runLLMIteration(ctx context.Context, messages []providers.M
|
|||
maxRetries := 2
|
||||
for retry := 0; retry <= maxRetries; retry++ {
|
||||
response, err = al.provider.Chat(ctx, messages, providerToolDefs, al.model, map[string]interface{}{
|
||||
"max_tokens": 8192,
|
||||
"max_tokens": al.maxTokens,
|
||||
"temperature": 0.7,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ func TestRecordLastChannel(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -91,6 +92,7 @@ func TestRecordLastChatID(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -137,6 +139,7 @@ func TestNewAgentLoop_StateInitialized(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -174,6 +177,7 @@ func TestToolRegistry_ToolRegistration(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -221,6 +225,7 @@ func TestToolContext_Updates(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -253,6 +258,7 @@ func TestToolRegistry_GetDefinitions(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -298,6 +304,7 @@ func TestAgentLoop_GetStartupInfo(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -346,6 +353,7 @@ func TestCreateToolRegistry_ExecDisabled(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -386,6 +394,7 @@ func TestCreateToolRegistry_ExecEnabled(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -431,6 +440,7 @@ func TestCreateToolRegistry_I2CDisabled(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -469,6 +479,7 @@ func TestCreateToolRegistry_I2CEnabled(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -512,6 +523,7 @@ func TestCreateToolRegistry_SPIDisabled(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -550,6 +562,7 @@ func TestCreateToolRegistry_SPIEnabled(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -593,6 +606,7 @@ func TestAgentLoop_Stop(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -716,6 +730,7 @@ func TestToolResult_SilentToolDoesNotSendUserMessage(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -759,6 +774,7 @@ func TestToolResult_UserFacingToolDoesSendMessage(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -825,6 +841,7 @@ func TestAgentLoop_ContextExhaustionRetry(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -921,6 +938,7 @@ func TestRetryLoop_CancelledContextSkipsCompression(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -983,6 +1001,7 @@ func TestForceCompression_ToolGroupBoundary(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -1069,6 +1088,7 @@ func TestForceCompression_MidOnAssistantWithToolCalls(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
@ -1156,6 +1176,7 @@ func TestForceCompression_NoteUsesUserRole(t *testing.T) {
|
|||
DataDir: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
ContextWindow: 128000,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ type AgentDefaults struct {
|
|||
Provider string `json:"provider" env:"PICOCLAW_AGENTS_DEFAULTS_PROVIDER"`
|
||||
Model string `json:"model" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL"`
|
||||
MaxTokens int `json:"max_tokens" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS"`
|
||||
ContextWindow int `json:"context_window" env:"PICOCLAW_AGENTS_DEFAULTS_CONTEXT_WINDOW"`
|
||||
Temperature float64 `json:"temperature" env:"PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE"`
|
||||
MaxToolIterations int `json:"max_tool_iterations" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS"`
|
||||
}
|
||||
|
|
@ -281,6 +282,7 @@ func DefaultConfig() *Config {
|
|||
Provider: "",
|
||||
Model: "glm-4.7",
|
||||
MaxTokens: 8192,
|
||||
ContextWindow: 128000,
|
||||
Temperature: 0.7,
|
||||
MaxToolIterations: 20,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -145,11 +145,11 @@ func (t *AndroidTool) Parameters() map[string]interface{} {
|
|||
},
|
||||
"max_depth": map[string]interface{}{
|
||||
"type": "integer",
|
||||
"description": "Maximum traversal depth (for get_ui_tree, default 30)",
|
||||
"description": "Maximum traversal depth (for get_ui_tree, default 15)",
|
||||
},
|
||||
"max_nodes": map[string]interface{}{
|
||||
"type": "integer",
|
||||
"description": "Maximum number of nodes to output (for get_ui_tree, default 2000)",
|
||||
"description": "Maximum number of nodes to output (for get_ui_tree, default 300)",
|
||||
},
|
||||
},
|
||||
"required": []string{"action"},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue