pygent 0.1.5__tar.gz → 0.1.7__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.
- {pygent-0.1.5 → pygent-0.1.7}/PKG-INFO +2 -3
- {pygent-0.1.5 → pygent-0.1.7}/README.md +1 -2
- {pygent-0.1.5 → pygent-0.1.7}/pygent/agent.py +13 -1
- {pygent-0.1.5 → pygent-0.1.7}/pygent/runtime.py +2 -2
- {pygent-0.1.5 → pygent-0.1.7}/pygent/tools.py +10 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent.egg-info/PKG-INFO +2 -3
- {pygent-0.1.5 → pygent-0.1.7}/pygent.egg-info/SOURCES.txt +1 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent.egg-info/requires.txt +1 -3
- {pygent-0.1.5 → pygent-0.1.7}/pyproject.toml +3 -3
- pygent-0.1.7/tests/test_autorun.py +69 -0
- {pygent-0.1.5 → pygent-0.1.7}/LICENSE +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent/__init__.py +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent/__main__.py +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent/cli.py +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent/models.py +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent/openai_compat.py +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent/py.typed +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent/ui.py +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent.egg-info/dependency_links.txt +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent.egg-info/entry_points.txt +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/pygent.egg-info/top_level.txt +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/setup.cfg +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/tests/test_custom_model.py +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/tests/test_tools.py +0 -0
- {pygent-0.1.5 → pygent-0.1.7}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pygent
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.7
|
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
|
@@ -8,8 +8,7 @@ Project-URL: Repository, https://github.com/marianochaves/pygent
|
|
8
8
|
Requires-Python: >=3.9
|
9
9
|
License-File: LICENSE
|
10
10
|
Requires-Dist: rich>=13.7.0
|
11
|
-
|
12
|
-
Requires-Dist: openai>=1.0.0; extra == "llm"
|
11
|
+
Requires-Dist: openai>=1.0.0
|
13
12
|
Provides-Extra: test
|
14
13
|
Requires-Dist: pytest; extra == "test"
|
15
14
|
Provides-Extra: docs
|
@@ -18,8 +18,7 @@ Installing from source is recommended:
|
|
18
18
|
pip install -e .
|
19
19
|
```
|
20
20
|
|
21
|
-
Python ≥ 3.9 is required. The
|
22
|
-
Install any OpenAI-compatible library such as `openai` or `litellm` separately to enable model access.
|
21
|
+
Python ≥ 3.9 is required. The package now bundles the `openai` client for model access.
|
23
22
|
To run commands in Docker containers also install `pygent[docker]`.
|
24
23
|
|
25
24
|
## Configuration
|
@@ -35,7 +35,7 @@ class Agent:
|
|
35
35
|
{"role": "system", "content": SYSTEM_MSG}
|
36
36
|
])
|
37
37
|
|
38
|
-
def step(self, user_msg: str)
|
38
|
+
def step(self, user_msg: str):
|
39
39
|
self.history.append({"role": "user", "content": user_msg})
|
40
40
|
assistant_msg = self.model.chat(self.history, self.model_name, TOOL_SCHEMAS)
|
41
41
|
self.history.append(assistant_msg)
|
@@ -47,6 +47,18 @@ class Agent:
|
|
47
47
|
console.print(Panel(output, title=f"tool:{call.function.name}"))
|
48
48
|
else:
|
49
49
|
console.print(assistant_msg.content)
|
50
|
+
return assistant_msg
|
51
|
+
|
52
|
+
def run_until_stop(self, user_msg: str, max_steps: int = 10) -> None:
|
53
|
+
"""Run steps automatically until the model calls the ``stop`` tool or
|
54
|
+
the step limit is reached."""
|
55
|
+
msg = user_msg
|
56
|
+
for _ in range(max_steps):
|
57
|
+
assistant_msg = self.step(msg)
|
58
|
+
calls = assistant_msg.tool_calls or []
|
59
|
+
if any(c.function.name == "stop" for c in calls):
|
60
|
+
break
|
61
|
+
msg = "continue"
|
50
62
|
|
51
63
|
|
52
64
|
def run_interactive(use_docker: bool | None = None) -> None: # pragma: no cover
|
@@ -71,8 +71,8 @@ class Runtime:
|
|
71
71
|
)
|
72
72
|
return proc.stdout + proc.stderr
|
73
73
|
|
74
|
-
def write_file(self,
|
75
|
-
p = self.base_dir /
|
74
|
+
def write_file(self, path: Union[str, Path], content: str) -> str:
|
75
|
+
p = self.base_dir / path
|
76
76
|
p.parent.mkdir(parents=True, exist_ok=True)
|
77
77
|
p.write_text(content)
|
78
78
|
return f"Wrote {p.relative_to(self.base_dir)}"
|
@@ -35,6 +35,14 @@ TOOL_SCHEMAS = [
|
|
35
35
|
},
|
36
36
|
},
|
37
37
|
},
|
38
|
+
{
|
39
|
+
"type": "function",
|
40
|
+
"function": {
|
41
|
+
"name": "stop",
|
42
|
+
"description": "Stop the autonomous loop.",
|
43
|
+
"parameters": {"type": "object", "properties": {}},
|
44
|
+
},
|
45
|
+
},
|
38
46
|
]
|
39
47
|
|
40
48
|
# --------------- dispatcher ---------------
|
@@ -47,4 +55,6 @@ def execute_tool(call: Any, rt: Runtime) -> str: # pragma: no cover, Any→open
|
|
47
55
|
return rt.bash(**args)
|
48
56
|
if name == "write_file":
|
49
57
|
return rt.write_file(**args)
|
58
|
+
if name == "stop":
|
59
|
+
return "Stopping."
|
50
60
|
return f"⚠️ unknown tool {name}"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pygent
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.7
|
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
|
@@ -8,8 +8,7 @@ Project-URL: Repository, https://github.com/marianochaves/pygent
|
|
8
8
|
Requires-Python: >=3.9
|
9
9
|
License-File: LICENSE
|
10
10
|
Requires-Dist: rich>=13.7.0
|
11
|
-
|
12
|
-
Requires-Dist: openai>=1.0.0; extra == "llm"
|
11
|
+
Requires-Dist: openai>=1.0.0
|
13
12
|
Provides-Extra: test
|
14
13
|
Requires-Dist: pytest; extra == "test"
|
15
14
|
Provides-Extra: docs
|
@@ -1,15 +1,15 @@
|
|
1
1
|
[project]
|
2
2
|
name = "pygent"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.7"
|
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
|
authors = [ { name = "Mariano Chaves", email = "mchaves.software@gmail.com" } ]
|
6
6
|
requires-python = ">=3.9"
|
7
7
|
dependencies = [
|
8
|
-
"rich>=13.7.0" # colored output (optional)
|
8
|
+
"rich>=13.7.0", # colored output (optional)
|
9
|
+
"openai>=1.0.0",
|
9
10
|
]
|
10
11
|
|
11
12
|
[project.optional-dependencies]
|
12
|
-
llm = ["openai>=1.0.0"] # OpenAI-compatible library (optional)
|
13
13
|
test = ["pytest"]
|
14
14
|
docs = ["mkdocs"]
|
15
15
|
docker = ["docker>=7.0.0"]
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import os
|
2
|
+
import sys
|
3
|
+
import types
|
4
|
+
|
5
|
+
sys.modules.setdefault('openai', types.ModuleType('openai'))
|
6
|
+
sys.modules.setdefault('docker', types.ModuleType('docker'))
|
7
|
+
rich_mod = types.ModuleType('rich')
|
8
|
+
console_mod = types.ModuleType('console')
|
9
|
+
console_mod.Console = lambda *a, **k: type('C', (), {'print': lambda *a, **k: None})()
|
10
|
+
panel_mod = types.ModuleType('panel')
|
11
|
+
panel_mod.Panel = lambda *a, **k: None
|
12
|
+
sys.modules.setdefault('rich', rich_mod)
|
13
|
+
sys.modules.setdefault('rich.console', console_mod)
|
14
|
+
sys.modules.setdefault('rich.panel', panel_mod)
|
15
|
+
|
16
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
17
|
+
|
18
|
+
from pygent import Agent, openai_compat
|
19
|
+
|
20
|
+
class DummyModel:
|
21
|
+
def __init__(self):
|
22
|
+
self.count = 0
|
23
|
+
def chat(self, messages, model, tools):
|
24
|
+
self.count += 1
|
25
|
+
if self.count == 1:
|
26
|
+
return openai_compat.Message(
|
27
|
+
role='assistant',
|
28
|
+
content=None,
|
29
|
+
tool_calls=[
|
30
|
+
openai_compat.ToolCall(
|
31
|
+
id='1',
|
32
|
+
type='function',
|
33
|
+
function=openai_compat.ToolCallFunction(
|
34
|
+
name='bash',
|
35
|
+
arguments='{"cmd": "ls"}'
|
36
|
+
)
|
37
|
+
)
|
38
|
+
]
|
39
|
+
)
|
40
|
+
else:
|
41
|
+
return openai_compat.Message(
|
42
|
+
role='assistant',
|
43
|
+
content=None,
|
44
|
+
tool_calls=[
|
45
|
+
openai_compat.ToolCall(
|
46
|
+
id='2',
|
47
|
+
type='function',
|
48
|
+
function=openai_compat.ToolCallFunction(
|
49
|
+
name='stop',
|
50
|
+
arguments='{}'
|
51
|
+
)
|
52
|
+
)
|
53
|
+
]
|
54
|
+
)
|
55
|
+
|
56
|
+
class DummyRuntime:
|
57
|
+
def bash(self, cmd: str):
|
58
|
+
return f"ran {cmd}"
|
59
|
+
def write_file(self, path: str, content: str):
|
60
|
+
return f"wrote {path}"
|
61
|
+
|
62
|
+
|
63
|
+
def test_run_until_stop():
|
64
|
+
ag = Agent(runtime=DummyRuntime(), model=DummyModel())
|
65
|
+
ag.run_until_stop('start', max_steps=5)
|
66
|
+
assert any(call.function.name == 'stop'
|
67
|
+
for msg in ag.history
|
68
|
+
if hasattr(msg, 'tool_calls') and msg.tool_calls
|
69
|
+
for call in msg.tool_calls)
|
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
|