strix-agent 0.1.13__tar.gz → 0.1.14__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. {strix_agent-0.1.13 → strix_agent-0.1.14}/PKG-INFO +1 -1
  2. {strix_agent-0.1.13 → strix_agent-0.1.14}/pyproject.toml +1 -1
  3. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/agents/base_agent.py +33 -6
  4. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/agents/state.py +4 -1
  5. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/app.py +10 -3
  6. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/browser_renderer.py +13 -0
  7. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/file_edit_renderer.py +4 -0
  8. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/llm/__init__.py +4 -1
  9. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/llm/llm.py +8 -9
  10. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/llm/memory_compressor.py +4 -1
  11. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/llm/request_queue.py +2 -2
  12. {strix_agent-0.1.13 → strix_agent-0.1.14}/LICENSE +0 -0
  13. {strix_agent-0.1.13 → strix_agent-0.1.14}/README.md +0 -0
  14. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/__init__.py +0 -0
  15. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/agents/StrixAgent/__init__.py +0 -0
  16. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/agents/StrixAgent/strix_agent.py +0 -0
  17. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/agents/StrixAgent/system_prompt.jinja +0 -0
  18. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/agents/__init__.py +0 -0
  19. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/__init__.py +0 -0
  20. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/assets/cli.tcss +0 -0
  21. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/main.py +0 -0
  22. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/__init__.py +0 -0
  23. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/agents_graph_renderer.py +0 -0
  24. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/base_renderer.py +0 -0
  25. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/finish_renderer.py +0 -0
  26. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/notes_renderer.py +0 -0
  27. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/proxy_renderer.py +0 -0
  28. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/python_renderer.py +0 -0
  29. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/registry.py +0 -0
  30. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/reporting_renderer.py +0 -0
  31. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/scan_info_renderer.py +0 -0
  32. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/terminal_renderer.py +0 -0
  33. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/thinking_renderer.py +0 -0
  34. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/user_message_renderer.py +0 -0
  35. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tool_components/web_search_renderer.py +0 -0
  36. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/cli/tracer.py +0 -0
  37. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/llm/config.py +0 -0
  38. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/llm/utils.py +0 -0
  39. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/__init__.py +0 -0
  40. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/coordination/root_agent.jinja +0 -0
  41. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/authentication_jwt.jinja +0 -0
  42. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/business_logic.jinja +0 -0
  43. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/csrf.jinja +0 -0
  44. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/idor.jinja +0 -0
  45. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/race_conditions.jinja +0 -0
  46. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/rce.jinja +0 -0
  47. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/sql_injection.jinja +0 -0
  48. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/ssrf.jinja +0 -0
  49. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/xss.jinja +0 -0
  50. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/prompts/vulnerabilities/xxe.jinja +0 -0
  51. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/runtime/__init__.py +0 -0
  52. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/runtime/docker_runtime.py +0 -0
  53. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/runtime/runtime.py +0 -0
  54. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/runtime/tool_server.py +0 -0
  55. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/__init__.py +0 -0
  56. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/agents_graph/__init__.py +0 -0
  57. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/agents_graph/agents_graph_actions.py +0 -0
  58. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/agents_graph/agents_graph_actions_schema.xml +0 -0
  59. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/argument_parser.py +0 -0
  60. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/browser/__init__.py +0 -0
  61. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/browser/browser_actions.py +0 -0
  62. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/browser/browser_actions_schema.xml +0 -0
  63. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/browser/browser_instance.py +0 -0
  64. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/browser/tab_manager.py +0 -0
  65. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/executor.py +0 -0
  66. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/file_edit/__init__.py +0 -0
  67. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/file_edit/file_edit_actions.py +0 -0
  68. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/file_edit/file_edit_actions_schema.xml +0 -0
  69. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/finish/__init__.py +0 -0
  70. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/finish/finish_actions.py +0 -0
  71. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/finish/finish_actions_schema.xml +0 -0
  72. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/notes/__init__.py +0 -0
  73. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/notes/notes_actions.py +0 -0
  74. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/notes/notes_actions_schema.xml +0 -0
  75. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/proxy/__init__.py +0 -0
  76. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/proxy/proxy_actions.py +0 -0
  77. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/proxy/proxy_actions_schema.xml +0 -0
  78. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/proxy/proxy_manager.py +0 -0
  79. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/python/__init__.py +0 -0
  80. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/python/python_actions.py +0 -0
  81. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/python/python_actions_schema.xml +0 -0
  82. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/python/python_instance.py +0 -0
  83. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/python/python_manager.py +0 -0
  84. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/registry.py +0 -0
  85. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/reporting/__init__.py +0 -0
  86. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/reporting/reporting_actions.py +0 -0
  87. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/reporting/reporting_actions_schema.xml +0 -0
  88. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/terminal/__init__.py +0 -0
  89. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/terminal/terminal_actions.py +0 -0
  90. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/terminal/terminal_actions_schema.xml +0 -0
  91. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/terminal/terminal_manager.py +0 -0
  92. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/terminal/terminal_session.py +0 -0
  93. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/thinking/__init__.py +0 -0
  94. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/thinking/thinking_actions.py +0 -0
  95. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/thinking/thinking_actions_schema.xml +0 -0
  96. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/web_search/__init__.py +0 -0
  97. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/web_search/web_search_actions.py +0 -0
  98. {strix_agent-0.1.13 → strix_agent-0.1.14}/strix/tools/web_search/web_search_actions_schema.xml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: strix-agent
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Open-source AI Hackers for your apps
5
5
  License: Apache-2.0
