agentic-python-coder 3.0.0__tar.gz → 3.2.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 (48) hide show
  1. agentic_python_coder-3.2.0/MEM/MEM_000.md +114 -0
  2. agentic_python_coder-3.2.0/MEM/MEM_001_frameworkless_react_agent.md +110 -0
  3. agentic_python_coder-3.2.0/MEM/MEM_002_tool_description_quality.md +107 -0
  4. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/PKG-INFO +8 -7
  5. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/README.md +4 -3
  6. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/llm.py +18 -10
  7. agentic_python_coder-3.2.0/coder/src/agentic_python_coder/models/gemini31.json +7 -0
  8. agentic_python_coder-3.2.0/coder/src/agentic_python_coder/models/gemini3flash.json +5 -0
  9. agentic_python_coder-3.2.0/coder/src/agentic_python_coder/models/sonnet46.json +6 -0
  10. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/pyproject.toml +4 -4
  11. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/.gitignore +0 -0
  12. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/LICENSE +0 -0
  13. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/.gitignore +0 -0
  14. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/prompts/system.md +0 -0
  15. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/prompts/system_todo.md +0 -0
  16. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/__init__.py +0 -0
  17. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/agent.py +0 -0
  18. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/cli.py +0 -0
  19. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/__init__.py +0 -0
  20. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/clingo/README.md +0 -0
  21. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/clingo/clingo.md +0 -0
  22. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/bird_reasoning.md +0 -0
  23. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/diagnosis.md +0 -0
  24. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/simple_coloring.md +0 -0
  25. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/stable_marriage.md +0 -0
  26. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/clingo/sample_tasks/sudoku_mini.md +0 -0
  27. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/cpmpy/README.md +0 -0
  28. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/cpmpy/cpmpy.md +0 -0
  29. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/cpmpy/sample_tasks/magic_square.md +0 -0
  30. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/cpmpy/sample_tasks/n_queens.md +0 -0
  31. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/regex/README.md +0 -0
  32. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/regex/regex.md +0 -0
  33. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/regex/sample_tasks/email_extraction.md +0 -0
  34. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/regex/sample_tasks/phone_validation.md +0 -0
  35. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/examples/regex/sample_tasks/url_parsing.md +0 -0
  36. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/kernel.py +0 -0
  37. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/mcp_server.py +0 -0
  38. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/models/deepseek31.json +0 -0
  39. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/models/gemini25.json +0 -0
  40. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/models/gemini3pro.json +0 -0
  41. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/models/gpt52.json +0 -0
  42. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/models/grok41.json +0 -0
  43. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/models/opus45.json +0 -0
  44. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/models/qwen3.json +0 -0
  45. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/models/sonnet45.json +0 -0
  46. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/project_md.py +0 -0
  47. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/runner.py +0 -0
  48. {agentic_python_coder-3.0.0 → agentic_python_coder-3.2.0}/coder/src/agentic_python_coder/tools.py +0 -0
