Skip to content
This repository was archived by the owner on Apr 18, 2026. It is now read-only.

Commit e0b7ef6

Browse files
committed
✨ performance updated 2
1 parent a716fb0 commit e0b7ef6

4 files changed

Lines changed: 180 additions & 8 deletions

File tree

src/agent.py

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
set_rotation_callbacks, model_fallback_manager
2626
)
2727
from .tools import TOOL_DEFINITIONS, execute_tool, TOOLS, get_all_tool_definitions
28+
from .terminal import terminal_title
2829
from .ui import (
2930
console, display_tool_call, display_tool_result, display_executed_tool,
3031
display_code_execution_result, display_info, display_warning,
@@ -397,6 +398,7 @@ def _generate():
397398
try:
398399
title = self.client_manager.generate_title(first_message)
399400
history_manager.set_title(title)
401+
terminal_title.set_session(title)
400402
log_debug(f"Generated title: {title}")
401403
except Exception as e:
402404
log_error("Failed to generate title", e)
@@ -435,18 +437,91 @@ def _parse_tool_calls_from_text(self, text: str) -> List[ToolCall]:
435437

436438
return tool_calls
437439

440+
def _repair_json(self, json_str: str) -> str:
441+
"""Attempt to repair malformed JSON from LLM responses"""
442+
if not json_str or not json_str.strip():
443+
return "{}"
444+
445+
s = json_str.strip()
446+
447+
# Extract JSON if wrapped in markdown code blocks
448+
if "```json" in s:
449+
start = s.find("```json") + 7
450+
end = s.find("```", start)
451+
if end > start:
452+
s = s[start:end].strip()
453+
elif "```" in s:
454+
start = s.find("```") + 3
455+
end = s.find("```", start)
456+
if end > start:
457+
s = s[start:end].strip()
458+
459+
# Find the actual JSON object/array
460+
first_brace = s.find('{')
461+
first_bracket = s.find('[')
462+
if first_brace == -1 and first_bracket == -1:
463+
return "{}"
464+
465+
if first_brace != -1 and (first_bracket == -1 or first_brace < first_bracket):
466+
s = s[first_brace:]
467+
# Find matching closing brace
468+
depth = 0
469+
in_string = False
470+
escape = False
471+
end_idx = len(s)
472+
for i, c in enumerate(s):
473+
if escape:
474+
escape = False
475+
continue
476+
if c == '\\':
477+
escape = True
478+
continue
479+
if c == '"' and not escape:
480+
in_string = not in_string
481+
continue
482+
if in_string:
483+
continue
484+
if c == '{':
485+
depth += 1
486+
elif c == '}':
487+
depth -= 1
488+
if depth == 0:
489+
end_idx = i + 1
490+
break
491+
s = s[:end_idx]
492+
elif first_bracket != -1:
493+
s = s[first_bracket:]
494+
495+
# Fix common JSON issues
496+
# Remove trailing commas before } or ]
497+
s = re.sub(r',\s*([}\]])', r'\1', s)
498+
499+
# Balance braces if needed
500+
open_braces = s.count('{') - s.count('}')
501+
open_brackets = s.count('[') - s.count(']')
502+
s = s + '}' * max(0, open_braces) + ']' * max(0, open_brackets)
503+
504+
return s
505+
438506
def _parse_tool_args(self, tc) -> dict:
439-
"""Parse tool call arguments"""
507+
"""Parse tool call arguments with JSON repair for malformed responses"""
440508
try:
441509
if isinstance(tc.arguments, dict):
442510
return tc.arguments
443511
elif isinstance(tc.arguments, str) and tc.arguments.strip():
444512
return json.loads(tc.arguments)
445513
else:
446514
return {}
447-
except json.JSONDecodeError as e:
448-
log_error("Failed to parse tool arguments", e, {"tool": tc.name, "args": tc.arguments[:200] if tc.arguments else ""})
449-
return {}
515+
except json.JSONDecodeError:
516+
# Try to repair the JSON
517+
try:
518+
repaired = self._repair_json(tc.arguments)
519+
result = json.loads(repaired)
520+
log_debug(f"Repaired malformed JSON for tool {tc.name}")
521+
return result
522+
except json.JSONDecodeError as e:
523+
log_error("Failed to parse tool arguments", e, {"tool": tc.name, "args": tc.arguments[:200] if tc.arguments else ""})
524+
return {}
450525

451526
def _execute_tool_only(self, tc, args: dict) -> tuple:
452527
"""Execute a tool without UI - for parallel execution"""

src/clients.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,53 @@ def _stream_compound_chat(
307307
)
308308
raise
309309

310+
def _estimate_tokens(self, messages: List[Dict[str, Any]]) -> int:
311+
"""Rough estimate of token count (approx 4 chars per token)"""
312+
total_chars = 0
313+
for msg in messages:
314+
content = msg.get("content", "")
315+
if isinstance(content, str):
316+
total_chars += len(content)
317+
elif isinstance(content, list):
318+
for item in content:
319+
if isinstance(item, dict) and "text" in item:
320+
total_chars += len(item["text"])
321+
# Count tool calls too
322+
if "tool_calls" in msg:
323+
for tc in msg.get("tool_calls", []):
324+
if isinstance(tc, dict) and "function" in tc:
325+
total_chars += len(str(tc["function"]))
326+
return total_chars // 4
327+
328+
def _truncate_messages_for_limit(self, messages: List[Dict[str, Any]], max_tokens: int = 5000) -> List[Dict[str, Any]]:
329+
"""Truncate older messages to stay within token limit for GPT-OSS models"""
330+
if not messages:
331+
return messages
332+
333+
# Always keep system message and last few messages
334+
system_msgs = [m for m in messages if m.get("role") == "system"]
335+
non_system = [m for m in messages if m.get("role") != "system"]
336+
337+
if self._estimate_tokens(messages) <= max_tokens:
338+
return messages
339+
340+
# Keep system + last N messages, progressively reducing
341+
result = system_msgs.copy()
342+
for i in range(len(non_system) - 1, -1, -1):
343+
candidate = result + non_system[i:]
344+
if self._estimate_tokens(candidate) <= max_tokens:
345+
return candidate
346+
347+
# If still too large, just keep system + last message with truncated content
348+
if non_system:
349+
last_msg = non_system[-1].copy()
350+
content = last_msg.get("content", "")
351+
if isinstance(content, str) and len(content) > 2000:
352+
last_msg["content"] = content[:2000] + "... (truncated)"
353+
result.append(last_msg)
354+
355+
return result
356+
310357
def _stream_gpt_oss_chat(
311358
self,
312359
client,
@@ -320,9 +367,12 @@ def _stream_gpt_oss_chat(
320367
# Import here to avoid circular imports
321368
from .tools import get_all_tool_definitions
322369

370+
# Truncate messages to avoid rate limit (GPT-OSS has 8000 TPM limit)
371+
truncated_messages = self._truncate_messages_for_limit(messages, max_tokens=5000)
372+
323373
try:
324374
kwargs = {
325-
"messages": messages,
375+
"messages": truncated_messages,
326376
"model": model,
327377
"stream": True
328378
}

src/multi_agent.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import time
88
import uuid
99
import queue
10+
import re
11+
import json
1012
from enum import Enum
1113
from typing import Dict, Any, List, Optional, Callable
1214
from dataclasses import dataclass, field
@@ -24,6 +26,47 @@
2426
from .config import COLORS, AVAILABLE_MODELS, DEFAULT_MODEL, get_system_prompt
2527

2628

29+
# ═══════════════════════════════════════════════════════════════════════════════
30+
# JSON Repair Helper
31+
# ═══════════════════════════════════════════════════════════════════════════════
32+
33+
def _repair_json(json_str: str) -> str:
34+
"""Attempt to repair malformed JSON from LLM responses"""
35+
if not json_str or not json_str.strip():
36+
return "{}"
37+
38+
s = json_str.strip()
39+
40+
# Extract JSON if wrapped in markdown code blocks
41+
if "```json" in s:
42+
start = s.find("```json") + 7
43+
end = s.find("```", start)
44+
if end > start:
45+
s = s[start:end].strip()
46+
elif "```" in s:
47+
start = s.find("```") + 3
48+
end = s.find("```", start)
49+
if end > start:
50+
s = s[start:end].strip()
51+
52+
# Find the actual JSON object
53+
first_brace = s.find('{')
54+
if first_brace == -1:
55+
return "{}"
56+
57+
s = s[first_brace:]
58+
59+
# Remove trailing commas before } or ]
60+
s = re.sub(r',\s*([}\]])', r'\1', s)
61+
62+
# Balance braces if needed
63+
open_braces = s.count('{') - s.count('}')
64+
open_brackets = s.count('[') - s.count(']')
65+
s = s + '}' * max(0, open_braces) + ']' * max(0, open_brackets)
66+
67+
return s
68+
69+
2770
# ═══════════════════════════════════════════════════════════════════════════════
2871
# Task Status
2972
# ═══════════════════════════════════════════════════════════════════════════════
@@ -184,11 +227,15 @@ def execute_task(self, task: AgentTask, progress_callback: Callable = None) -> s
184227
if isinstance(tc.arguments, dict):
185228
args = tc.arguments
186229
elif isinstance(tc.arguments, str):
187-
import json
188230
try:
189231
args = json.loads(tc.arguments) if tc.arguments.strip() else {}
190232
except json.JSONDecodeError:
191-
args = {}
233+
# Try to repair malformed JSON
234+
try:
235+
repaired = _repair_json(tc.arguments)
236+
args = json.loads(repaired)
237+
except json.JSONDecodeError:
238+
args = {}
192239
else:
193240
args = {}
194241

static-api/version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "0.0.0.31"
2+
"version": "0.0.0.32"
33
}

0 commit comments

Comments
 (0)