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.
- {agentnova-0.0 → agentnova-0.2}/LICENSE +20 -20
- {agentnova-0.0 → agentnova-0.2}/PKG-INFO +17 -15
- {agentnova-0.0 → agentnova-0.2}/README.md +16 -14
- {agentnova-0.0 → agentnova-0.2}/agentnova/__init__.py +15 -2
- {agentnova-0.0 → agentnova-0.2}/agentnova/__main__.py +27 -27
- {agentnova-0.0 → agentnova-0.2}/agentnova/acp_plugin.py +1 -1
- {agentnova-0.0 → agentnova-0.2}/agentnova/agent_mode.py +30 -1
- {agentnova-0.0 → agentnova-0.2}/agentnova/bitnet_client.py +150 -150
- {agentnova-0.0 → agentnova-0.2}/agentnova/cli.py +102 -17
- {agentnova-0.0 → agentnova-0.2}/agentnova/core/agent.py +848 -79
- {agentnova-0.0 → agentnova-0.2}/agentnova/core/math_prompts.py +396 -396
- {agentnova-0.0 → agentnova-0.2}/agentnova/core/memory.py +191 -191
- agentnova-0.2/agentnova/core/model_family_config.py +381 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/core/ollama_client.py +531 -531
- {agentnova-0.0 → agentnova-0.2}/agentnova/core/orchestrator.py +190 -190
- {agentnova-0.0 → agentnova-0.2}/agentnova/core/orchestrator_enhanced.py +393 -393
- {agentnova-0.0 → agentnova-0.2}/agentnova/core/tools.py +303 -274
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/00_backend_demo.py +2 -2
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/04_comprehensive_test.py +11 -2
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/05_tool_tests.py +13 -4
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/07_model_comparison.py +88 -5
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/08_robust_comparison.py +94 -7
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/09_expanded_benchmark.py +50 -5
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/10_skills_demo.py +2 -2
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/11_skill_creator_test.py +3 -3
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/12_batch_operations.py +1 -1
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/13_shutdown_demo.py +1 -1
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/14_gsm8k_benchmark.py +2 -2
- {agentnova-0.0 → agentnova-0.2}/agentnova/model_discovery.py +341 -341
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/__init__.py +24 -24
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/loader.py +444 -444
- {agentnova-0.0 → agentnova-0.2}/agentnova/tools/builtins.py +692 -666
- agentnova-0.2/agentnova/tools/sandboxed_repl.py +523 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/PKG-INFO +17 -15
- {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/SOURCES.txt +2 -0
- {agentnova-0.0 → agentnova-0.2}/localclaw/__init__.py +1 -1
- {agentnova-0.0 → agentnova-0.2}/pyproject.toml +1 -1
- {agentnova-0.0 → agentnova-0.2}/tests/test_acp_integration.py +217 -217
- {agentnova-0.0 → agentnova-0.2}/tests/test_acp_subagents.py +310 -310
- {agentnova-0.0 → agentnova-0.2}/agentnova/bitnet_setup.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/config.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/01_basic_agent.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/02_tool_agent.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/03_orchestrator.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/examples/06_interactive_chat.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/shared_args.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/acp/SKILL.md +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/datetime/SKILL.md +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/SKILL.md +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/__init__.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/aggregate_benchmark.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/generate_report.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/improve_description.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/init_skill.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/package_skill.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/quick_validate.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/run_eval.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/run_loop.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/security_scan.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/test_package_skill.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/test_quick_validate.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/utils.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/skill-creator/scripts/validate.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova/skills/web_search/SKILL.md +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/dependency_links.txt +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/entry_points.txt +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/requires.txt +0 -0
- {agentnova-0.0 → agentnova-0.2}/agentnova.egg-info/top_level.txt +0 -0
- {agentnova-0.0 → agentnova-0.2}/localclaw/__main__.py +0 -0
- {agentnova-0.0 → agentnova-0.2}/setup.cfg +0 -0
- {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.
|
|
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
|
|
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
|
-
[](https://pypi.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
[](https://pypi.python.org/pypi/agentnova/) [](https://pypi.python.org/pypi/agentnova/) [](https://pypi.python.org/pypi/agentnova/)
|
|
45
|
+
[](https://pypi.org/project/agentnova/) [](https://pypi.python.org/pypi/agentnova/) [](https://GitHub.com/VTSTech/AgentNova/commit/)
|
|
50
46
|
|
|
47
|
+
[](https://pypi.org/project/agentnova/) [](https://pypi.org/project/agentnova/) [](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
|
|
204
|
+
Recent test results with native tool synthesis:
|
|
208
205
|
|
|
209
|
-
| Model | Params | Tool Support |
|
|
210
|
-
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
213
|
-
| `granite4:350m` | 350M | native | ~
|
|
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
|
|
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
|
|
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
|
|
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
|
-
[](https://pypi.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
[](https://pypi.python.org/pypi/agentnova/) [](https://pypi.python.org/pypi/agentnova/) [](https://pypi.python.org/pypi/agentnova/)
|
|
9
|
+
[](https://pypi.org/project/agentnova/) [](https://pypi.python.org/pypi/agentnova/) [](https://GitHub.com/VTSTech/AgentNova/commit/)
|
|
14
10
|
|
|
11
|
+
[](https://pypi.org/project/agentnova/) [](https://pypi.org/project/agentnova/) [](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
|
|
168
|
+
Recent test results with native tool synthesis:
|
|
172
169
|
|
|
173
|
-
| Model | Params | Tool Support |
|
|
174
|
-
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
177
|
-
| `granite4:350m` | 350M | native | ~
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|