@@ -0,0 +1,114 @@
1
+ ---
2
+ keywords: [session log, process notes, history]
3
+ context: Searchable session log — grep when needed, not loaded routinely
4
+ added: 2026-01-31
5
+ ---
6
+ # Process Notes
7
+
8
+ Searchable session log. Not loaded routinely—search with grep when needed.
9
+
10
+ ---
11
+ ## 2026-02-15
12
+
13
+ **Goal**: Routine maintenance — dependency updates, new model config, code review fixes
14
+
15
+ **Done**:
16
+ - Bumped deps: openai 2.16→2.21, rich 14.3.1→14.3.2, ipykernel 7.1→7.2
17
+ - Added gemini3flash model config (Gemini 3 Flash Preview)
18
+ - Code review (ULTRA) found 6 issues in llm.py — all fixed: provider routing bug, OSError handling, model_path→model_id rename, empty "Available:" message, Warning→Error label, unnecessary ./ prefix
19
+ - Published v3.1.0 to PyPI
20
+ - Migrated EXPERTISE/ → MEM/ (2 entries + PROCESS_NOTES.md)
21
+
22
+ **Tried**:
23
+ - Initially copied gemini3pro.json params blindly for gemini3flash — Stefan caught this, verified actual supported params via WS7
24
+
25
+ **Notes**: PyPI token is at ~/git/.pipy.txt (updated CLAUDE.md to record this)
26
+
27
+ ---
28
+
29
+
30
+ ## 2026-01-31 (session 3)
31
+
32
+ **Goal**: Run e2e benchmark tests across models, fix failures, improve prompts, merge v3.0.0 to main, publish to PyPI
33
+
34
+ **Done**:
35
+ - Ran hard problem benchmarks (5 CPMpy + 5 Clingo) across opus45, gpt52, gemini3pro
36
+ - Created gemini3pro.json model config (google/gemini-3-pro-preview)
37
+ - Fixed gpt52 max_tokens: 3000→16384 (was truncating tool calls, caused 0/5 Clingo)
38
+ - Rewrote Clingo validate.py (old one was wrong ASP-Bench full-suite validator)
39
+ - Added mandatory verification prompts to system.md, system_todo.md, and save_code tool description
40
+ - Analyzed GPT-5.2 CPMpy failures with GPTT consult (circular verification, wrong problem interpretation)
41
+ - Fixed flaky test_todo_flag_integration (catch TimeoutExpired for partial output)
42
+ - Squash-merged dev→main, published v3.0.0 to PyPI
43
+ - Updated README: examples link, gemini3pro in model list
44
+
45
+ **Tried**:
46
+ - Verification prompts in system prompt + tool description — improved opus45 CPMpy 4/5→5/5 and gpt52 Clingo 0/5→5/5
47
+ - GPT-5.2 on CPMpy still only 2/5 despite verification prompts — model sometimes ignores them (2 tool calls before save) or writes circular verify()
48
+ - Gemini 3 Pro initially 0/10 with max_tokens=2048; bumped to 16384 → 5/10
49
+
50
+ **Notes**: Key insight — verification gates work but models can write verify() functions that re-implement solver bugs instead of checking problem text independently. Future prompt work should address this circular verification pitfall.
51
+
52
+ ---
53
+
54
+ ## 2026-01-31 (session 2)
55
+
56
+ **Goal**: Clean up dev branch for v3.0.0 release — model configs, tool descriptions, README, examples consolidation
57
+
58
+ **Done**:
59
+ - Replaced gpt5 model config with gpt52 (openai/gpt-5.2)
60
+ - Restored verbose LangGraph-era tool descriptions in tools.py (fixes performance regression on hard tasks)
61
+ - Restored parameter schemas to match tested reference (title fields, additionalProperties style)
62
+ - Created EXPERTISE/E002_tool_description_quality.md
63
+ - Removed LangGraph badge and references from README
64
+ - Bumped all dependency minimum versions to current
65
+ - Consolidated examples: deleted legacy examples/ dir, kept only bundled coder/src/.../examples/
66
+ - Confirmed _todo.md project prompts are unused (--todo only switches system prompt)
67
+ - Improved CPMpy project prompt with InDomain constraint and pitfalls section
68
+ - Copied e2e challenge tests (hard_cpmpy, hard_clingo) to tests/ (gitignored)
69
+ - All 70 unit tests pass
70
+
71
+ **Tried**:
72
+ - Initially kept minimal tool descriptions after LangGraph removal — caused performance regression on challenging tasks, discovered in separate test session
73
+ - Tried using "description" field in JSON schemas instead of "title" — reverted to match LangGraph-generated style that was proven to work
74
+
75
+ **Notes**: Tool descriptions are behavioral instructions for the model, not documentation. Verbose descriptions with examples, Args/Returns sections are essential for tool-calling quality. This is documented in E002.
76
+
77
+ ---
78
+
79
+ ## 2026-01-31
80
+
81
+ **Goal**: Update model configs and fix tool description regression
82
+
83
+ **Done**:
84
+ - Replaced gpt5 model config with gpt52 (openai/gpt-5.2) — JSON, README, tests
85
+ - Restored tool descriptions in tools.py to verbose LangGraph-era format (critical for performance)
86
+ - Restored parameter schemas to match tested reference (title fields, additionalProperties style)
87
+ - Created EXPERTISE/E002_tool_description_quality.md
88
+ - Removed unused _task_basename import in test_library_api.py
89
+
90
+ **Tried**:
91
+ - Initially kept minimal tool descriptions after LangGraph removal — caused performance regression on challenging tasks
92
+ - Discovered that verbose descriptions with examples, Args/Returns sections are essential for model tool-calling quality
93
+
94
+ **Notes**: Tool descriptions are behavioral instructions for the model, not documentation for humans. This is a key insight for any framework migration.
95
+
96
+ ---
97
+
98
+ ## 2026-01-30
99
+
100
+ **Goal**: Verify test suite, create expertise entry for frameworkless agent pattern, housekeeping
101
+
102
+ **Done**:
103
+ - Ran all 70 pytest tests — all pass after v3.0.0 migration
104
+ - Confirmed examples/ has sample tasks but no automated e2e tests
105
+ - Created EXPERTISE/E001_frameworkless_react_agent.md documenting the frameworkless ReAct pattern
106
+ - Added EXPERTISE/ to .gitignore, committed and pushed to dev
107
+ - Removed dead links from Key Documents (LOCAL/ directory no longer exists)
108
+
109
+ **Tried**:
110
+ - Looked for e2e tests in examples/ — only sample task files and one manual test run (regex/sample_tasks/test_email/)
111
+
112
+ **Notes**: E2E tests remain the top priority open goal. They need mocked OpenAI API responses (mock client.chat.completions.create()).
113
+
114
+ ---
@@ -0,0 +1,110 @@
1
+ ---
2
+ keywords: [react, agent, openai, frameworkless, langgraph, langchain, migration, tool-calling]
3
+ context: Building LLM-powered agents without heavy frameworks (LangGraph, LangChain, CrewAI, etc.)
4
+ added: 2026-01-30
5
+ ---
6
+ # Frameworkless ReAct Agent Pattern
7
+
8
+ ## Insight
9
+
10
+ A fully functional ReAct agent requires only ~4 components using the `openai` Python client directly, with no framework overhead:
11
+
12
+ 1. **LLM config** — A dataclass wrapping `openai.OpenAI` client, model path, and API params (temperature, max_tokens). Route through OpenRouter for multi-provider access.
13
+
14
+ 2. **Tool registry** — A `Tool` dataclass (name, description, JSON Schema parameters, callable function) with a `ToolRegistry` class that converts tools to OpenAI format via `to_openai_tool()`. Each tool's `execute()` returns a JSON string.
15
+
16
+ 3. **ReAct loop** — A simple `for step in range(limit)` loop that:
17
+ - Calls `client.chat.completions.create()` with messages + tools
18
+ - If response has `tool_calls`: execute each, append tool results to messages
19
+ - If no `tool_calls`: it's the final answer — break
20
+ - Messages list grows naturally (system → user → assistant+tool_calls → tool → assistant+tool_calls → ... → assistant)
21
+
22
+ 4. **Agent dataclass** — Holds config, tools, system prompt, working directory, and persistent `messages` list (enables multi-turn/interactive mode).
23
+
24
+ ### Key advantages over LangGraph/LangChain
25
+
26
+ - **Dependency reduction**: 112 → 69 installed packages (removed all LangChain/LangGraph deps)
27
+ - **Debuggability**: The entire agent loop is ~60 lines of straightforward Python. No graph compilation, no state machines, no hidden middleware.
28
+ - **Message format control**: Full control over the `messages` list. No framework-imposed message wrappers or serialization formats.
29
+ - **Token tracking**: Direct access to `response.usage.prompt_tokens` / `completion_tokens`.
30
+ - **Error handling**: Tool errors are just JSON strings fed back to the model — the model self-corrects. No need for framework error recovery machinery.
31
+
32
+ ### Pitfalls learned during migration
33
+
34
+ - **`model or "default"` bug**: If `model=""` (empty string), `or` evaluates to `"default"` — use explicit `None` checks instead.
35
+ - **Environment variable leaks**: Global env vars (e.g., `CODER_WITH_PACKAGES`) persist across runs — always clear them with `os.environ.pop()` when not set.
36
+ - **Global state accumulation**: Module-level globals (todo lists, file basenames) accumulate across runs. Add a `reset_global_state()` function called at agent creation time.
37
+ - **Tool call message format**: OpenAI API requires tool_calls in assistant messages to be dicts with `id`, `type`, `function.name`, `function.arguments`. Missing any field causes cryptic errors.
38
+ - **UTF-8 encoding**: Always use `encoding="utf-8"` when opening files — platform default encoding can vary.
39
+
40
+ ### When to use a framework instead
41
+
42
+ - If you need **graph-based workflows** with conditional branching between multiple agents
43
+ - If you need **built-in memory/persistence** across sessions (vector stores, checkpoints)
44
+ - If you need **streaming with backpressure** and complex output routing
45
+ - For simple single-agent ReAct loops, the framework adds complexity without benefit
46
+
47
+ ## Example
48
+
49
+ ```python
50
+ from dataclasses import dataclass, field
51
+ from openai import OpenAI
52
+ import json
53
+
54
+ @dataclass
55
+ class Tool:
56
+ name: str
57
+ description: str
58
+ parameters: dict
59
+ function: callable
60
+
61
+ def to_openai_tool(self):
62
+ return {
63
+ "type": "function",
64
+ "function": {
65
+ "name": self.name,
66
+ "description": self.description,
67
+ "parameters": self.parameters,
68
+ },
69
+ }
70
+
71
+ def execute(self, **kwargs):
72
+ return self.function(**kwargs)
73
+
74
+ # ReAct loop core (~30 lines)
75
+ def run(client, model, messages, tools, limit=200):
76
+ openai_tools = [t.to_openai_tool() for t in tools]
77
+ tool_map = {t.name: t for t in tools}
78
+
79
+ for _ in range(limit):
80
+ resp = client.chat.completions.create(
81
+ model=model, messages=messages, tools=openai_tools, tool_choice="auto"
82
+ )
83
+ msg = resp.choices[0].message
84
+
85
+ if msg.tool_calls:
86
+ messages.append({
87
+ "role": "assistant",
88
+ "content": msg.content or "",
89
+ "tool_calls": [
90
+ {"id": tc.id, "type": "function",
91
+ "function": {"name": tc.function.name,
92
+ "arguments": tc.function.arguments}}
93
+ for tc in msg.tool_calls
94
+ ],
95
+ })
96
+ for tc in msg.tool_calls:
97
+ args = json.loads(tc.function.arguments)
98
+ result = tool_map[tc.function.name].execute(**args)
99
+ messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})
100
+ else:
101
+ messages.append({"role": "assistant", "content": msg.content or ""})
102
+ break
103
+ return messages
104
+ ```
105
+
106
+ ## Reference
107
+
108
+ - Blueprint implementation: `/Users/szeider/work/myreact` (the original pattern)
109
+ - Production implementation: `coder/src/agentic_python_coder/agent.py` (v3.0.0)
110
+ - Migration context: LangGraph v2.x → frameworkless in one session, all 70 tests passing
@@ -0,0 +1,107 @@
1
+ ---
2
+ keywords: [tool-descriptions, openai, tool-calling, performance, langgraph, schema, regression]
3
+ context: When writing or modifying OpenAI tool definitions for agents that use tool calling
4
+ added: 2026-01-31
5
+ ---
6
+ # Tool Description Quality Impacts Agent Performance
7
+
8
+ ## Insight
9
+
10
+ Verbose, LangGraph-style tool descriptions significantly outperform minimal descriptions on challenging tasks. When migrating from LangGraph to frameworkless tool definitions, preserving the full docstring content is critical.
11
+
12
+ ### What works (LangGraph-style verbose descriptions)
13
+
14
+ 1. **Multiple examples** showing multi-step usage patterns — not just what the tool does, but how to chain calls (e.g., define a function in one call, use it in the next)
15
+ 2. **Explicit Args section** describing each parameter in natural language
16
+ 3. **Explicit Returns section** listing every possible field in the response JSON (`success`, `stdout`, `result`, `stderr`, `error`)
17
+ 4. **Behavioral hints** embedded in the description (e.g., "The kernel maintains state between executions!", "use print() to see output")
18
+
19
+ ### What fails (minimal descriptions)
20
+
21
+ Stripping descriptions to just the core purpose (e.g., "Execute Python code in a persistent IPython kernel.") removes behavioral guidance the model relies on for:
22
+ - Understanding that state persists across calls
23
+ - Knowing what output format to expect
24
+ - Planning multi-step tool use sequences
25
+
26
+ ### Parameter schema style also matters
27
+
28
+ The LangGraph auto-generated schemas use `"title"` fields on properties and top-level schema, plus `"additionalProperties": True` for flexible object items. Switching to `"description"` fields and strict schemas (with explicit properties and enums) may change how models interpret and use the tools. When in doubt, match the schema style that was tested and proven to work.
29
+
30
+ ### Verification gates in tool descriptions
31
+
32
+ Adding verification requirements to `save_code` tool description ("Only call this AFTER you have executed the code and confirmed it produces correct output") measurably improved benchmark scores. Opus 4.5 went from 4/5→5/5 on hard CPMpy and GPT-5.2 from 0/5→5/5 on hard Clingo after adding these gates. However, GPT-5.2 still sometimes ignores verification (only 2 tool calls before saving on 016_sports). The verification prompt works better on some models than others.
33
+
34
+ ### Circular verification pitfall
35
+
36
+ Models that do write verify() functions sometimes verify against their own wrong model rather than the problem statement. The verify() re-implements the same bugs as the solver constraints. Prompt language should explicitly require deriving verification checks from the problem text, not from the model code.
37
+
38
+ ### Key takeaway
39
+
40
+ Tool descriptions are not documentation for humans — they are **behavioral instructions for the model**. Treat them as part of the prompt engineering, not as code comments to be minimized.
41
+
42
+ **Updated**: 2026-01-31
43
+
44
+ ## Example
45
+
46
+ ```python
47
+ # BAD: Minimal description (causes performance regression)
48
+ PYTHON_EXEC_TOOL = Tool(
49
+ name="python_exec",
50
+ description="Execute Python code in a persistent IPython kernel.",
51
+ parameters={
52
+ "type": "object",
53
+ "properties": {
54
+ "code": {"type": "string", "description": "Python code to execute."},
55
+ },
56
+ "required": ["code"],
57
+ },
58
+ function=python_exec,
59
+ )
60
+
61
+ # GOOD: Verbose LangGraph-style description (proven performance)
62
+ PYTHON_EXEC_TOOL = Tool(
63
+ name="python_exec",
64
+ description=(
65
+ "Execute Python code in a persistent IPython kernel.\n\n"
66
+ "IMPORTANT: The kernel maintains state between executions!\n"
67
+ "- Variables, functions, and imports persist across calls\n"
68
+ "- Use print() to see output, or the last expression will be returned\n"
69
+ "- The kernel runs in the working directory context\n\n"
70
+ "Example:\n"
71
+ " First call: x = 5\n"
72
+ ' Second call: print(x) # Returns: {"success": true, "stdout": "5"}\n\n'
73
+ " First call: def add(a, b): return a + b\n"
74
+ ' Second call: add(3, 4) # Returns: {"success": true, "result": "7"}\n\n'
75
+ "The code executes in the working directory context, so you can read/write files\n"
76
+ "using relative paths.\n\n"
77
+ "Args:\n"
78
+ " code: Python code to execute. Multi-line code is supported.\n\n"
79
+ "Returns:\n"
80
+ " JSON string with execution results:\n"
81
+ " - success: boolean indicating if execution succeeded\n"
82
+ " - stdout: captured print output (if any)\n"
83
+ " - result: the last expression's value (if any)\n"
84
+ " - stderr: warnings (if any)\n"
85
+ " - error: error message (if execution failed)"
86
+ ),
87
+ parameters={
88
+ "type": "object",
89
+ "properties": {
90
+ "code": {
91
+ "type": "string",
92
+ "title": "Code",
93
+ },
94
+ },
95
+ "required": ["code"],
96
+ "title": "python_exec",
97
+ },
98
+ function=python_exec,
99
+ )
100
+ ```
101
+
102
+ ## Reference
103
+
104
+ - Regression discovered: 2026-01-31 during benchmark testing of dev branch
105
+ - Fix verified in: `/Users/szeider/temp/codertest/agentic-python-coder-dev/`
106
+ - Production file: `coder/src/agentic_python_coder/tools.py`
107
+ - Related: E001_frameworkless_react_agent.md (migration context)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-python-coder
3
- Version: 3.0.0
3
+ Version: 3.2.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,13 +14,13 @@ 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>=7.1.0
17
+ Requires-Dist: ipykernel>=7.2.0
18
18
  Requires-Dist: jupyter-client>=8.8.0
