pygent 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 (30) hide show
  1. {pygent-0.1.13 → pygent-0.1.14}/PKG-INFO +1 -1
  2. {pygent-0.1.13 → pygent-0.1.14}/pygent/agent.py +30 -3
  3. {pygent-0.1.13 → pygent-0.1.14}/pygent/task_manager.py +18 -2
  4. {pygent-0.1.13 → pygent-0.1.14}/pygent/tools.py +11 -1
  5. {pygent-0.1.13 → pygent-0.1.14}/pygent.egg-info/PKG-INFO +1 -1
  6. {pygent-0.1.13 → pygent-0.1.14}/pyproject.toml +1 -1
  7. {pygent-0.1.13 → pygent-0.1.14}/tests/test_tasks.py +17 -1
  8. {pygent-0.1.13 → pygent-0.1.14}/LICENSE +0 -0
  9. {pygent-0.1.13 → pygent-0.1.14}/README.md +0 -0
  10. {pygent-0.1.13 → pygent-0.1.14}/pygent/__init__.py +0 -0
  11. {pygent-0.1.13 → pygent-0.1.14}/pygent/__main__.py +0 -0
  12. {pygent-0.1.13 → pygent-0.1.14}/pygent/cli.py +0 -0
  13. {pygent-0.1.13 → pygent-0.1.14}/pygent/errors.py +0 -0
  14. {pygent-0.1.13 → pygent-0.1.14}/pygent/models.py +0 -0
  15. {pygent-0.1.13 → pygent-0.1.14}/pygent/openai_compat.py +0 -0
  16. {pygent-0.1.13 → pygent-0.1.14}/pygent/py.typed +0 -0
  17. {pygent-0.1.13 → pygent-0.1.14}/pygent/runtime.py +0 -0
  18. {pygent-0.1.13 → pygent-0.1.14}/pygent/ui.py +0 -0
  19. {pygent-0.1.13 → pygent-0.1.14}/pygent.egg-info/SOURCES.txt +0 -0
  20. {pygent-0.1.13 → pygent-0.1.14}/pygent.egg-info/dependency_links.txt +0 -0
  21. {pygent-0.1.13 → pygent-0.1.14}/pygent.egg-info/entry_points.txt +0 -0
  22. {pygent-0.1.13 → pygent-0.1.14}/pygent.egg-info/requires.txt +0 -0
  23. {pygent-0.1.13 → pygent-0.1.14}/pygent.egg-info/top_level.txt +0 -0
  24. {pygent-0.1.13 → pygent-0.1.14}/setup.cfg +0 -0
  25. {pygent-0.1.13 → pygent-0.1.14}/tests/test_autorun.py +0 -0
  26. {pygent-0.1.13 → pygent-0.1.14}/tests/test_custom_model.py +0 -0
  27. {pygent-0.1.13 → pygent-0.1.14}/tests/test_error_handling.py +0 -0
  28. {pygent-0.1.13 → pygent-0.1.14}/tests/test_runtime.py +0 -0
  29. {pygent-0.1.13 → pygent-0.1.14}/tests/test_tools.py +0 -0
  30. {pygent-0.1.13 → pygent-0.1.14}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygent
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Pygent is a minimalist coding assistant that runs commands in a Docker container when available and falls back to local execution. See https://marianochaves.github.io/pygent for documentation and https://github.com/marianochaves/pygent for the source code.
5
5
  Author-email: Mariano Chaves <mchaves.software@gmail.com>
6
6
  Project-URL: Documentation, https://marianochaves.github.io/pygent
@@ -44,7 +44,10 @@ class Agent:
44
44
  self.history.append({"role": "system", "content": self.system_msg})
45
45
 
46
46
  def step(self, user_msg: str):
47
+ """Execute one round of interaction with the model."""
48
+
47
49
  self.history.append({"role": "user", "content": user_msg})
50
+
48
51
  assistant_msg = self.model.chat(
49
52
  self.history, self.model_name, tools.TOOL_SCHEMAS
50
53
  )
@@ -60,12 +63,36 @@ class Agent:
60
63
  console.print(Panel(markdown_response, title="Resposta do Agente", title_align="left", border_style="cyan"))
61
64
  return assistant_msg
62
65
 
63
- def run_until_stop(self, user_msg: str, max_steps: int = 10) -> None:
64
- """Run steps automatically until the model calls the ``stop`` tool or
65
- the step limit is reached."""
66
+ def run_until_stop(
67
+ self,
68
+ user_msg: str,
69
+ max_steps: int = 20,
70
+ step_timeout: float | None = None,
71
+ max_time: float | None = None,
72
+ ) -> None:
73
+ """Run steps until ``stop`` is called or limits are reached."""
74
+
75
+ if step_timeout is None:
76
+ env = os.getenv("PYGENT_STEP_TIMEOUT")
77
+ step_timeout = float(env) if env else None
78
+ if max_time is None:
79
+ env = os.getenv("PYGENT_TASK_TIMEOUT")
80
+ max_time = float(env) if env else None
81
+
66
82
  msg = user_msg
83
+ start = time.monotonic()
84
+ self._timed_out = False
67
85
  for _ in range(max_steps):
86
+ if max_time is not None and time.monotonic() - start > max_time:
87
+ self.history.append({"role": "system", "content": f"[timeout after {max_time}s]"})
88
+ self._timed_out = True
89
+ break
90
+ step_start = time.monotonic()
68
91
  assistant_msg = self.step(msg)