6
6
  Keywords: cybersecurity,security,vulnerability,scanner,pentest,agent,ai,cli
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "strix-agent"
3
- version = "0.1.13"
3
+ version = "0.1.14"
4
4
  description = "Open-source AI Hackers for your apps"
5
5
  authors = ["Strix <hi@usestrix.com>"]
6
6
  readme = "README.md"
@@ -13,7 +13,7 @@ from jinja2 import (
13
13
  select_autoescape,
14
14
  )
15
15
 
16
- from strix.llm import LLM, LLMConfig
16
+ from strix.llm import LLM, LLMConfig, LLMRequestFailedError
17
17
  from strix.llm.utils import clean_content
18
18
  from strix.tools import process_tool_invocations
19
19
 
@@ -164,6 +164,10 @@ class BaseAgent(metaclass=AgentMeta):
164
164
  await self._enter_waiting_state(tracer)
165
165
  continue
166
166
 
167
+ if self.state.llm_failed:
168
+ await self._wait_for_input()
169
+ continue
170
+
167
171
  self.state.increment_iteration()
168
172
 
169
173
  try:
@@ -176,6 +180,13 @@ class BaseAgent(metaclass=AgentMeta):
176
180
  await self._enter_waiting_state(tracer, error_occurred=False, was_cancelled=True)
177
181
  continue
178
182
 
183
+ except LLMRequestFailedError as e:
184
+ self.state.add_error(f"LLM request failed: {e}")
185
+ self.state.enter_waiting_state(llm_failed=True)
186
+ if tracer:
187
+ tracer.update_agent_status(self.state.agent_id, "llm_failed")
188
+ continue
189
+
179
190
  except (RuntimeError, ValueError, TypeError) as e:
180
191
  if not await self._handle_iteration_error(e, tracer):
181
192
  await self._enter_waiting_state(tracer, error_occurred=True)
@@ -327,7 +338,7 @@ class BaseAgent(metaclass=AgentMeta):
327
338
  tracer.update_agent_status(self.state.agent_id, "error")
328
339
  return True
329
340
 
330
- def _check_agent_messages(self, state: AgentState) -> None:
341
+ def _check_agent_messages(self, state: AgentState) -> None: # noqa: PLR0912
331
342
  try:
332
343
  from strix.tools.agents_graph.agents_graph_actions import _agent_graph, _agent_messages
333
344
 
@@ -340,12 +351,28 @@ class BaseAgent(metaclass=AgentMeta):
340
351
  has_new_messages = False
341
352
  for message in messages:
342
353
  if not message.get("read", False):
354
+ sender_id = message.get("from")
355
+
343
356
  if state.is_waiting_for_input():
344
- state.resume_from_waiting()
345
- has_new_messages = True
357
+ if state.llm_failed:
358
+ if sender_id == "user":
359
+ state.resume_from_waiting()
360
+ has_new_messages = True
346
361
 
347
- sender_name = "Unknown Agent"
348
- sender_id = message.get("from")
362
+ from strix.cli.tracer import get_global_tracer
363
+
364
+ tracer = get_global_tracer()
365
+ if tracer:
366
+ tracer.update_agent_status(state.agent_id, "running")
367
+ else:
368
+ state.resume_from_waiting()
369
+ has_new_messages = True
370
+
371
+ from strix.cli.tracer import get_global_tracer
372
+
373
+ tracer = get_global_tracer()
374
+ if tracer:
375
+ tracer.update_agent_status(state.agent_id, "running")
349
376
 
350
377
  if sender_id == "user":
351
378
  sender_name = "User"
@@ -23,6 +23,7 @@ class AgentState(BaseModel):
23
23
  completed: bool = False
24
24
  stop_requested: bool = False
25
25
  waiting_for_input: bool = False
26
+ llm_failed: bool = False
26
27
  final_result: dict[str, Any] | None = None
27
28
 
28
29
  messages: list[dict[str, Any]] = Field(default_factory=list)
@@ -85,15 +86,17 @@ class AgentState(BaseModel):
85
86
  def is_waiting_for_input(self) -> bool:
86
87
  return self.waiting_for_input
87
88
 
88
- def enter_waiting_state(self) -> None:
89
+ def enter_waiting_state(self, llm_failed: bool = False) -> None:
89
90
  self.waiting_for_input = True
90
91
  self.stop_requested = False
92
+ self.llm_failed = llm_failed
91
93
  self.last_updated = datetime.now(UTC).isoformat()
92
94
 
93
95
  def resume_from_waiting(self, new_task: str | None = None) -> None:
94
96
  self.waiting_for_input = False
95
97
  self.stop_requested = False
96
98
  self.completed = False
99
+ self.llm_failed = False
97
100
  if new_task:
98
101
  self.task = new_task
99
102
  self.last_updated = datetime.now(UTC).isoformat()
@@ -420,6 +420,7 @@ class StrixCLIApp(App): # type: ignore[misc]
420
420
  "failed": "❌",
421
421
  "stopped": "⏹️",
422
422
  "stopping": "⏸️",
423
+ "llm_failed": "🔴",
423
424
  }
424
425
 
425
426
  status_icon = status_indicators.get(status, "🔵")
@@ -544,6 +545,12 @@ class StrixCLIApp(App): # type: ignore[misc]
544
545
  self._safe_widget_operation(status_text.update, "Agent completed")
545
546
  self._safe_widget_operation(keymap_indicator.update, "")
546
547
  self._safe_widget_operation(status_display.remove_class, "hidden")
548
+ elif status == "llm_failed":
549
+ self._safe_widget_operation(status_text.update, "[red]LLM request failed[/red]")
550
+ self._safe_widget_operation(
551
+ keymap_indicator.update, "[dim]Send message to retry[/dim]"
552
+ )
553
+ self._safe_widget_operation(status_display.remove_class, "hidden")
547
554
  elif status == "waiting":
548
555
  animated_text = self._get_animated_waiting_text(self.selected_agent_id)
549
556
  self._safe_widget_operation(status_text.update, animated_text)
@@ -626,7 +633,7 @@ class StrixCLIApp(App): # type: ignore[misc]
626
633
 
627
634
  for agent_id, agent_data in self.tracer.agents.items():
628
635
  status = agent_data.get("status", "running")
629
- if status in ["running", "waiting"]:
636
+ if status in ["running", "waiting", "llm_failed"]:
630
637
  has_active_agents = True
631
638
  current_dots = self._agent_dot_states.get(agent_id, 0)
632
639
  self._agent_dot_states[agent_id] = (current_dots + 1) % 4
@@ -637,7 +644,7 @@ class StrixCLIApp(App): # type: ignore[misc]
637
644
  and self.selected_agent_id in self.tracer.agents
638
645
  ):
639
646
  selected_status = self.tracer.agents[self.selected_agent_id].get("status", "running")
640
- if selected_status in ["running", "waiting"]:
647
+ if selected_status in ["running", "waiting", "llm_failed"]:
641
648
  self._update_agent_status_display()
642
649
 
643
650
  if not has_active_agents:
@@ -645,7 +652,7 @@ class StrixCLIApp(App): # type: ignore[misc]
645
652
  for agent_id in list(self._agent_dot_states.keys()):
646
653
  if agent_id not in self.tracer.agents or self.tracer.agents[agent_id].get(
647
654
  "status"
648
- ) not in ["running", "waiting"]:
655
+ ) not in ["running", "waiting", "llm_failed"]:
649
656
  del self._agent_dot_states[agent_id]
650
657
 
651
658
  def _gather_agent_events(self, agent_id: str) -> list[dict[str, Any]]:
@@ -30,6 +30,8 @@ class BrowserRenderer(BaseToolRenderer):
30
30
  url = args.get("url")
31
31
  text = args.get("text")
32
32
  js_code = args.get("js_code")
33
+ key = args.get("key")
34
+ file_path = args.get("file_path")
33
35
 
34
36
  if action in [
35
37
  "launch",
@@ -40,6 +42,8 @@ class BrowserRenderer(BaseToolRenderer):
40
42
  "click",
41
43
  "double_click",
42
44
  "hover",
45
+ "press_key",
46
+ "save_pdf",
43
47
  ]:
44
48
  if action == "launch":
45
49
  display_url = cls._format_url(url) if url else None
@@ -60,6 +64,12 @@ class BrowserRenderer(BaseToolRenderer):
60
64
  message = (
61
65
  f"executing javascript\n{display_js}" if display_js else "executing javascript"
62
66
  )
67
+ elif action == "press_key":
68
+ display_key = cls.escape_markup(key) if key else None
69
+ message = f"pressing key {display_key}" if display_key else "pressing key"
70
+ elif action == "save_pdf":
71
+ display_path = cls.escape_markup(file_path) if file_path else None
72
+ message = f"saving PDF to {display_path}" if display_path else "saving PDF"
63
73
  else:
64
74
  action_words = {
65
75
  "click": "clicking",
@@ -73,11 +83,14 @@ class BrowserRenderer(BaseToolRenderer):
73
83
  simple_actions = {
74
84
  "back": "going back in browser history",
75
85
  "forward": "going forward in browser history",
86
+ "scroll_down": "scrolling down",
87
+ "scroll_up": "scrolling up",
76
88
  "refresh": "refreshing browser tab",
77
89
  "close_tab": "closing browser tab",
78
90
  "switch_tab": "switching browser tab",
79
91
  "list_tabs": "listing browser tabs",
80
92
  "view_source": "viewing page source",
93
+ "get_console_logs": "getting console logs",
81
94
  "screenshot": "taking screenshot of browser tab",
82
95
  "wait": "waiting...",
83
96
  "close": "closing browser",
@@ -25,6 +25,10 @@ class StrReplaceEditorRenderer(BaseToolRenderer):
25
25
  header = "✏️ [bold #10b981]Editing file[/]"
26
26
  elif command == "create":
27
27
  header = "📝 [bold #10b981]Creating file[/]"
28
+ elif command == "insert":
29
+ header = "✏️ [bold #10b981]Inserting text[/]"
30
+ elif command == "undo_edit":
31
+ header = "↩️ [bold #10b981]Undoing edit[/]"
28
32
  else:
29
33
  header = "📄 [bold #10b981]File operation[/]"
30
34
 
@@ -1,12 +1,15 @@
1
1
  import litellm
2
2
 
3
3
  from .config import LLMConfig
4
- from .llm import LLM
4
+ from .llm import LLM, LLMRequestFailedError
5
5
 
6
6
 
7
7
  __all__ = [
8
8
  "LLM",
9
9
  "LLMConfig",
10
+ "LLMRequestFailedError",
10
11
  ]
11
12
 
13
+ litellm._logging._disable_debugging()
14
+
12
15
  litellm.drop_params = True
@@ -28,6 +28,11 @@ api_key = os.getenv("LLM_API_KEY")
28
28
  if api_key:
29
29
  litellm.api_key = api_key
30
30
 
31
+
32
+ class LLMRequestFailedError(Exception):
33
+ """Raised when LLM request fails after all retry attempts."""
34
+
35
+
31
36
  MODELS_WITHOUT_STOP_WORDS = [
32
37
  "gpt-5",
33
38
  "gpt-5-mini",
@@ -250,15 +255,8 @@ class LLM:
250
255
  tool_invocations=tool_invocations if tool_invocations else None,
251
256
  )
252
257
 
253
- except (ValueError, TypeError, RuntimeError):
254
- logger.exception("Error in LLM generation")
255
- return LLMResponse(
256
- scan_id=scan_id,
257
- step_number=step_number,
258
- role=StepRole.AGENT,
259
- content="An error occurred while generating the response",
260
- tool_invocations=None,
261
- )
258
+ except Exception as e:
259
+ raise LLMRequestFailedError("LLM request failed after all retry attempts") from e
262
260
 
263
261
  @property
264
262
  def usage_stats(self) -> dict[str, dict[str, int | float]]:
@@ -307,6 +305,7 @@ class LLM:
307
305
  "model": self.config.model_name,
308
306
  "messages": messages,
309
307
  "temperature": self.config.temperature,
308
+ "timeout": 180,
310
309
  }
311
310
 
312
311
  if self._should_include_stop_param():
@@ -106,10 +106,13 @@ def _summarize_messages(
106
106
  completion_args = {
107
107
  "model": model,
108
108
  "messages": [{"role": "user", "content": prompt}],
109
+ "timeout": 180,
109
110
  }
110
111
 
111
112
  response = litellm.completion(**completion_args)
112
- summary = response.choices[0].message.content
113
+ summary = response.choices[0].message.content or ""
114
+ if not summary.strip():
115
+ return messages[0]
113
116
  summary_msg = "<context_summary message_count='{count}'>{text}</context_summary>"
114
117
  return {
115
118
  "role": "assistant",
@@ -38,8 +38,8 @@ class LLMRequestQueue:
38
38
  self._semaphore.release()
39
39
 
40
40
  @retry( # type: ignore[misc]
41
- stop=stop_after_attempt(15),
42
- wait=wait_exponential(multiplier=1.2, min=1, max=300),
41
+ stop=stop_after_attempt(5),
42
+ wait=wait_exponential(multiplier=2, min=1, max=30),
43
43
  reraise=True,
44
44
  )
45
45
  async def _reliable_request(self, completion_args: dict[str, Any]) -> ModelResponse:
File without changes
File without changes