19
19
  Requires-Dist: mcp>=1.26.0
20
- Requires-Dist: openai>=2.16.0
20
+ Requires-Dist: openai>=2.21.0
21
21
  Requires-Dist: python-dotenv>=1.2.1
22
22
  Requires-Dist: pyyaml>=6.0.3
23
- Requires-Dist: rich>=14.3.1
23
+ Requires-Dist: rich>=14.3.2
24
24
  Provides-Extra: dev
25
25
  Requires-Dist: mypy>=1.19.1; extra == 'dev'
26
26
  Requires-Dist: ruff>=0.14.14; extra == 'dev'
@@ -152,8 +152,9 @@ coder --model deepseek31 "task" # DeepSeek v3.1
152
152
  coder --model grok41 "task" # X.AI Grok 4.1
153
153
  coder --model qwen3 "task" # Qwen3 Coder
154
154
  coder --model gemini25 "task" # Gemini Pro 2.5
155
- coder --model gemini3pro "task" # Gemini 3 Pro Preview
156
- coder --model gpt52 "task" # GPT-5.2
155
+ coder --model gemini3pro "task" # Gemini 3 Pro Preview
156
+ coder --model gemini3flash "task" # Gemini 3 Flash Preview (fast, low-cost)
157
+ coder --model gpt52 "task" # GPT-5.2
157
158
 
158
159
  # Custom model (JSON file)
159
160
  coder --model ./mymodel.json "task"
@@ -259,7 +260,7 @@ from agentic_python_coder import get_openrouter_llm, list_available_models
259
260
 
260
261
  llm = get_openrouter_llm(model="sonnet45")
261
262
  print(list_available_models())
262
- # ['deepseek31', 'gemini25', 'gemini3pro', 'gpt52', 'grok41', 'opus45', 'qwen3', 'sonnet45']
263
+ # ['deepseek31', 'gemini25', 'gemini3flash', 'gemini3pro', 'gpt52', 'grok41', 'opus45', 'qwen3', 'sonnet45']
263
264
  ```
264
265
 
265
266
  ---
@@ -119,8 +119,9 @@ coder --model deepseek31 "task" # DeepSeek v3.1
119
119
  coder --model grok41 "task" # X.AI Grok 4.1
120
120
  coder --model qwen3 "task" # Qwen3 Coder
121
121
  coder --model gemini25 "task" # Gemini Pro 2.5
122
- coder --model gemini3pro "task" # Gemini 3 Pro Preview
123
- coder --model gpt52 "task" # GPT-5.2
122
+ coder --model gemini3pro "task" # Gemini 3 Pro Preview
123
+ coder --model gemini3flash "task" # Gemini 3 Flash Preview (fast, low-cost)
124
+ coder --model gpt52 "task" # GPT-5.2
124
125
 
125
126
  # Custom model (JSON file)
126
127
  coder --model ./mymodel.json "task"
@@ -226,7 +227,7 @@ from agentic_python_coder import get_openrouter_llm, list_available_models
226
227
 
227
228
  llm = get_openrouter_llm(model="sonnet45")
228
229
  print(list_available_models())
229
- # ['deepseek31', 'gemini25', 'gemini3pro', 'gpt52', 'grok41', 'opus45', 'qwen3', 'sonnet45']
230
+ # ['deepseek31', 'gemini25', 'gemini3flash', 'gemini3pro', 'gpt52', 'grok41', 'opus45', 'qwen3', 'sonnet45']
230
231
  ```
231
232
 
232
233
  ---
@@ -43,7 +43,7 @@ def get_api_key() -> str:
43
43
  api_key = os.getenv("OPENROUTER_API_KEY")
44
44
 
45
45
  if not api_key:
46
- print("Warning: No API key found. Set up with:", file=sys.stderr)
46
+ print("Error: No API key found. Set up with:", file=sys.stderr)
47
47
  print(" mkdir -p ~/.config/coder", file=sys.stderr)
48
48
  print(
49
49
  " echo 'OPENROUTER_API_KEY=sk-or-...' > ~/.config/coder/.env",
@@ -62,6 +62,8 @@ def _load_json_file(path: Path, model: str) -> dict[str, Any]:
62
62
  config = json.load(f)
63
63
  except json.JSONDecodeError as e:
64
64
  raise ValueError(f"Invalid JSON in model config '{model}': {e}") from e
65
+ except OSError as e:
66
+ raise FileNotFoundError(f"Cannot read model config '{model}': {e}") from e
65
67
 
66
68
  if "path" not in config:
67
69
  raise ValueError(f"Model config '{model}' missing required key: 'path'")
@@ -95,7 +97,7 @@ def load_model_config(model: str) -> dict[str, Any]:
95
97
  return _load_json_file(path, model)
96
98
 
97
99
  # Local override: ./{model}.json
98
- local_path = Path(f"./{model}.json")
100
+ local_path = Path(f"{model}.json")
99
101
  if local_path.exists():
100
102
  return _load_json_file(local_path, model)
101
103
 
@@ -107,9 +109,8 @@ def load_model_config(model: str) -> dict[str, Any]:
107
109
 
108
110
  # List available models for error message
109
111
  available = list_available_models()
110
- raise FileNotFoundError(
111
- f"Model '{model}' not found. Available: {', '.join(available)}"
112
- )
112
+ suffix = f" Available: {', '.join(available)}" if available else ""
113
+ raise FileNotFoundError(f"Model '{model}' not found.{suffix}")
113
114
 
114
115
 
115
116
  def list_available_models() -> list[str]:
@@ -146,10 +147,10 @@ def get_openrouter_llm(
146
147
  """
147
148
  # Load config from JSON
148
149
  config = load_model_config(model)
149
- model_path = config["path"]
150
+ model_id = config["path"]
150
151
 
151
152
  if verbose:
152
- print(f"Using model: {model_path}")
153
+ print(f"Using model: {model_id}")
153
154
  if os.getenv("CODER_VERBOSE"):
154
155
  for key, value in config.items():
155
156
  if key != "path":
@@ -194,7 +195,14 @@ def get_openrouter_llm(
194
195
  if key in config:
195
196
  api_params[key] = config[key]
196
197
  if "top_k" in config:
197
- # OpenRouter supports top_k via extra_body
198
- api_params["extra_body"] = {"top_k": config["top_k"]}
198
+ api_params.setdefault("extra_body", {})["top_k"] = config["top_k"]
199
199
 
200
- return LLMConfig(client=client, model=model_path, api_params=api_params)
200
+ # Provider routing works for all models (including no_sampling_params)
201
+ if "provider" in config:
202
+ api_params.setdefault("extra_body", {})["provider"] = config["provider"]
203
+
204
+ # Reasoning/thinking tokens (e.g. Gemini 3 Flash thinking levels)
205
+ if "reasoning" in config:
206
+ api_params.setdefault("extra_body", {})["reasoning"] = config["reasoning"]
207
+
208
+ return LLMConfig(client=client, model=model_id, api_params=api_params)
@@ -0,0 +1,7 @@
1
+ {
2
+ "path": "google/gemini-3.1-pro-preview",
3
+ "temperature": 0.0,
4
+ "max_tokens": 16384,
5
+ "streaming": true,
6
+ "request_timeout": 120
7
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "path": "google/gemini-3-flash-preview",
3
+ "temperature": 0.3,
4
+ "max_tokens": 16384
5
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "path": "anthropic/claude-sonnet-4.6",
3
+ "temperature": 0.0,
4
+ "max_tokens": 16384,
5
+ "streaming": true
6
+ }
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentic-python-coder"
3
- version = "3.0.0"
3
+ version = "3.2.0"
4
4
  description = "A lightweight Python coding agent that writes, executes, and iterates on code through natural language instructions"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13,<3.14"
@@ -19,11 +19,11 @@ classifiers = [
19
19
  ]
20
20
  keywords = ["ai", "coding", "agent", "llm", "code-generation", "automation"]
21
21
  dependencies = [
22
- "openai>=2.16.0",
22
+ "openai>=2.21.0",
23
23
  "python-dotenv>=1.2.1",
24
- "rich>=14.3.1",
24
+ "rich>=14.3.2",
25
25
  "jupyter-client>=8.8.0",
26
- "ipykernel>=7.1.0",
26
+ "ipykernel>=7.2.0",
27
27
  "PyYAML>=6.0.3",
28
28
  "mcp>=1.26.0",
29
29
  ]