agentnova 0.2.3__tar.gz → 0.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. {agentnova-0.2.3 → agentnova-0.2.4}/PKG-INFO +5 -3
  2. {agentnova-0.2.3 → agentnova-0.2.4}/README.md +4 -2
  3. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/__init__.py +2 -2
  4. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/core/agent.py +36 -20
  5. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/core/model_family_config.py +17 -12
  6. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/15_quick_diagnostic.py +59 -4
  7. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova.egg-info/PKG-INFO +5 -3
  8. {agentnova-0.2.3 → agentnova-0.2.4}/pyproject.toml +1 -1
  9. {agentnova-0.2.3 → agentnova-0.2.4}/LICENSE +0 -0
  10. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/__main__.py +0 -0
  11. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/acp_plugin.py +0 -0
  12. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/agent_mode.py +0 -0
  13. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/bitnet_client.py +0 -0
  14. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/bitnet_setup.py +0 -0
  15. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/cli.py +0 -0
  16. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/config.py +0 -0
  17. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/core/math_prompts.py +0 -0
  18. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/core/memory.py +0 -0
  19. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/core/ollama_client.py +0 -0
  20. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/core/orchestrator.py +0 -0
  21. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/core/orchestrator_enhanced.py +0 -0
  22. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/core/tools.py +0 -0
  23. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/00_backend_demo.py +0 -0
  24. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/01_basic_agent.py +0 -0
  25. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/02_tool_agent.py +0 -0
  26. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/03_orchestrator.py +0 -0
  27. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/04_comprehensive_test.py +0 -0
  28. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/05_tool_tests.py +0 -0
  29. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/06_interactive_chat.py +0 -0
  30. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/07_model_comparison.py +0 -0
  31. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/08_robust_comparison.py +0 -0
  32. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/09_expanded_benchmark.py +0 -0
  33. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/10_skills_demo.py +0 -0
  34. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/11_skill_creator_test.py +0 -0
  35. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/12_batch_operations.py +0 -0
  36. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/13_shutdown_demo.py +0 -0
  37. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/examples/14_gsm8k_benchmark.py +0 -0
  38. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/model_discovery.py +0 -0
  39. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/shared_args.py +0 -0
  40. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/__init__.py +0 -0
  41. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/acp/SKILL.md +0 -0
  42. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/datetime/SKILL.md +0 -0
  43. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/loader.py +0 -0
  44. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/SKILL.md +0 -0
  45. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/__init__.py +0 -0
  46. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/aggregate_benchmark.py +0 -0
  47. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/generate_report.py +0 -0
  48. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/improve_description.py +0 -0
  49. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/init_skill.py +0 -0
  50. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/package_skill.py +0 -0
  51. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/quick_validate.py +0 -0
  52. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/run_eval.py +0 -0
  53. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/run_loop.py +0 -0
  54. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/security_scan.py +0 -0
  55. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/test_package_skill.py +0 -0
  56. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/test_quick_validate.py +0 -0
  57. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/utils.py +0 -0
  58. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/skill-creator/scripts/validate.py +0 -0
  59. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/skills/web_search/SKILL.md +0 -0
  60. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/tools/builtins.py +0 -0
  61. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova/tools/sandboxed_repl.py +0 -0
  62. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova.egg-info/SOURCES.txt +0 -0
  63. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova.egg-info/dependency_links.txt +0 -0
  64. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova.egg-info/entry_points.txt +0 -0
  65. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova.egg-info/requires.txt +0 -0
  66. {agentnova-0.2.3 → agentnova-0.2.4}/agentnova.egg-info/top_level.txt +0 -0
  67. {agentnova-0.2.3 → agentnova-0.2.4}/localclaw/__init__.py +0 -0
  68. {agentnova-0.2.3 → agentnova-0.2.4}/localclaw/__main__.py +0 -0
  69. {agentnova-0.2.3 → agentnova-0.2.4}/setup.cfg +0 -0
  70. {agentnova-0.2.3 → agentnova-0.2.4}/tests/test_acp_integration.py +0 -0
  71. {agentnova-0.2.3 → agentnova-0.2.4}/tests/test_acp_subagents.py +0 -0
  72. {agentnova-0.2.3 → agentnova-0.2.4}/tests/test_agent.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentnova
3
- Version: 0.2.3
3
+ Version: 0.2.4
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 R02.3
37
+ # ⚛️ AgentNova R02.4
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
 
