agentic-python-coder 2.2.0__tar.gz → 2.3.0__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 (72) hide show
  1. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/.gitignore +1 -0
  2. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/PKG-INFO +31 -15
  3. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/README.md +18 -3
  4. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/prompts/system.md +3 -15
  5. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/prompts/system_todo.md +10 -22
  6. agentic_python_coder-2.3.0/coder/src/agentic_python_coder/__init__.py +74 -0
  7. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/agent.py +28 -49
  8. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/cli.py +17 -7
  9. agentic_python_coder-2.3.0/coder/src/agentic_python_coder/kernel.py +634 -0
  10. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/llm.py +4 -1
  11. agentic_python_coder-2.3.0/coder/src/agentic_python_coder/mcp_server.py +526 -0
  12. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/runner.py +1 -28
  13. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/tools.py +1 -44
  14. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/tests/test_library_api.py +21 -17
  15. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/tests/test_mcp_server.py +109 -20
  16. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/tests/test_todo_tool_availability.py +2 -0
  17. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/pyproject.toml +13 -12
  18. agentic_python_coder-2.2.0/coder/src/agentic_python_coder/__init__.py +0 -39
  19. agentic_python_coder-2.2.0/coder/src/agentic_python_coder/kernel.py +0 -343
  20. agentic_python_coder-2.2.0/coder/src/agentic_python_coder/mcp_server.py +0 -485
  21. agentic_python_coder-2.2.0/examples/cpmpy/README.md +0 -69
  22. agentic_python_coder-2.2.0/examples/cpmpy/cpmpy.md +0 -46
  23. agentic_python_coder-2.2.0/examples/cpmpy/sample_problems/magic_square.md +0 -46
  24. agentic_python_coder-2.2.0/examples/cpmpy/sample_problems/n_queens.md +0 -36
  25. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/LICENSE +0 -0
  26. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/.gitignore +0 -0
  27. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/__init__.py +0 -0
  28. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/clingo/README.md +0 -0
  29. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/clingo/clingo.md +0 -0
  30. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/bird_reasoning.md +0 -0
  31. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/diagnosis.md +0 -0
  32. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/simple_coloring.md +0 -0
  33. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/stable_marriage.md +0 -0
  34. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/sudoku_mini.md +0 -0
  35. {agentic_python_coder-2.2.0/coder-examples → agentic_python_coder-2.3.0/coder/src/agentic_python_coder/examples}/cpmpy/README.md +0 -0
  36. {agentic_python_coder-2.2.0/coder-examples → agentic_python_coder-2.3.0/coder/src/agentic_python_coder/examples}/cpmpy/cpmpy.md +0 -0
  37. {agentic_python_coder-2.2.0/coder-examples → agentic_python_coder-2.3.0/coder/src/agentic_python_coder/examples}/cpmpy/sample_tasks/magic_square.md +0 -0
  38. {agentic_python_coder-2.2.0/coder-examples → agentic_python_coder-2.3.0/coder/src/agentic_python_coder/examples}/cpmpy/sample_tasks/n_queens.md +0 -0
  39. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/regex/README.md +0 -0
  40. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/regex/regex.md +0 -0
  41. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/regex/sample_tasks/email_extraction.md +0 -0
  42. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/regex/sample_tasks/phone_validation.md +0 -0
  43. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/examples/regex/sample_tasks/url_parsing.md +0 -0
  44. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/models/deepseek31.json +0 -0
  45. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/models/gemini25.json +0 -0
  46. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/models/gpt5.json +0 -0
  47. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/models/grok41.json +0 -0
  48. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/models/opus45.json +0 -0
  49. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/models/qwen3.json +0 -0
  50. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/models/sonnet45.json +0 -0
  51. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/src/agentic_python_coder/project_md.py +0 -0
  52. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/tests/test_kernel.py +0 -0
  53. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/coder/tests/test_todo_flag_integration.py +0 -0
  54. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/clingo/README.md +0 -0
  55. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/clingo/clingo.md +0 -0
  56. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/clingo/sample_tasks/bird_reasoning.md +0 -0
  57. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/clingo/sample_tasks/diagnosis.md +0 -0
  58. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/clingo/sample_tasks/simple_coloring.md +0 -0
  59. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/clingo/sample_tasks/stable_marriage.md +0 -0
  60. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/clingo/sample_tasks/sudoku_mini.md +0 -0
  61. {agentic_python_coder-2.2.0/coder/src/agentic_python_coder → agentic_python_coder-2.3.0}/examples/cpmpy/README.md +0 -0
  62. {agentic_python_coder-2.2.0/coder/src/agentic_python_coder → agentic_python_coder-2.3.0}/examples/cpmpy/cpmpy.md +0 -0
  63. {agentic_python_coder-2.2.0/coder/src/agentic_python_coder/examples/cpmpy/sample_tasks → agentic_python_coder-2.3.0/examples/cpmpy/sample_problems}/magic_square.md +0 -0
  64. {agentic_python_coder-2.2.0/coder/src/agentic_python_coder/examples/cpmpy/sample_tasks → agentic_python_coder-2.3.0/examples/cpmpy/sample_problems}/n_queens.md +0 -0
  65. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/regex/README.md +0 -0
  66. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/regex/regex.md +0 -0
  67. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/regex/sample_tasks/email_extraction.md +0 -0
  68. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/regex/sample_tasks/phone_validation.md +0 -0
  69. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/regex/sample_tasks/test_email/email_extractor.py +0 -0
  70. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/regex/sample_tasks/test_email/extracted_emails.txt +0 -0
  71. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/regex/sample_tasks/test_email/text.txt +0 -0
  72. {agentic_python_coder-2.2.0 → agentic_python_coder-2.3.0}/examples/regex/sample_tasks/url_parsing.md +0 -0