92
+ if step_timeout is not None and time.monotonic() - step_start > step_timeout:
93
+ self.history.append({"role": "system", "content": f"[timeout after {step_timeout}s]"})
94
+ self._timed_out = True
95
+ break
69
96
  calls = assistant_msg.tool_calls or []
70
97
  if any(c.function.name in ("stop", "continue") for c in calls):
71
98
  break
@@ -47,6 +47,8 @@ class TaskManager:
47
47
  parent_rt: Runtime,
48
48
  files: list[str] | None = None,
49
49
  parent_depth: int = 0,
50
+ step_timeout: float | None = None,
51
+ task_timeout: float | None = None,
50
52
  ) -> str:
51
53
  """Create a new agent and run ``prompt`` asynchronously."""
52
54
 
@@ -58,6 +60,13 @@ class TaskManager:
58
60
  if active >= self.max_tasks:
59
61
  raise RuntimeError(f"max {self.max_tasks} tasks reached")
60
62
 
63
+ if step_timeout is None:
64
+ env = os.getenv("PYGENT_STEP_TIMEOUT")
65
+ step_timeout = float(env) if env else 60*5 # default 5 minutes
66
+ if task_timeout is None:
67
+ env = os.getenv("PYGENT_TASK_TIMEOUT")
68
+ task_timeout = float(env) if env else 60*20 # default 20 minutes
69
+
61
70
  agent = self.agent_factory()
62
71
  setattr(agent.runtime, "task_depth", parent_depth + 1)
63
72
  if files:
@@ -74,8 +83,15 @@ class TaskManager:
74
83
 
75
84
  def run() -> None:
76
85
  try:
77
- agent.run_until_stop(prompt)
78
- task.status = "finished"
86
+ agent.run_until_stop(
87
+ prompt,
88
+ step_timeout=step_timeout,
89
+ max_time=task_timeout,
90
+ )
91
+ if getattr(agent, "_timed_out", False):
92
+ task.status = f"timeout after {task_timeout}s"
93
+ else:
94
+ task.status = "finished"
79
95
  except Exception as exc: # pragma: no cover - error propagation
80
96
  task.status = f"error: {exc}"
81
97
 
@@ -122,11 +122,19 @@ def _continue(rt: Runtime) -> str: # pragma: no cover - side-effect free
122
122
  "items": {"type": "string"},
123
123
  "description": "Files to copy to the sub-agent before starting",
124
124
  },
125
+ "timeout": {"type": "number", "description": "Max seconds for the task"},
126
+ "step_timeout": {"type": "number", "description": "Time limit per step"},
125
127
  },
126
128
  "required": ["prompt"],
127
129
  },
128
130
  )
129
- def _delegate_task(rt: Runtime, prompt: str, files: list[str] | None = None) -> str:
131
+ def _delegate_task(
132
+ rt: Runtime,
133
+ prompt: str,
134
+ files: list[str] | None = None,
135
+ timeout: float | None = None,
136
+ step_timeout: float | None = None,
137
+ ) -> str:
130
138
  if getattr(rt, "task_depth", 0) >= 1:
131
139
  return "error: delegation not allowed in sub-tasks"
132
140
  try:
@@ -135,6 +143,8 @@ def _delegate_task(rt: Runtime, prompt: str, files: list[str] | None = None) ->
135
143
  parent_rt=rt,
136
144
  files=files,
137
145
  parent_depth=getattr(rt, "task_depth", 0),
146
+ step_timeout=step_timeout,
147
+ task_timeout=timeout,
138
148
  )
139
149
  except RuntimeError as exc:
140
150
  return str(exc)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygent
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Pygent is a minimalist coding assistant that runs commands in a Docker container when available and falls back to local execution. See https://marianochaves.github.io/pygent for documentation and https://github.com/marianochaves/pygent for the source code.
5
5
  Author-email: Mariano Chaves <mchaves.software@gmail.com>
6
6
  Project-URL: Documentation, https://marianochaves.github.io/pygent
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pygent"
3
- version = "0.1.13"
3
+ version = "0.1.14"
4
4
  description = "Pygent is a minimalist coding assistant that runs commands in a Docker container when available and falls back to local execution. See https://marianochaves.github.io/pygent for documentation and https://github.com/marianochaves/pygent for the source code."
5
5
  readme = "README.md"
6
6
  authors = [ { name = "Mariano Chaves", email = "mchaves.software@gmail.com" } ]
@@ -184,4 +184,20 @@ def test_task_limit():
184
184
  second = tools._delegate_task(Runtime(use_docker=False), prompt='run')
185
185
  assert 'max' in second
186
186
 
187
- tm.tasks[tid].thread.join()
187
+ tm.tasks[tid].thread.join()
188
+
189
+
190
+ def test_step_timeout():
191
+ ag = make_slow_agent()
192
+ ag.run_until_stop('run', step_timeout=0.05, max_steps=1)
193
+ assert 'timeout' in ag.history[-1]["content"]
194
+
195
+
196
+ def test_task_timeout():
197
+ tm = TaskManager(agent_factory=make_slow_agent, max_tasks=1)
198
+ tools._task_manager = tm
199
+ rt = Runtime(use_docker=False)
200
+ tid = tm.start_task('run', rt, task_timeout=0.05, step_timeout=0.01)
201
+ tm.tasks[tid].thread.join()
202
+ assert 'timeout' in tm.status(tid)
203
+
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes