agentnova 0.0__tar.gz → 0.2__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 (71) hide show
  1. {agentnova-0.0 → agentnova-0.2}/LICENSE +20 -20
  2. {agentnova-0.0 → agentnova-0.2}/PKG-INFO +17 -15
  3. {agentnova-0.0 → agentnova-0.2}/README.md +16 -14
  4. {agentnova-0.0 → agentnova-0.2}/agentnova/__init__.py +15 -2
  5. {agentnova-0.0 → agentnova-0.2}/agentnova/__main__.py +27 -27
  6. {agentnova-0.0 → agentnova-0.2}/agentnova/acp_plugin.py +1 -1
  7. {agentnova-0.0 → agentnova-0.2}/agentnova/agent_mode.py +30 -1
  8. {agentnova-0.0 → agentnova-0.2}/agentnova/bitnet_client.py +150 -150
  9. {agentnova-0.0 → agentnova-0.2}/agentnova/cli.py +102 -17
  10. {agentnova-0.0 → agentnova-0.2}/agentnova/core/agent.py +848 -79
  11. {agentnova-0.0 → agentnova-0.2}/agentnova/core/math_prompts.py +396 -396
  12. {agentnova-0.0 → agentnova-0.2}/agentnova/core/memory.py +191 -191
  13. agentnova-0.2/agentnova/core/model_family_config.py +381 -0
  14. {agentnova-0.0 → agentnova-0.2}/agentnova/core/ollama_client.py +531 -531
  15. {agentnova-0.0 → agentnova-0.2}/agentnova/core/orchestrator.py +190 -190
  16. {agentnova-0.0 → agentnova-0.2}/agentnova/core/orchestrator_enhanced.py +393 -393
  17. {agentnova-0.0 → agentnova-0.2}/agentnova/core/tools.py +303 -274
  18. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/00_backend_demo.py +2 -2
  19. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/04_comprehensive_test.py +11 -2
  20. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/05_tool_tests.py +13 -4
  21. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/07_model_comparison.py +88 -5
  22. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/08_robust_comparison.py +94 -7
  23. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/09_expanded_benchmark.py +50 -5
  24. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/10_skills_demo.py +2 -2
  25. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/11_skill_creator_test.py +3 -3
  26. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/12_batch_operations.py +1 -1
  27. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/13_shutdown_demo.py +1 -1
  28. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/14_gsm8k_benchmark.py +2 -2
  29. {agentnova-0.0 → agentnova-0.2}/agentnova/model_discovery.py +341 -341
  30. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/__init__.py +24 -24
  31. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/loader.py +444 -444
  32. {agentnova-0.0 → agentnova-0.2}/agentnova/tools/builtins.py +692 -666
  33. agentnova-0.2/agentnova/tools/sandboxed_repl.py +523 -0
  34. {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/PKG-INFO +17 -15
  35. {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/SOURCES.txt +2 -0
  36. {agentnova-0.0 → agentnova-0.2}/localclaw/__init__.py +1 -1
  37. {agentnova-0.0 → agentnova-0.2}/pyproject.toml +1 -1
  38. {agentnova-0.0 → agentnova-0.2}/tests/test_acp_integration.py +217 -217
  39. {agentnova-0.0 → agentnova-0.2}/tests/test_acp_subagents.py +310 -310
  40. {agentnova-0.0 → agentnova-0.2}/agentnova/bitnet_setup.py +0 -0
  41. {agentnova-0.0 → agentnova-0.2}/agentnova/config.py +0 -0
  42. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/01_basic_agent.py +0 -0
  43. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/02_tool_agent.py +0 -0
  44. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/03_orchestrator.py +0 -0
  45. {agentnova-0.0 → agentnova-0.2}/agentnova/examples/06_interactive_chat.py +0 -0
  46. {agentnova-0.0 → agentnova-0.2}/agentnova/shared_args.py +0 -0
  47. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/acp/SKILL.md +0 -0
  48. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/datetime/SKILL.md +0 -0
  49. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/SKILL.md +0 -0
  50. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/__init__.py +0 -0
  51. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/aggregate_benchmark.py +0 -0
  52. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/generate_report.py +0 -0
  53. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/improve_description.py +0 -0
  54. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/init_skill.py +0 -0
  55. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/package_skill.py +0 -0
  56. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/quick_validate.py +0 -0
  57. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/run_eval.py +0 -0
  58. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/run_loop.py +0 -0
  59. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/security_scan.py +0 -0
  60. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/test_package_skill.py +0 -0
  61. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/test_quick_validate.py +0 -0
  62. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/utils.py +0 -0
  63. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/validate.py +0 -0
  64. {agentnova-0.0 → agentnova-0.2}/agentnova/skills/web_search/SKILL.md +0 -0
  65. {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/dependency_links.txt +0 -0
  66. {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/entry_points.txt +0 -0
  67. {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/requires.txt +0 -0
  68. {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/top_level.txt +0 -0
  69. {agentnova-0.0 → agentnova-0.2}/localclaw/__main__.py +0 -0
  70. {agentnova-0.0 → agentnova-0.2}/setup.cfg +0 -0
  71. {agentnova-0.0 → agentnova-0.2}/tests/test_agent.py +0 -0
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Nigel Todman
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nigel Todman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentnova
3
- Version: 0.0
3
+ Version: 0.2
4
4
  Summary: A minimal, hackable agentic framework for Ollama and BitNet - local-first AI agent toolkit
5
5
  Author-email: VTSTech <veritas@vts-tech.org>
6
6
  Maintainer-email: VTSTech <veritas@vts-tech.org>
@@ -34,7 +34,7 @@ Requires-Dist: black>=23.0; extra == "dev"
34
34
  Requires-Dist: ruff>=0.1.0; extra == "dev"
35
35
  Dynamic: license-file
36
36
 
37
- # ⚛️ AgentNova R00
37
+ # ⚛️ AgentNova R02
38
38
 
39
39
  A minimal, hackable agentic framework engineered to run **entirely locally** with [Ollama](https://ollama.com) or [BitNet](https://github.com/microsoft/BitNet).
40
40
 
@@ -42,12 +42,9 @@ Inspired by the architecture of OpenClaw, rebuilt from scratch for local-first o
42
42
 
43
43
  **Written by [VTSTech](https://www.vts-tech.org)** · [GitHub](https://github.com/VTSTech/AgentNova)
44
44
 
45
- [![PyPI version fury.io](https://badge.fury.io/py/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![PyPI status](https://img.shields.io/pypi/status/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![GitHub commits](https://badgen.net/github/commits/VTSTech/AgentNova)](https://GitHub.com/VTSTech/AgentNova/commit/)
46
-
47
-
48
-
49
- [![PyPI download month](https://img.shields.io/pypi/dm/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![PyPI download week](https://img.shields.io/pypi/dw/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![PyPI download day](https://img.shields.io/pypi/dd/agentnova.svg)](https://pypi.python.org/pypi/agentnova/)
45
+ [![PyPI version fury.io](https://badge.fury.io/py/agentnova.svg?style=plastic)](https://pypi.org/project/agentnova/) [![PyPI status](https://img.shields.io/pypi/status/agentnova.svg?style=plastic)](https://pypi.python.org/pypi/agentnova/) [![GitHub commits](https://badgen.net/github/commits/VTSTech/AgentNova?style=plastic)](https://GitHub.com/VTSTech/AgentNova/commit/)
50
46
 
47
+ [![PyPI download month](https://img.shields.io/pypi/dm/agentnova.svg?style=plastic)](https://pypi.org/project/agentnova/) [![PyPI download week](https://img.shields.io/pypi/dw/agentnova.svg?style=plastic)](https://pypi.org/project/agentnova/) [![PyPI download day](https://img.shields.io/pypi/dd/agentnova.svg?style=plastic)](https://pypi.org/project/agentnova/)
51
48
 
52
49
  ---
53
50
 
@@ -204,15 +201,20 @@ agentnova models --tool_support
204
201
 
205
202
  ### Performance by Tool Support
206
203
 
207
- Recent GSM8K benchmark results (50 math questions):
204
+ Recent test results with native tool synthesis:
208
205
 
209
- | Model | Params | Tool Support | Score |
210
- |-------|--------|--------------|-------|
211
- | `gemma3:270m` | 270M | none | **64%** |
212
- | `functiongemma:270m` | 270M | native | 36% |
213
- | `granite4:350m` | 350M | native | ~40% |
206
+ | Model | Params | Tool Support | Calculator | Shell | Python |
207
+ |-------|--------|--------------|------------|-------|--------|
208
+ | `qwen2.5:0.5b` | 494M | native | **100%** | **100%** | **100%** |
209
+ | `qwen2.5-coder:0.5b` | 494M | ReAct | **100%** | **100%** | **100%** |
210
+ | `granite4:350m` | 350M | native | ~90% | ✅ | ✅ |
211
+ | `gemma3:270m` | 270M | none | **64%** | N/A | N/A |
214
212
 
215
- **Key insight**: Sub-500M models often perform better with `none` (pure reasoning) than with tools!
213
+ **Key improvements in R01**:
214
+ - Native tool synthesis extracts expressions from natural language
215
+ - Two-tier retry: hint → synthesize (bypasses confused models)
216
+ - Bare expression wrapping: `2**20` → `print(2**20)`
217
+ - Hallucinated mention detection for models that talk about tools but don't call them
216
218
 
217
219
  ---
218
220
 
@@ -263,7 +265,7 @@ Output shows:
263
265
  - **Tool Support** - `✓ native`, `ReAct`, `○ none`, or `untested`
264
266
 
265
267
  ```
266
- ⚛️ AgentNova R00 Models
268
+ ⚛️ AgentNova R02 Models
267
269
  Model Family Context Tool Support
268
270
  ──────────────────────────────────────────────────────────────────────────────
269
271
  gemma3:270m gemma3 32K ○ none
@@ -1,4 +1,4 @@
1
- # ⚛️ AgentNova R00
1
+ # ⚛️ AgentNova R02
2
2
 
3
3
  A minimal, hackable agentic framework engineered to run **entirely locally** with [Ollama](https://ollama.com) or [BitNet](https://github.com/microsoft/BitNet).
4
4
 
@@ -6,12 +6,9 @@ Inspired by the architecture of OpenClaw, rebuilt from scratch for local-first o
6
6
 
7
7
  **Written by [VTSTech](https://www.vts-tech.org)** · [GitHub](https://github.com/VTSTech/AgentNova)
8
8
 
9
- [![PyPI version fury.io](https://badge.fury.io/py/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![PyPI status](https://img.shields.io/pypi/status/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![GitHub commits](https://badgen.net/github/commits/VTSTech/AgentNova)](https://GitHub.com/VTSTech/AgentNova/commit/)
10
-
11
-
12
-
13
- [![PyPI download month](https://img.shields.io/pypi/dm/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![PyPI download week](https://img.shields.io/pypi/dw/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![PyPI download day](https://img.shields.io/pypi/dd/agentnova.svg)](https://pypi.python.org/pypi/agentnova/)
9
+ [![PyPI version fury.io](https://badge.fury.io/py/agentnova.svg?style=plastic)](https://pypi.org/project/agentnova/) [![PyPI status](https://img.shields.io/pypi/status/agentnova.svg?style=plastic)](https://pypi.python.org/pypi/agentnova/) [![GitHub commits](https://badgen.net/github/commits/VTSTech/AgentNova?style=plastic)](https://GitHub.com/VTSTech/AgentNova/commit/)
14
10
 
11
+ [![PyPI download month](https://img.shields.io/pypi/dm/agentnova.svg?style=plastic)](https://pypi.org/project/agentnova/) [![PyPI download week](https://img.shields.io/pypi/dw/agentnova.svg?style=plastic)](https://pypi.org/project/agentnova/) [![PyPI download day](https://img.shields.io/pypi/dd/agentnova.svg?style=plastic)](https://pypi.org/project/agentnova/)
15
12
 
16
13
  ---
17
14
 
@@ -168,15 +165,20 @@ agentnova models --tool_support
168
165
 
169
166
  ### Performance by Tool Support
170
167
 
171
- Recent GSM8K benchmark results (50 math questions):
168
+ Recent test results with native tool synthesis:
172
169
 
173
- | Model | Params | Tool Support | Score |
174
- |-------|--------|--------------|-------|
175
- | `gemma3:270m` | 270M | none | **64%** |
176
- | `functiongemma:270m` | 270M | native | 36% |
177
- | `granite4:350m` | 350M | native | ~40% |
170
+ | Model | Params | Tool Support | Calculator | Shell | Python |
171
+ |-------|--------|--------------|------------|-------|--------|
172
+ | `qwen2.5:0.5b` | 494M | native | **100%** | **100%** | **100%** |
173
+ | `qwen2.5-coder:0.5b` | 494M | ReAct | **100%** | **100%** | **100%** |
174
+ | `granite4:350m` | 350M | native | ~90% | ✅ | ✅ |
175
+ | `gemma3:270m` | 270M | none | **64%** | N/A | N/A |
178
176
 
179
- **Key insight**: Sub-500M models often perform better with `none` (pure reasoning) than with tools!
177
+ **Key improvements in R01**:
178
+ - Native tool synthesis extracts expressions from natural language
179
+ - Two-tier retry: hint → synthesize (bypasses confused models)
180
+ - Bare expression wrapping: `2**20` → `print(2**20)`
181
+ - Hallucinated mention detection for models that talk about tools but don't call them
180
182
 
181
183
  ---
182
184
 
@@ -227,7 +229,7 @@ Output shows:
227
229
  - **Tool Support** - `✓ native`, `ReAct`, `○ none`, or `untested`
228
230
 
229
231
  ```
230
- ⚛️ AgentNova R00 Models
232
+ ⚛️ AgentNova R02 Models
231
233
  Model Family Context Tool Support
232
234
  ──────────────────────────────────────────────────────────────────────────────
233
235
  gemma3:270m gemma3 32K ○ none
@@ -1,5 +1,5 @@
1
1
  """
2
- ⚛️ AgentNova R00 - A minimal, hackable agentic framework for Ollama and BitNet
2
+ ⚛️ AgentNova R02 - A minimal, hackable agentic framework for Ollama and BitNet
3
3
 
4
4
  Written by VTSTech
5
5
  https://www.vts-tech.org
@@ -15,6 +15,13 @@ from .core.ollama_client import OllamaClient
15
15
  from .core.orchestrator import Orchestrator, AgentCard
16
16
  from .skills import SkillLoader, Skill, SkillRegistry
17
17
  from .acp_plugin import ACPPlugin, create_acp_agent
18
+ from .core.model_family_config import (
19
+ ModelFamilyConfig, FAMILY_CONFIGS,
20
+ get_family_config, get_stop_tokens, supports_tools,
21
+ get_tool_format, get_preferred_temperature, should_use_few_shot,
22
+ get_few_shot_style, has_known_issues, get_react_system_suffix,
23
+ get_native_tool_hints,
24
+ )
18
25
 
19
26
  # Config exports
20
27
  from .config import (
@@ -177,6 +184,12 @@ __all__ = [
177
184
  "create_file_write_action", "create_file_delete_action",
178
185
  "create_mkdir_action", "create_shell_action",
179
186
  "format_status", "format_progress",
187
+ # R02: Model Family Configuration
188
+ "ModelFamilyConfig", "FAMILY_CONFIGS",
189
+ "get_family_config", "get_stop_tokens", "supports_tools",
190
+ "get_tool_format", "get_preferred_temperature", "should_use_few_shot",
191
+ "get_few_shot_style", "has_known_issues", "get_react_system_suffix",
192
+ "get_native_tool_hints",
180
193
  # Config exports
181
194
  "OLLAMA_BASE_URL",
182
195
  "BITNET_BASE_URL",
@@ -191,7 +204,7 @@ __all__ = [
191
204
  if _BITNET_AVAILABLE:
192
205
  __all__.extend(["BitnetClient", "KNOWN_MODELS"])
193
206
 
194
- __version__ = "0.0"
207
+ __version__ = "0.2"
195
208
  __author__ = "VTSTech"
196
209
  __author_email__ = "contact@vts-tech.org"
197
210
  __url__ = "https://github.com/VTSTech/AgentNova"
@@ -1,27 +1,27 @@
1
- #!/usr/bin/env python3
2
- """
3
- 🦞 AgentNova R04 — Command Line Interface
4
-
5
- Entry point for: python -m agentnova [command] [options]
6
-
7
- Commands:
8
- run Run the agent on a single prompt and exit
9
- chat Interactive multi-turn conversation with memory
10
- models List models available in Ollama
11
- tools List available built-in tools
12
- skills List available Agent Skills
13
-
14
- Examples:
15
- python -m agentnova run "What is the capital of France?"
16
- python -m agentnova chat --model llama3.1:8b --tools calculator,shell
17
- python -m agentnova models
18
- python -m agentnova tools
19
- python -m agentnova skills
20
-
21
- Written by VTSTech · https://www.vts-tech.org · https://github.com/VTSTech/AgentNova
22
- """
23
-
24
- from agentnova.cli import main
25
-
26
- if __name__ == "__main__":
27
- main()
1
+ #!/usr/bin/env python3
2
+ """
3
+ ⚛️ AgentNova R00 — Command Line Interface
4
+
5
+ Entry point for: python -m agentnova [command] [options]
6
+
7
+ Commands:
8
+ run Run the agent on a single prompt and exit
9
+ chat Interactive multi-turn conversation with memory
10
+ models List models available in Ollama
11
+ tools List available built-in tools
12
+ skills List available Agent Skills
13
+
14
+ Examples:
15
+ python -m agentnova run "What is the capital of France?"
16
+ python -m agentnova chat --model llama3.1:8b --tools calculator,shell
17
+ python -m agentnova models
18
+ python -m agentnova tools
19
+ python -m agentnova skills
20
+
21
+ Written by VTSTech · https://www.vts-tech.org · https://github.com/VTSTech/AgentNova
22
+ """
23
+
24
+ from agentnova.cli import main
25
+
26
+ if __name__ == "__main__":
27
+ main()
@@ -1,5 +1,5 @@
1
1
  """
2
- 🦞 AgentNova ACP Plugin - Bridge to Agent Control Panel
2
+ ⚛️ AgentNova ACP Plugin - Bridge to Agent Control Panel
3
3
 
4
4
  This plugin connects AgentNova agents to an ACP (Agent Control Panel) server,
5
5
  enabling real-time monitoring, token tracking, and STOP/Resume control.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- ⚛️ AgentNova R04 — Agent Mode
3
+ ⚛️ AgentNova R00 — Agent Mode
4
4
 
5
5
  A goal-driven execution mode where the agent autonomously works through tasks.
6
6
  Unlike chat mode (which is user-driven), agent mode:
@@ -29,6 +29,27 @@ from pathlib import Path
29
29
  from typing import Any, Callable, Optional
30
30
 
31
31
 
32
+ # ANSI color helpers
33
+ def dim(text: str) -> str:
34
+ """Return dimmed text using ANSI escape codes."""
35
+ return f"\033[2m{text}\033[0m"
36
+
37
+
38
+ def green(text: str) -> str:
39
+ """Return green text using ANSI escape codes."""
40
+ return f"\033[32m{text}\033[0m"
41
+
42
+
43
+ def yellow(text: str) -> str:
44
+ """Return yellow text using ANSI escape codes."""
45
+ return f"\033[33m{text}\033[0m"
46
+
47
+
48
+ def cyan(text: str) -> str:
49
+ """Return cyan text using ANSI escape codes."""
50
+ return f"\033[36m{text}\033[0m"
51
+
52
+
32
53
  class AgentState(Enum):
33
54
  """Agent execution states."""
34
55
  IDLE = "idle" # Ready for new tasks
@@ -622,6 +643,14 @@ Example: [{{"description": "Step 1"}}, {{"description": "Step 2"}}]"""
622
643
  # Track the result
623
644
  result_msg = run.final_answer
624
645
 
646
+ # Debug output: show step completion info
647
+ if self.verbose and run:
648
+ tool_calls = [s for s in run.steps if s.type == "tool_call"]
649
+ tool_names = list(set(s.tool_name for s in tool_calls if hasattr(s, 'tool_name')))
650
+ print(dim(f" ⏱️ {len(run.steps)} steps, {len(tool_calls)} tool calls, {run.total_ms:.0f}ms"))
651
+ if tool_names:
652
+ print(dim(f" 🔧 Tools used: {', '.join(tool_names)}"))
653
+
625
654
  # Log step completion
626
655
  step.status = "done"
627
656
  step.completed_at = datetime.now().isoformat()
@@ -1,151 +1,151 @@
1
- #!/usr/bin/env python3
2
- """
3
- 🦞 AgentNova R04 — BitnetClient
4
- Drop-in replacement for OllamaClient that uses Microsoft's bitnet.cpp
5
- (llama-server) as the inference backend instead of Ollama.
6
-
7
- Architecture:
8
- - Wraps bitnet.cpp's llama-server HTTP process
9
- - Exposes the same interface as OllamaClient: chat(), list_models(),
10
- model_supports_tools(), is_running()
11
- - Normalises OpenAI-format responses → Ollama-format so agent.py is
12
- completely unmodified
13
- - Manages llama-server lifecycle (start/stop/health-check)
14
- - Falls back gracefully to ReAct tool-calling (no native function
15
- calling in current bitnet models)
16
-
17
- Supported models (as of bitnet.cpp 2025):
18
- - microsoft/BitNet-b1.58-2B-4T (~0.4 GB, recommended)
19
- - 1bitLLM/bitnet_b1_58-3B (~0.7 GB)
20
- - HF1BitLLM/Llama3-8B-1.58-100B-tokens
21
- - tiiuae/Falcon3-1B-Instruct-1.58bit
22
- - tiiuae/Falcon3-3B-Instruct-1.58bit
23
- - tiiuae/Falcon3-7B-Instruct-1.58bit
24
- """
25
-
26
- import json
27
- import urllib.request
28
- from .config import BITNET_BASE_URL
29
-
30
- # Known BitNet model identifiers
31
- KNOWN_MODELS = [
32
- "bitnet-b1.58-2b-4t",
33
- "BitNet-b1.58-2B-4T",
34
- "bitnet-b1.58-large",
35
- ]
36
-
37
- class BitnetClient:
38
- def __init__(self, base_url=None, timeout=120):
39
- # Prioritize passed URL, fallback to config
40
- self.base_url = base_url or BITNET_BASE_URL
41
- self.timeout = timeout
42
-
43
- def is_running(self) -> bool:
44
- try:
45
- with urllib.request.urlopen(f"{self.base_url}/health", timeout=2) as resp:
46
- return resp.getcode() == 200
47
- except:
48
- return False
49
-
50
- def list_models(self):
51
- try:
52
- with urllib.request.urlopen(f"{self.base_url}/v1/models", timeout=5) as resp:
53
- if resp.getcode() == 200:
54
- data = json.loads(resp.read().decode("utf-8"))
55
- # Return the ID of the model(s), cleaned up to show just model folder/filename
56
- models = []
57
- for m in data.get('data', []):
58
- model_id = m['id']
59
- # Strip common prefixes to show just model_dir/filename.gguf
60
- # e.g., /content/BitNet/models/BitNet-b1.58-2B-4T/bitnet_2b_i2_s.gguf
61
- # -> BitNet-b1.58-2B-4T/bitnet_2b_i2_s.gguf
62
- if '/models/' in model_id:
63
- # Keep everything after /models/
64
- model_id = model_id.split('/models/')[-1]
65
- elif model_id.startswith('/'):
66
- # Just keep last two path components
67
- parts = model_id.strip('/').split('/')
68
- if len(parts) >= 2:
69
- model_id = '/'.join(parts[-2:])
70
- models.append(model_id)
71
- return models
72
- except:
73
- return []
74
-
75
- def chat(
76
- self,
77
- model: str,
78
- messages: list[dict],
79
- options: dict | None = None,
80
- stream: bool = False,
81
- tools: list | None = None, # Signature fix for agent.py
82
- **kwargs, # Catch-all for extra agent args
83
- ):
84
- """
85
- Chat completion for BitNet.
86
-
87
- Note: BitNet llama-server doesn't support streaming in the same way
88
- as Ollama. When stream=True, this yields tokens from a streaming
89
- response. Otherwise returns a complete response dict.
90
- """
91
- url = f"{self.base_url}/v1/chat/completions"
92
-
93
- data = {
94
- "model": model,
95
- "messages": messages,
96
- "stream": stream,
97
- "temperature": (options or {}).get("temperature", 0.7),
98
- }
99
-
100
- req = urllib.request.Request(
101
- url,
102
- data=json.dumps(data).encode("utf-8"),
103
- headers={"Content-Type": "application/json"}
104
- )
105
-
106
- if stream:
107
- # Return a generator for streaming
108
- return self._stream_response(req, model)
109
- else:
110
- # Non-streaming: return complete response
111
- with urllib.request.urlopen(req, timeout=self.timeout) as resp:
112
- result = json.loads(resp.read().decode("utf-8"))
113
- # Normalize OpenAI completion to Ollama format for the Agent
114
- return {
115
- "model": model,
116
- "message": {
117
- "role": "assistant",
118
- "content": result["choices"][0]["message"]["content"]
119
- },
120
- "done": True
121
- }
122
-
123
- def _stream_response(self, req, model):
124
- """
125
- Handle SSE streaming response from BitNet server.
126
- Yields tokens one at a time.
127
- """
128
- with urllib.request.urlopen(req, timeout=self.timeout) as resp:
129
- buffer = ""
130
- for line in resp:
131
- line = line.decode("utf-8")
132
- if line.startswith("data: "):
133
- data_str = line[6:].strip()
134
- if data_str == "[DONE]":
135
- break
136
- try:
137
- chunk = json.loads(data_str)
138
- if "choices" in chunk and len(chunk["choices"]) > 0:
139
- delta = chunk["choices"][0].get("delta", {})
140
- content = delta.get("content", "")
141
- if content:
142
- yield content
143
- except json.JSONDecodeError:
144
- continue
145
-
146
- def model_supports_tools(self, model: str) -> bool:
147
- return False # BitNet models require ReAct fallback in Agent.py
148
-
149
- def supports_streaming(self) -> bool:
150
- """Return True if this client supports streaming."""
1
+ #!/usr/bin/env python3
2
+ """
3
+ ⚛️ AgentNova R00 — BitnetClient
4
+ Drop-in replacement for OllamaClient that uses Microsoft's bitnet.cpp
5
+ (llama-server) as the inference backend instead of Ollama.
6
+
7
+ Architecture:
8
+ - Wraps bitnet.cpp's llama-server HTTP process
9
+ - Exposes the same interface as OllamaClient: chat(), list_models(),
10
+ model_supports_tools(), is_running()
11
+ - Normalises OpenAI-format responses → Ollama-format so agent.py is
12
+ completely unmodified
13
+ - Manages llama-server lifecycle (start/stop/health-check)
14
+ - Falls back gracefully to ReAct tool-calling (no native function
15
+ calling in current bitnet models)
16
+
17
+ Supported models (as of bitnet.cpp 2025):
18
+ - microsoft/BitNet-b1.58-2B-4T (~0.4 GB, recommended)
19
+ - 1bitLLM/bitnet_b1_58-3B (~0.7 GB)
20
+ - HF1BitLLM/Llama3-8B-1.58-100B-tokens
21
+ - tiiuae/Falcon3-1B-Instruct-1.58bit
22
+ - tiiuae/Falcon3-3B-Instruct-1.58bit
23
+ - tiiuae/Falcon3-7B-Instruct-1.58bit
24
+ """
25
+
26
+ import json
27
+ import urllib.request
28
+ from .config import BITNET_BASE_URL
29
+
30
+ # Known BitNet model identifiers
31
+ KNOWN_MODELS = [
32
+ "bitnet-b1.58-2b-4t",
33
+ "BitNet-b1.58-2B-4T",
34
+ "bitnet-b1.58-large",
35
+ ]
36
+
37
+ class BitnetClient:
38
+ def __init__(self, base_url=None, timeout=120):
39
+ # Prioritize passed URL, fallback to config
40
+ self.base_url = base_url or BITNET_BASE_URL
41
+ self.timeout = timeout
42
+
43
+ def is_running(self) -> bool:
44
+ try:
45
+ with urllib.request.urlopen(f"{self.base_url}/health", timeout=2) as resp:
46
+ return resp.getcode() == 200
47
+ except:
48
+ return False
49
+
50
+ def list_models(self):
51
+ try:
52
+ with urllib.request.urlopen(f"{self.base_url}/v1/models", timeout=5) as resp:
53
+ if resp.getcode() == 200:
54
+ data = json.loads(resp.read().decode("utf-8"))
55
+ # Return the ID of the model(s), cleaned up to show just model folder/filename
56
+ models = []
57
+ for m in data.get('data', []):
58
+ model_id = m['id']
59
+ # Strip common prefixes to show just model_dir/filename.gguf
60
+ # e.g., /content/BitNet/models/BitNet-b1.58-2B-4T/bitnet_2b_i2_s.gguf
61
+ # -> BitNet-b1.58-2B-4T/bitnet_2b_i2_s.gguf
62
+ if '/models/' in model_id:
63
+ # Keep everything after /models/
64
+ model_id = model_id.split('/models/')[-1]
65
+ elif model_id.startswith('/'):
66
+ # Just keep last two path components
67
+ parts = model_id.strip('/').split('/')
68
+ if len(parts) >= 2:
69
+ model_id = '/'.join(parts[-2:])
70
+ models.append(model_id)
71
+ return models
72
+ except:
73
+ return []
74
+
75
+ def chat(
76
+ self,
77
+ model: str,
78
+ messages: list[dict],
79
+ options: dict | None = None,
80
+ stream: bool = False,
81
+ tools: list | None = None, # Signature fix for agent.py
82
+ **kwargs, # Catch-all for extra agent args
83
+ ):
84
+ """
85
+ Chat completion for BitNet.
86
+
87
+ Note: BitNet llama-server doesn't support streaming in the same way
88
+ as Ollama. When stream=True, this yields tokens from a streaming
89
+ response. Otherwise returns a complete response dict.
90
+ """
91
+ url = f"{self.base_url}/v1/chat/completions"
92
+
93
+ data = {
94
+ "model": model,
95
+ "messages": messages,
96
+ "stream": stream,
97
+ "temperature": (options or {}).get("temperature", 0.7),
98
+ }
99
+
100
+ req = urllib.request.Request(
101
+ url,
102
+ data=json.dumps(data).encode("utf-8"),
103
+ headers={"Content-Type": "application/json"}
104
+ )
105
+
106
+ if stream:
107
+ # Return a generator for streaming
108
+ return self._stream_response(req, model)
109
+ else:
110
+ # Non-streaming: return complete response
111
+ with urllib.request.urlopen(req, timeout=self.timeout) as resp:
112
+ result = json.loads(resp.read().decode("utf-8"))
113
+ # Normalize OpenAI completion to Ollama format for the Agent
114
+ return {
115
+ "model": model,
116
+ "message": {
117
+ "role": "assistant",
118
+ "content": result["choices"][0]["message"]["content"]
119
+ },
120
+ "done": True
121
+ }
122
+
123
+ def _stream_response(self, req, model):
124
+ """
125
+ Handle SSE streaming response from BitNet server.
126
+ Yields tokens one at a time.
127
+ """
128
+ with urllib.request.urlopen(req, timeout=self.timeout) as resp:
129
+ buffer = ""
130
+ for line in resp:
131
+ line = line.decode("utf-8")
132
+ if line.startswith("data: "):
133
+ data_str = line[6:].strip()
134
+ if data_str == "[DONE]":
135
+ break
136
+ try:
137
+ chunk = json.loads(data_str)
138
+ if "choices" in chunk and len(chunk["choices"]) > 0:
139
+ delta = chunk["choices"][0].get("delta", {})
140
+ content = delta.get("content", "")
141
+ if content:
142
+ yield content
143
+ except json.JSONDecodeError:
144
+ continue
145
+
146
+ def model_supports_tools(self, model: str) -> bool:
147
+ return False # BitNet models require ReAct fallback in Agent.py
148
+
149
+ def supports_streaming(self) -> bool:
150
+ """Return True if this client supports streaming."""
151
151
  return True