@@ -152,6 +152,7 @@ cython_debug/
152
152
  uv.lock
153
153
 
154
154
  # Project specific
155
+ coder-examples/
155
156
  CLAUDE-archive.md
156
157
  PROCESS_NOTES.md
157
158
  conversation_log.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-python-coder
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: A lightweight Python coding agent that writes, executes, and iterates on code through natural language instructions
5
5
  Author: Stefan Szeider
6
6
  License: Apache-2.0
@@ -14,25 +14,26 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
15
  Classifier: Topic :: Software Development :: Code Generators
16
16
  Requires-Python: <3.14,>=3.13
17
- Requires-Dist: ipykernel>=6.30.1
18
- Requires-Dist: jupyter-client>=8.6.3
19
- Requires-Dist: langchain-anthropic>=1.2.0
20
- Requires-Dist: langchain-core>=1.1.0
21
- Requires-Dist: langchain-experimental>=0.4.0
22
- Requires-Dist: langchain-openai>=1.1.0
23
- Requires-Dist: langgraph>=1.0.4
17
+ Requires-Dist: ipykernel>=7.0.0
18
+ Requires-Dist: jupyter-client>=8.7.0
19
+ Requires-Dist: langchain-anthropic>=1.3.0
20
+ Requires-Dist: langchain-core>=1.2.0
21
+ Requires-Dist: langchain-experimental>=0.4.1
22
+ Requires-Dist: langchain-openai>=1.1.6
23
+ Requires-Dist: langchain>=1.2.0
24
+ Requires-Dist: langgraph>=1.0.5
24
25
  Requires-Dist: mcp>=1.0.0
25
26
  Requires-Dist: python-dotenv>=1.2.1
26
27
  Requires-Dist: pyyaml>=6.0.3
27
28
  Requires-Dist: rich>=14.2.0
28
29
  Provides-Extra: dev
29
- Requires-Dist: mypy>=1.19.0; extra == 'dev'
30
- Requires-Dist: ruff>=0.14.7; extra == 'dev'
30
+ Requires-Dist: mypy>=1.19.1; extra == 'dev'
31
+ Requires-Dist: ruff>=0.14.10; extra == 'dev'
31
32
  Provides-Extra: test
32
- Requires-Dist: pytest-asyncio>=1.2.0; extra == 'test'
33
+ Requires-Dist: pytest-asyncio>=1.3.0; extra == 'test'
33
34
  Requires-Dist: pytest-cov>=7.0.0; extra == 'test'
34
35
  Requires-Dist: pytest-watch>=4.2.0; extra == 'test'
35
- Requires-Dist: pytest>=9.0.1; extra == 'test'
36
+ Requires-Dist: pytest>=9.0.2; extra == 'test'
36
37
  Description-Content-Type: text/markdown
37
38
 
38
39
  # Agentic Python Coder
@@ -291,17 +292,32 @@ Add to your MCP settings (e.g., `~/.claude/claude_desktop_config.json` or projec
291
292
  | Tool | Description |
292
293
  |------|-------------|
293
294
  | `python_exec` | Execute Python code. Auto-starts session if needed. Default 30s timeout. |
294
- | `python_reset` | Clear session state. Optionally install packages (e.g., `packages=["numpy", "pandas"]`). |
295
- | `python_status` | Check if session is active, Python version, installed packages, defined variables. |
295
+ | `python_reset` | Create new kernel (no `kernel_id`) OR reset existing kernel (with `kernel_id`). Optionally install packages. |
296
+ | `python_status` | Check session state: active flag, all active kernel IDs, Python version, packages, variables. |
296
297
  | `python_interrupt` | Send interrupt signal to stop long-running code. Session state is preserved. |
297
298
 
299
+ ### Multi-Agent Workflow
300
+
301
+ For parallel agents, each agent gets its own kernel:
302
+
303
+ ```
304
+ Agent A Agent B
305
+ ──────── ────────
306
+ python_reset() → kernel_id="aaa" python_reset() → kernel_id="bbb"
307
+ python_exec(kernel_id="aaa", ...) python_exec(kernel_id="bbb", ...)
308
+ python_exec(kernel_id="aaa", ...) python_exec(kernel_id="bbb", ...)
309
+ ```
310
+
311
+ Simple single-agent use: just call `python_exec()` — the default kernel auto-starts.
312
+
298
313
  ### Features
299
314
 
300
315
  - **Persistent state**: Variables, imports, and definitions persist across executions
301
- - **Auto-start**: Session starts automatically on first `python_exec`
316
+ - **Auto-start**: Default session starts automatically on first `python_exec`
302
317
  - **Package installation**: Use `python_reset` with `packages` parameter to install dependencies
303
318
  - **Timeout handling**: Long-running code times out gracefully (session preserved)
304
319
  - **Interrupt support**: Stop runaway code without losing session state
320
+ - **Multi-kernel**: Each `python_reset()` creates an isolated kernel for parallel agents
305
321
 
306
322
  ### Usage Tips
307
323
 
@@ -254,17 +254,32 @@ Add to your MCP settings (e.g., `~/.claude/claude_desktop_config.json` or projec
254
254
  | Tool | Description |
255
255
  |------|-------------|
256
256
  | `python_exec` | Execute Python code. Auto-starts session if needed. Default 30s timeout. |
257
- | `python_reset` | Clear session state. Optionally install packages (e.g., `packages=["numpy", "pandas"]`). |
258
- | `python_status` | Check if session is active, Python version, installed packages, defined variables. |
257
+ | `python_reset` | Create new kernel (no `kernel_id`) OR reset existing kernel (with `kernel_id`). Optionally install packages. |
258
+ | `python_status` | Check session state: active flag, all active kernel IDs, Python version, packages, variables. |
259
259
  | `python_interrupt` | Send interrupt signal to stop long-running code. Session state is preserved. |
260
260
 
261
+ ### Multi-Agent Workflow
262
+
263
+ For parallel agents, each agent gets its own kernel:
264
+
265
+ ```
266
+ Agent A Agent B
267
+ ──────── ────────
268
+ python_reset() → kernel_id="aaa" python_reset() → kernel_id="bbb"
269
+ python_exec(kernel_id="aaa", ...) python_exec(kernel_id="bbb", ...)
270
+ python_exec(kernel_id="aaa", ...) python_exec(kernel_id="bbb", ...)
271
+ ```
272
+
273
+ Simple single-agent use: just call `python_exec()` — the default kernel auto-starts.
274
+
261
275
  ### Features
262
276
 
263
277
  - **Persistent state**: Variables, imports, and definitions persist across executions
264
- - **Auto-start**: Session starts automatically on first `python_exec`
278
+ - **Auto-start**: Default session starts automatically on first `python_exec`
265
279
  - **Package installation**: Use `python_reset` with `packages` parameter to install dependencies
266
280
  - **Timeout handling**: Long-running code times out gracefully (session preserved)
267
281
  - **Interrupt support**: Stop runaway code without losing session state
282
+ - **Multi-kernel**: Each `python_reset()` creates an isolated kernel for parallel agents
268
283
 
269
284
  ### Usage Tips
270
285
 
@@ -16,19 +16,12 @@ You have access to these specialized tools:
16
16
  - Call this ONCE when you have a complete, working solution
17
17
  - The code will be saved to {basename}_code.py
18
18
 
19
- 3. **report_issue**: Provide feedback and summary
20
- - Use at the end to summarize what was accomplished
21
- - Report any issues, ambiguities, or difficulties encountered
22
- - Also report if everything worked perfectly
23
- - This feedback will be included in the log file
24
-
25
19
  ## Workflow
26
20
 
27
21
  1. **Understand the Task**: Read the problem in the <task> section carefully
28
22
  2. **Plan Your Approach**: Think through the problem and plan your solution strategy
29
23
  3. **Develop Solution**: Use python_exec iteratively to build and test
30
24
  4. **Save Final Code**: Call save_code with your complete solution
31
- 5. **Provide Feedback**: Use report_issue to summarize and provide feedback
32
25
 
33
26
  ## Python Execution Best Practices
34
27
 
@@ -68,8 +61,7 @@ Build solutions incrementally:
68
61
  1. **Focus on the Task**: Complete what's requested, nothing more
69
62
  2. **Test Efficiently**: One or two test cases are usually sufficient
70
63
  3. **Save Once**: Call save_code only when you have the final code
71
- 4. **Always Provide Feedback**: Use report_issue at the end to summarize your work
72
- 5. **Stop When Done**: Don't add features not requested
64
+ 4. **Stop When Done**: Don't add features not requested
73
65
 
74
66
  ## Error Recovery
75
67
 
@@ -94,10 +86,6 @@ When finishing:
94
86
  1. Verify the solution works correctly
95
87
  2. Clean the code according to the **Code Cleaning Requirements** above
96
88
  3. Call save_code with the complete, cleaned code
97
- 4. Call report_issue to provide your final summary and feedback:
98
- - Summarize what was accomplished
99
- - Report any issues, ambiguities, or difficulties encountered
100
- - Even if everything worked perfectly, report: "All is fine - no issues encountered."
101
- 5. STOP - do not continue unless asked
89
+ 4. STOP - do not continue unless asked
102
90
 
103
- Your goal is efficient, focused problem-solving.
91
+ Your goal is efficient, focused problem-solving.
@@ -16,35 +16,28 @@ You have access to these specialized tools:
16
16
  - Call this ONCE when you have a complete, working solution
17
17
  - The code will be saved to {basename}_code.py
18
18
 
19
- 3. **report_issue**: Provide feedback and summary
20
- - Use at the end to summarize what was accomplished
21
- - Report any issues, ambiguities, or difficulties encountered
22
- - Also report if everything worked perfectly
23
- - This feedback will be included in the log file
24
-
25
- 4. **todo_write**: MANDATORY task management tool
19
+ 3. **todo_write**: MANDATORY task management tool
26
20
  - You MUST use this tool after understanding the problem
27
21
  - Create a todo list with items appropriate to the problem complexity
28
22
  (ranging from 3 simple items to over a dozen for complex problems)
29
23
  - Update todo item status as you progress (pending → in_progress → completed)
30
24
  - Only ONE todo item can be in_progress at a time
31
25
  - Add new todo items if you discover additional work needed
32
- - Your last three todo items should always be: "Clean final code", "Save final code", and "Provide feedback"
26
+ - Your last two todo items should always be: "Clean final code" and "Save final code"
33
27
 
34
28
  ## Workflow
35
29
 
36
30
  1. **Understand the Task**: Read the problem in the <task> section carefully
37
- 2. **Plan Your Approach** (MANDATORY):
31
+ 2. **Plan Your Approach** (MANDATORY):
38
32
  - Use todo_write to create your todo list based on your understanding
39
33
  - The number of todo items should match problem complexity (3-12+ items)
40
- - Include "Save final code" and "Provide feedback" as your last two items
34
+ - Include "Save final code" as your last item
41
35
  - This demonstrates planning and helps track progress
42
36
  3. **Develop Solution**: Use python_exec iteratively to build and test
43
37
  - Mark todo items as in_progress when starting them
44
38
  - Mark as completed when done
45
- 4. **Clean Final Code**: Clean the code according to Code Cleaning Requirements (third-to-last todo item)
46
- 5. **Save Final Code**: Call save_code with your complete, cleaned solution (second-to-last todo item)
47
- 6. **Provide Feedback**: Use report_issue to summarize and provide feedback (final todo item)
39
+ 4. **Clean Final Code**: Clean the code according to Code Cleaning Requirements (second-to-last todo item)
40
+ 5. **Save Final Code**: Call save_code with your complete, cleaned solution (final todo item)
48
41
 
49
42
  ## Python Execution Best Practices
50
43
 
@@ -85,8 +78,7 @@ Build solutions incrementally:
85
78
  2. **Focus on the Task**: Complete what's requested, nothing more
86
79
  3. **Test Efficiently**: One or two test cases are usually sufficient
87
80
  4. **Save Once**: Call save_code only when you have the final code
88
- 5. **Always Provide Feedback**: Use report_issue at the end to summarize your work
89
- 6. **Stop When Done**: Don't add features not requested
81
+ 5. **Stop When Done**: Don't add features not requested
90
82
 
91
83
  ## Error Recovery
92
84
 
@@ -111,13 +103,9 @@ When finishing (these should be your final todo items):
111
103
  1. Ensure all todo items are marked as completed
112
104
  2. Verify the solution works correctly
113
105
  3. Clean the code according to the **Code Cleaning Requirements** above
114
- 4. Call save_code with the complete, cleaned code (second-to-last todo item)
115
- 5. Call report_issue to provide your final summary and feedback (final todo item):
116
- - Summarize what was accomplished
117
- - Report any issues, ambiguities, or difficulties encountered
118
- - Even if everything worked perfectly, report: "All is fine - no issues encountered."
119
- 6. STOP - do not continue unless asked
106
+ 4. Call save_code with the complete, cleaned code (final todo item)
107
+ 5. STOP - do not continue unless asked
120
108
 
121
109
  Note: Your todo list should show a clear progression from planning through completion.
122
110
 
123
- Your goal is efficient, focused problem-solving.
111
+ Your goal is efficient, focused problem-solving.
@@ -0,0 +1,74 @@
1
+ """Python Coding Agent - A minimal coding assistant using LangGraph and OpenRouter."""
2
+
3
+ __version__ = "2.3.0"
4
+
5
+ # High-level API (recommended for most users)
6
+ from agentic_python_coder.runner import solve_task
7
+
8
+ # Lower-level API (for custom workflows)
9
+ from agentic_python_coder.agent import (
10
+ create_coding_agent,
11
+ run_agent,
12
+ get_final_response,
13
+ DEFAULT_STEP_LIMIT,
14
+ )
15
+
16
+ # LLM utilities
17
+ from agentic_python_coder.llm import (
18
+ get_openrouter_llm,
19
+ load_model_config,
20
+ list_available_models,
21
+ DEFAULT_MODEL,
22
+ )
23
+
24
+ # Kernel management (multi-kernel API)
25
+ from agentic_python_coder.kernel import (
26
+ # Core functions
27
+ create_kernel,
28
+ execute_in_kernel,
29
+ shutdown_kernel_by_id,
30
+ interrupt_kernel_by_id,
31
+ restart_kernel,
32
+ # Query functions
33
+ list_kernels,
34
+ kernel_exists,
35
+ get_kernel_info,
36
+ shutdown_all_kernels,
37
+ # Backward compat
38
+ get_kernel,
39
+ shutdown_kernel,
40
+ # Constants
41
+ DEFAULT_KERNEL_ID,
42
+ MAX_KERNELS,
43
+ )
44
+
45
+ __all__ = [
46
+ # Version
47
+ "__version__",
48
+ # High-level
49
+ "solve_task",
50
+ # Low-level agent
51
+ "create_coding_agent",
52
+ "run_agent",
53
+ "get_final_response",
54
+ "DEFAULT_STEP_LIMIT",
55
+ # LLM
56
+ "get_openrouter_llm",
57
+ "load_model_config",
58
+ "list_available_models",
59
+ "DEFAULT_MODEL",
60
+ # Kernel management
61
+ "create_kernel",
62
+ "execute_in_kernel",
63
+ "shutdown_kernel_by_id",
64
+ "interrupt_kernel_by_id",
65
+ "restart_kernel",
66
+ "list_kernels",
67
+ "kernel_exists",
68
+ "get_kernel_info",
69
+ "shutdown_all_kernels",
70
+ "get_kernel",
71
+ "shutdown_kernel",
72
+ "DEFAULT_KERNEL_ID",
73
+ "MAX_KERNELS",
74
+ ]
@@ -1,8 +1,11 @@
1
1
  """ReAct agent for Python coding tasks."""
2
2
 
3
+ import json
4
+ import os
5
+ import time
3
6
  from typing import Dict, Any, List, Optional
4
7
  from pathlib import Path
5
- from langgraph.prebuilt import create_react_agent
8
+ from langchain.agents import create_agent
6
9
  from langgraph.checkpoint.memory import InMemorySaver
7
10
 
8
11
  from agentic_python_coder.llm import get_openrouter_llm
@@ -10,7 +13,6 @@ from agentic_python_coder.tools import (
10
13
  todo_write,
11
14
  python_exec,
12
15
  save_code,
13
- report_issue,
14
16
  working_dir,
15
17
  set_task_basename,
16
18
  reset_global_state,
@@ -46,7 +48,7 @@ def create_coding_agent(
46
48
  working_directory: Directory for file operations
47
49
  system_prompt: System prompt as string (takes precedence over path)
48
50
  system_prompt_path: Path to system prompt file (used if system_prompt not provided)
49
- model: Optional model name (defaults to claude-sonnet-4.5)
51
+ model: Optional model name (uses configured default if not specified)
50
52
  project_prompt: Optional project-specific prompt
51
53
  with_packages: Optional list of packages for dynamic mode
52
54
  task_content: Task description/content
@@ -67,8 +69,6 @@ def create_coding_agent(
67
69
 
68
70
  # Store packages for kernel initialization
69
71
  if with_packages is not None:
70
- import os
71
-
72
72
  os.environ["CODER_WITH_PACKAGES"] = ",".join(with_packages)
73
73
 
74
74
  # Get LLM instance
@@ -80,9 +80,9 @@ def create_coding_agent(
80
80
 
81
81
  # Minimal tool set
82
82
  if todo:
83
- tools = [python_exec, save_code, report_issue, todo_write]
83
+ tools = [python_exec, save_code, todo_write]
84
84
  else:
85
- tools = [python_exec, save_code, report_issue]
85
+ tools = [python_exec, save_code]
86
86
 
87
87
  # Build combined prompt
88
88
  prompts = []
@@ -109,15 +109,13 @@ def create_coding_agent(
109
109
 
110
110
  # Create the agent with memory
111
111
  checkpointer = InMemorySaver()
112
- agent = create_react_agent(
113
- llm, tools, prompt=combined_prompt, checkpointer=checkpointer
112
+ agent = create_agent(
113
+ llm, tools, system_prompt=combined_prompt, checkpointer=checkpointer
114
114
  )
115
115
 
116
116
  # Store metadata for run_agent to use
117
117
  agent._coder_metadata = {
118
118
  "working_directory": working_directory,
119
- "with_packages": with_packages,
120
- "task_basename": task_basename,
121
119
  }
122
120
 
123
121
  return agent
@@ -127,24 +125,21 @@ def _print_tool_progress(tool_name: str, args: dict):
127
125
  """Print progress info for a tool call."""
128
126
  if tool_name == "python_exec" and "code" in args:
129
127
  code = args["code"]
128
+ code_stripped = code.strip()
130
129
  if "def " in code:
131
- func_match = code.split("def ")[1].split("(")[0] if "def " in code else ""
130
+ func_match = code.split("def ")[1].split("(")[0]
132
131
  print(f" {tool_name}: defining function {func_match}()")
133
132
  elif "class " in code:
134
- class_match = (
135
- code.split("class ")[1].split("(")[0].split(":")[0]
136
- if "class " in code
137
- else ""
138
- )
133
+ class_match = code.split("class ")[1].split("(")[0].split(":")[0]
139
134
  print(f" {tool_name}: defining class {class_match}")
140
- elif "import " in code and len(code.strip().split("\n")) == 1:
141
- print(f" {tool_name}: {code.strip()}")
142
- elif "=" in code and len(code.strip().split("\n")) == 1:
135
+ elif "import " in code and len(code_stripped.split("\n")) == 1:
136
+ print(f" {tool_name}: {code_stripped}")
137
+ elif "=" in code and len(code_stripped.split("\n")) == 1:
143
138
  var_name = code.split("=")[0].strip()
144
139
  print(f" {tool_name}: assigning variable {var_name}")
145
- elif code.strip().startswith("print("):
140
+ elif code_stripped.startswith("print("):
146
141
  print(
147
- f" {tool_name}: {code.strip()[:50]}{'...' if len(code.strip()) > 50 else ''}"
142
+ f" {tool_name}: {code_stripped[:50]}{'...' if len(code_stripped) > 50 else ''}"
148
143
  )
149
144
  elif "read_csv" in code or "read_excel" in code or "read_json" in code:
150
145
  print(f" {tool_name}: loading data file")
@@ -171,32 +166,23 @@ def _print_tool_progress(tool_name: str, args: dict):
171
166
  print(f"\n {tool_name}:")
172
167
  todos = args["todos"]
173
168
  for todo in todos:
174
- status_symbol = (
175
- ""
176
- if todo["status"] == "completed"
177
- else "☐"
178
- if todo["status"] == "pending"
179
- else "▶"
180
- )
181
- print(f" {status_symbol} {todo['content']}")
169
+ status = todo.get("status", "")
170
+ content = todo.get("content", "")
171
+ status_symbol = "" if status == "completed" else "☐" if status == "pending" else "▶"
172
+ print(f" {status_symbol} {content}")
182
173
  else:
183
- arg_str = str(args)[:30]
184
- if len(str(args)) > 30:
185
- arg_str += "..."
186
- print(f" {tool_name}: {arg_str}")
174
+ args_str = str(args)
175
+ arg_display = args_str[:30] + "..." if len(args_str) > 30 else args_str
176
+ print(f" {tool_name}: {arg_display}")
187
177
 
188
178
 
189
- def _process_tool_calls(msg, current_tools: dict, stats: dict, quiet: bool):
179
+ def _process_tool_calls(msg, stats: dict, quiet: bool):
190
180
  """Process tool calls from a message, updating stats and optionally printing."""
191
181
  if hasattr(msg, "tool_calls") and msg.tool_calls:
192
182
  for tool_call in msg.tool_calls:
193
183
  tool_name = tool_call.get("name") or tool_call.get("function", {}).get(
194
184
  "name"
195
185
  )
196
- tool_id = tool_call.get("id")
197
- if tool_id:
198
- current_tools[tool_id] = tool_name
199
-
200
186
  if tool_name:
201
187
  stats["tool_usage"][tool_name] = (
202
188
  stats["tool_usage"].get(tool_name, 0) + 1
@@ -214,9 +200,6 @@ def _process_tool_calls(msg, current_tools: dict, stats: dict, quiet: bool):
214
200
  for tool_call in tool_calls:
215
201
  function = tool_call.get("function", {})
216
202
  tool_name = function.get("name")
217
- tool_id = tool_call.get("id")
218
- if tool_id and tool_name:
219
- current_tools[tool_id] = tool_name
220
203
 
221
204
  if tool_name:
222
205
  stats["tool_usage"][tool_name] = (
@@ -226,11 +209,9 @@ def _process_tool_calls(msg, current_tools: dict, stats: dict, quiet: bool):
226
209
  if not quiet and tool_name:
227
210
  args_str = function.get("arguments", "{}")
228
211
  try:
229
- import json
230
-
231
212
  args = json.loads(args_str)
232
213
  _print_tool_progress(tool_name, args)
233
- except Exception:
214
+ except json.JSONDecodeError:
234
215
  print(f" {tool_name}")
235
216
 
236
217
 
@@ -244,6 +225,7 @@ def _update_token_stats(msg, stats: dict):
244
225
  "completion_tokens", 0
245
226
  )
246
227
  stats["token_consumption"]["total_tokens"] += usage.get("total_tokens", 0)
228
+ return # Avoid double-counting if both metadata sources exist
247
229
 
248
230
  if hasattr(msg, "usage_metadata") and msg.usage_metadata:
249
231
  stats["token_consumption"]["input_tokens"] += msg.usage_metadata.get(
@@ -286,7 +268,6 @@ def run_agent(
286
268
  config = {"configurable": {"thread_id": thread_id}, "recursion_limit": limit}
287
269
 
288
270
  messages = []
289
- current_tools = {}
290
271
 
291
272
  # Initialize statistics
292
273
  stats = {
@@ -295,8 +276,6 @@ def run_agent(
295
276
  "execution_time_seconds": 0,
296
277
  }
297
278
 
298
- import time
299
-
300
279
  start_time = time.time()
301
280
 
302
281
  # Stream the agent's work
@@ -314,7 +293,7 @@ def run_agent(
314
293
  messages.append(msg)
315
294
 
316
295
  # Process tool calls (always update stats, optionally print)
317
- _process_tool_calls(msg, current_tools, stats, quiet)
296
+ _process_tool_calls(msg, stats, quiet)
318
297
 
319
298
  # Update token statistics
320
299
  _update_token_stats(msg, stats)
@@ -23,7 +23,11 @@ from agentic_python_coder.project_md import (
23
23
  check_packages_available,
24
24
  create_project_prompt,
25
25
  )
26
- from agentic_python_coder.llm import DEFAULT_MODEL, list_available_models, load_model_config
26
+ from agentic_python_coder.llm import (
27
+ DEFAULT_MODEL,
28
+ list_available_models,
29
+ load_model_config,
30
+ )
27
31
  from agentic_python_coder import __version__
28
32
 
29
33
 
@@ -88,7 +92,8 @@ def parse_args():
88
92
  )
89
93
 
90
94
  parser.add_argument(
91
- "--version", "-V",
95
+ "--version",
96
+ "-V",
92
97
  action="version",
93
98
  version=f"%(prog)s {__version__}",
94
99
  )
@@ -106,7 +111,9 @@ def parse_args():
106
111
  help="Path to task file (creates {basename}_code.py and {basename}.jsonl)",
107
112
  )
108
113
 
109
- parser.add_argument("--model", help=f"Model name or JSON file (default: {DEFAULT_MODEL})")
114
+ parser.add_argument(
115
+ "--model", help=f"Model name or JSON file (default: {DEFAULT_MODEL})"
116
+ )
110
117
 
111
118
  parser.add_argument(
112
119
  "--interactive", "-i", action="store_true", help="Interactive mode"
@@ -145,7 +152,8 @@ def parse_args():
145
152
  )
146
153
 
147
154
  parser.add_argument(
148
- "--quiet", "-q",
155
+ "--quiet",
156
+ "-q",
149
157
  action="store_true",
150
158
  help="Suppress console output during execution",
151
159
  )
@@ -176,13 +184,13 @@ def copy_resource_dir(source_path, dest_path: Path):
176
184
  dest_item = dest_path / item.name
177
185
  if item.is_file():
178
186
  # Skip __pycache__ and .pyc files
179
- if item.name.endswith('.pyc') or item.name == '__pycache__':
187
+ if item.name.endswith(".pyc") or item.name == "__pycache__":
180
188
  continue
181
189
  # Read and write file content
182
190
  content = item.read_bytes()
183
191
  dest_item.write_bytes(content)
184
192
  elif item.is_dir():
185
- if item.name == '__pycache__':
193
+ if item.name == "__pycache__":
186
194
  continue
187
195
  copy_resource_dir(item, dest_item)
188
196
 
@@ -216,7 +224,9 @@ def init_examples(template: str = "all"):
216
224
 
217
225
  print(f"\nExamples initialized in: {output_dir.absolute()}")
218
226
  print("\nUsage:")
219
- print(f" coder --with cpmpy --project {output_dir}/cpmpy/cpmpy.md --task {output_dir}/cpmpy/sample_tasks/n_queens.md")
227
+ print(
228
+ f" coder --with cpmpy --project {output_dir}/cpmpy/cpmpy.md --task {output_dir}/cpmpy/sample_tasks/n_queens.md"
229
+ )
220
230
 
221
231
 
222
232
  def validate_packages(packages):