@@ -44,7 +44,9 @@ Inspired by the architecture of OpenClaw, rebuilt from scratch for local-first o
44
44
 
45
45
  [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/VTSTech/AgentNova/blob/main/AgentNova.ipynb)
46
46
  [![GitHub commits](https://badgen.net/github/commits/VTSTech/AgentNova)](https://GitHub.com/VTSTech/AgentNova/commit/) [![GitHub latest commit](https://badgen.net/github/last-commit/VTSTech/AgentNova)](https://GitHub.com/VTSTech/AgentNova/commit/)
47
- [![pip - agentnova](https://img.shields.io/badge/pip-agentnova-2ea44f?logo=PyPi)](https://pypi.org/project/agentnova/)
47
+
48
+ [![pip - agentnova](https://img.shields.io/badge/pip-agentnova-2ea44f?logo=PyPi)](https://pypi.org/project/agentnova/) [![PyPI version fury.io](https://badge.fury.io/py/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![PyPI download month](https://img.shields.io/pypi/dm/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/)
49
+
48
50
  [![License](https://img.shields.io/badge/License-MIT-blue)](#license) [![Go to Python website](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2FVTSTech%2FAgentNova%2Frefs%2Fheads%2Fmain%2Fpyproject.toml&query=project.requires-python&label=python&logo=python&logoColor=white)](https://python.org)
49
51
 
50
52
  ---
@@ -1,4 +1,4 @@
1
- # ⚛️ AgentNova R02.3
1
+ # ⚛️ AgentNova R02.4
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
 
@@ -8,7 +8,9 @@ Inspired by the architecture of OpenClaw, rebuilt from scratch for local-first o
8
8
 
9
9
  [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/VTSTech/AgentNova/blob/main/AgentNova.ipynb)
10
10
  [![GitHub commits](https://badgen.net/github/commits/VTSTech/AgentNova)](https://GitHub.com/VTSTech/AgentNova/commit/) [![GitHub latest commit](https://badgen.net/github/last-commit/VTSTech/AgentNova)](https://GitHub.com/VTSTech/AgentNova/commit/)
11
- [![pip - agentnova](https://img.shields.io/badge/pip-agentnova-2ea44f?logo=PyPi)](https://pypi.org/project/agentnova/)
11
+
12
+ [![pip - agentnova](https://img.shields.io/badge/pip-agentnova-2ea44f?logo=PyPi)](https://pypi.org/project/agentnova/) [![PyPI version fury.io](https://badge.fury.io/py/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![PyPI download month](https://img.shields.io/pypi/dm/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/)
13
+
12
14
  [![License](https://img.shields.io/badge/License-MIT-blue)](#license) [![Go to Python website](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2FVTSTech%2FAgentNova%2Frefs%2Fheads%2Fmain%2Fpyproject.toml&query=project.requires-python&label=python&logo=python&logoColor=white)](https://python.org)
13
15
 
14
16
  ---
@@ -204,8 +204,8 @@ __all__ = [
204
204
  if _BITNET_AVAILABLE:
205
205
  __all__.extend(["BitnetClient", "KNOWN_MODELS"])
206
206
 
207
- __version__ = "0.2.2"
207
+ __version__ = "0.2.4"
208
208
  __author__ = "VTSTech"
209
- __author_email__ = "contact@vts-tech.org"
209
+ __author_email__ = "veritas@vts-tech.org"
210
210
  __url__ = "https://github.com/VTSTech/AgentNova"
211
211
  __website__ = "https://www.vts-tech.org"
@@ -1847,7 +1847,9 @@ class Agent:
1847
1847
  # ── Immediate synthesize after successful tool result ───────────── #
1848
1848
  # Prevents small models re-invoking tools when the answer is in hand.
1849
1849
  # Triggered when: a tool succeeded AND the query is simple (date, math, etc.)
1850
- if _successful_results and _is_simple_answered_query(user_input, _successful_results):
1850
+ # NOTE: Skip for ReAct mode - ReAct has its own "Final Answer" mechanism
1851
+ # that we need to respect (model may output "Final Answer: X" after tools)
1852
+ if _successful_results and _is_simple_answered_query(user_input, _successful_results) and self._native_tools:
1851
1853
  final_answer = self._synthesize(user_input, _successful_results)
1852
1854
  final_step = StepResult(type="final", content=final_answer, elapsed_ms=elapsed)
1853
1855
  run.steps.append(final_step)
@@ -2558,6 +2560,21 @@ class Agent:
2558
2560
  else:
2559
2561
  fallback_text = content
2560
2562
 
2563
+ # For ReAct mode: if model output looks incomplete (just "Thought: ..." without Final Answer)
2564
+ # and we have a numeric result, use the numeric result directly
2565
+ if not self._native_tools and _successful_results:
2566
+ content_stripped = content.strip()
2567
+ # Check if content is just a thought without actual answer
2568
+ if content_stripped.startswith("Thought:") and "Final Answer:" not in content_stripped:
2569
+ # Try to use numeric fallback
2570
+ try:
2571
+ fallback_num = float(fallback_text)
2572
+ if self.debug:
2573
+ print(f"\n 🔍 DEBUG: ReAct thought without Final Answer, using numeric result: {fallback_num}")
2574
+ content = fallback_text
2575
+ except (ValueError, TypeError):
2576
+ pass # Fall through to normal processing
2577
+
2561
2578
  if self.debug:
2562
2579
  print(f"\n 🔍 DEBUG: Final answer processing")
2563
2580
  print(f" content (before clean)={content!r}")
@@ -2594,25 +2611,24 @@ class Agent:
2594
2611
  return "I was unable to complete this task with the available tools."
2595
2612
 
2596
2613
  results_text = "\n".join(f"- {r}" for r in results)
2597
-
2598
- # For simple numeric results, use directly without LLM synthesis
2599
- # This prevents the model from repeating its pre-tool-call wrong answer
2600
- if len(results) == 1:
2601
- r_clean = _strip_tool_prefix(results[0])
2614
+
2615
+ # For numeric results, use the LAST one directly without LLM synthesis
2616
+ # NOTE: We no longer try to detect incomplete multi-step calculations
2617
+ # because we can't tell if the model computed the full expression or not
2618
+ # (e.g., "8 * 7 - 5" vs just "8 * 7"). Returning the numeric result directly
2619
+ # is safer than forcing LLM synthesis which might hallucinate.
2620
+ r_clean = _strip_tool_prefix(results[-1])
2621
+ if self.debug:
2622
+ print(f" synthesize: checking if numeric: '{r_clean}'")
2623
+ try:
2624
+ num = float(r_clean)
2602
2625
  if self.debug:
2603
- print(f" synthesize: checking if numeric: '{r_clean}'")
2604
- # Check if result is just a number (possibly with decimal)
2605
- try:
2606
- num = float(r_clean)
2607
- # It's a number - construct a simple answer
2608
- if self.debug:
2609
- print(f" synthesize: using numeric result directly: {num}")
2610
- # Return just the number as string - test expects this
2611
- return r_clean
2612
- except (ValueError, TypeError) as e:
2613
- if self.debug:
2614
- print(f" synthesize: not numeric: {e}")
2615
- pass
2626
+ print(f" synthesize: using numeric result directly: {num}")
2627
+ return r_clean
2628
+ except (ValueError, TypeError):
2629
+ if self.debug:
2630
+ print(f" synthesize: not numeric: could not convert string to float: '{r_clean}'")
2631
+ pass
2616
2632
 
2617
2633
  # Check if any result already looks like a complete answer
2618
2634
  # (starts with common answer patterns and is reasonably short)
@@ -2632,7 +2648,7 @@ class Agent:
2632
2648
 
2633
2649
  if self.debug:
2634
2650
  print(f" synthesize: making LLM call to summarize {len(results)} results...")
2635
-
2651
+
2636
2652
  # For synthesis, don't include the model's potentially wrong pre-tool responses
2637
2653
  # Just use the system prompt and the synthesis request
2638
2654
  synthesis_messages = [
@@ -226,23 +226,28 @@ Answer: 8""",
226
226
  few_shot_style="compact",
227
227
  reasoning_hints=["Be direct and helpful", "Follow instructions precisely"],
228
228
  # Optimized prompt for pure reasoning (Dolphin has no tool support)
229
- no_tools_system_prompt="""You are Dolphin, a helpful AI assistant. Solve math problems directly.
229
+ no_tools_system_prompt="""You are Dolphin, a helpful AI assistant.
230
230
 
231
- Calculate step by step. Give the final number as your answer.
231
+ Be concise and direct. For math, show the calculation then the answer.
232
232
 
233
233
  Examples:
234
- User: What is 15 plus 27?
235
- 15 + 27 = 42
236
- Answer: 42
234
+ Q: What is 15 plus 27?
235
+ A: 15 + 27 = 42
237
236
 
238
- User: What is 8 times 7 minus 5?
239
- 8 * 7 = 56
240
- 56 - 5 = 51
241
- Answer: 51
237
+ Q: What is 17 divided by 4?
238
+ A: 17 / 4 = 4.25
242
239
 
243
- User: A store has 24 apples. They sell 8 and 6.
244
- 24 - 8 - 6 = 10
245
- Answer: 10""",
240
+ Q: A store has 24 apples. They sell 8 and 6. How many left?
241
+ A: 24 - 8 - 6 = 10
242
+
243
+ Q: A store opens at 9 AM and closes at 5 PM. How many hours?
244
+ A: 17 - 9 = 8 hours
245
+
246
+ Q: What is the capital of Japan?
247
+ A: Tokyo
248
+
249
+ Q: Is 15 greater than 10?
250
+ A: Yes""",
246
251
  ),
247
252
  }
248
253
 
@@ -48,6 +48,15 @@ config = parse_shared_args(args)
48
48
 
49
49
  DEBUG = config.debug
50
50
 
51
+ # Check for optional ACP support
52
+ USE_ACP = config.acp
53
+ if USE_ACP:
54
+ try:
55
+ from agentnova.acp_plugin import ACPPlugin
56
+ except ImportError:
57
+ print("⚠️ ACP requested but ACPPlugin not available")
58
+ USE_ACP = False
59
+
51
60
  # Quick 5-question diagnostic test
52
61
  # Each question targets a specific failure mode
53
62
  TESTS = [
@@ -101,7 +110,7 @@ def make_step_callback(verbose: bool = True):
101
110
  return on_step
102
111
 
103
112
 
104
- def test_model(client, model: str, config: SharedConfig) -> dict:
113
+ def test_model(client, model: str, config: SharedConfig, acp=None) -> dict:
105
114
  """Test a single model and return results."""
106
115
  print(f"\n{'='*60}")
107
116
  print(f"🧪 Quick Diagnostic: {model}")
@@ -109,6 +118,10 @@ def test_model(client, model: str, config: SharedConfig) -> dict:
109
118
 
110
119
  results = {"model": model, "passed": 0, "total": len(TESTS), "time": 0, "tests": {}}
111
120
 
121
+ # Log test start to ACP
122
+ if acp:
123
+ acp.log_chat("system", f"Quick Diagnostic started for {model}", complete=True)
124
+
112
125
  for i, (test_name, prompt, tools, expected) in enumerate(TESTS):
113
126
  print(f" {test_name}...", end=" ", flush=True)
114
127
 
@@ -137,7 +150,7 @@ After getting the result, provide the final answer as a number."""
137
150
  max_steps=5,
138
151
  client=fresh_client,
139
152
  model_options=model_opts,
140
- force_react=(tool_support == "react"),
153
+ force_react=config.force_react or (tool_support == "react"),
141
154
  on_step=make_step_callback(DEBUG),
142
155
  debug=DEBUG,
143
156
  )
@@ -178,10 +191,20 @@ After getting the result, provide the final answer as a number."""
178
191
  else:
179
192
  print(f"❌ ({elapsed:.1f}s)")
180
193
  print(f" Expected: {expected}, Got: {response[:80]}")
194
+
195
+ # Log to ACP
196
+ if acp:
197
+ acp.log_chat(
198
+ "assistant" if passed else "error",
199
+ f"{test_name}: {'PASS' if passed else 'FAIL'} - Expected: {expected}, Got: {response[:50]}",
200
+ complete=True
201
+ )
181
202
 
182
203
  except Exception as e:
183
204
  results["tests"][test_name] = {"passed": False, "error": str(e)[:100]}
184
205
  print(f"❌ ERROR: {str(e)[:50]}")
206
+ if acp:
207
+ acp.log_chat("error", f"{test_name}: ERROR - {str(e)[:50]}", complete=True)
185
208
 
186
209
  pass_rate = results["passed"] / results["total"] * 100
187
210
  print(f"\n 📊 Result: {results['passed']}/{results['total']} ({pass_rate:.0f}%) in {results['time']:.1f}s")
@@ -199,8 +222,22 @@ def main():
199
222
  from agentnova.model_discovery import get_available_models
200
223
  available = list(dict.fromkeys(get_available_models(client)))
201
224
 
225
+ # Create main ACP instance if enabled
226
+ main_acp = None
227
+ acp_connected = False
228
+ if USE_ACP:
229
+ main_acp = ACPPlugin(
230
+ agent_name="AgentNova",
231
+ model_name="quick-diagnostic",
232
+ debug=DEBUG,
233
+ )
234
+ bootstrap = main_acp.bootstrap(claim_primary=False)
235
+ acp_connected = bootstrap.get("status") is not None
236
+
202
237
  print(f"\n⚛️ AgentNova Quick Diagnostic (5 questions)")
203
238
  print(f" Available models: {', '.join(available[:5])}{'...' if len(available) > 5 else ''}")
239
+ if USE_ACP:
240
+ print(f" ACP: {'connected' if acp_connected else 'unavailable'}")
204
241
 
205
242
  # Determine which models to test
206
243
  if config.model:
@@ -222,7 +259,17 @@ def main():
222
259
 
223
260
  all_results = []
224
261
  for model in models_to_test:
225
- result = test_model(client, model, config)
262
+ # Create model-specific ACP instance if enabled
263
+ model_acp = None
264
+ if USE_ACP and acp_connected:
265
+ model_acp = ACPPlugin(
266
+ agent_name="AgentNova",
267
+ model_name=model.split(':')[0][:25],
268
+ debug=DEBUG,
269
+ )
270
+ model_acp.bootstrap(claim_primary=False)
271
+
272
+ result = test_model(client, model, config, acp=model_acp)
226
273
  all_results.append(result)
227
274
 
228
275
  # Summary
@@ -231,10 +278,18 @@ def main():
231
278
  print("🏆 RANKINGS")
232
279
  print(f"{'='*60}")
233
280
  sorted_results = sorted(all_results, key=lambda x: (-x["passed"], x["time"]))
281
+ summary_lines = []
234
282
  for i, r in enumerate(sorted_results, 1):
235
283
  medal = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else " "
236
284
  rate = r["passed"] / r["total"] * 100
237
- print(f"{medal} {r['model']:<35} {r['passed']}/{r['total']} ({rate:.0f}%) - {r['time']:.1f}s")
285
+ line = f"{medal} {r['model']:<35} {r['passed']}/{r['total']} ({rate:.0f}%) - {r['time']:.1f}s"
286
+ print(line)
287
+ summary_lines.append(f"{r['model']}: {r['passed']}/{r['total']} ({rate:.0f}%)")
288
+
289
+ # Log summary to ACP
290
+ if main_acp and acp_connected:
291
+ summary_text = "Quick Diagnostic Rankings:\n" + "\n".join(summary_lines)
292
+ main_acp.log_chat("system", summary_text, complete=True)
238
293
 
239
294
 
240
295
  if __name__ == "__main__":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentnova
3
- Version: 0.2.3
3
+ Version: 0.2.4
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 R02.3
37
+ # ⚛️ AgentNova R02.4
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
 
@@ -44,7 +44,9 @@ Inspired by the architecture of OpenClaw, rebuilt from scratch for local-first o
44
44
 
45
45
  [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/VTSTech/AgentNova/blob/main/AgentNova.ipynb)
46
46
  [![GitHub commits](https://badgen.net/github/commits/VTSTech/AgentNova)](https://GitHub.com/VTSTech/AgentNova/commit/) [![GitHub latest commit](https://badgen.net/github/last-commit/VTSTech/AgentNova)](https://GitHub.com/VTSTech/AgentNova/commit/)
47
- [![pip - agentnova](https://img.shields.io/badge/pip-agentnova-2ea44f?logo=PyPi)](https://pypi.org/project/agentnova/)
47
+
48
+ [![pip - agentnova](https://img.shields.io/badge/pip-agentnova-2ea44f?logo=PyPi)](https://pypi.org/project/agentnova/) [![PyPI version fury.io](https://badge.fury.io/py/agentnova.svg)](https://pypi.python.org/pypi/agentnova/) [![PyPI download month](https://img.shields.io/pypi/dm/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/)
49
+
48
50
  [![License](https://img.shields.io/badge/License-MIT-blue)](#license) [![Go to Python website](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2FVTSTech%2FAgentNova%2Frefs%2Fheads%2Fmain%2Fpyproject.toml&query=project.requires-python&label=python&logo=python&logoColor=white)](https://python.org)
49
51
 
50
52
  ---
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentnova"
7
- version = "0.2.3"
7
+ version = "0.2.4"
8
8
  description = "A minimal, hackable agentic framework for Ollama and BitNet - local-first AI agent toolkit"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes
File without changes
File without changes
File without changes