agentnova 0.3.6__tar.gz → 0.3.7__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 (89) hide show
  1. {agentnova-0.3.6 → agentnova-0.3.7}/PKG-INFO +42 -12
  2. {agentnova-0.3.6 → agentnova-0.3.7}/README.md +42 -12
  3. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/__init__.py +1 -1
  4. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/agent.py +1 -1
  5. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/backends/__init__.py +5 -4
  6. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/backends/ollama.py +3 -3
  7. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/cli.py +25 -17
  8. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/types.py +5 -5
  9. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/00_basic_agent.py +29 -5
  10. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/01_quick_diagnostic.py +9 -8
  11. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/02_tool_test.py +221 -52
  12. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/03_reasoning_test.py +39 -3
  13. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/04_gsm8k_benchmark.py +39 -3
  14. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/05_common_sense.py +39 -3
  15. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/06_causal_reasoning.py +39 -3
  16. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/07_logical_deduction.py +39 -3
  17. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/08_reading_comprehension.py +39 -3
  18. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/09_general_knowledge.py +39 -3
  19. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/10_implicit_reasoning.py +39 -3
  20. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/11_analogical_reasoning.py +39 -3
  21. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova.egg-info/PKG-INFO +42 -12
  22. {agentnova-0.3.6 → agentnova-0.3.7}/pyproject.toml +1 -1
  23. {agentnova-0.3.6 → agentnova-0.3.7}/LICENSE +0 -0
  24. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/__main__.py +0 -0
  25. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/acp_plugin.py +0 -0
  26. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/agent_mode.py +0 -0
  27. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/backends/base.py +0 -0
  28. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/backends/bitnet.py +0 -0
  29. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/colors.py +0 -0
  30. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/config.py +0 -0
  31. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/__init__.py +0 -0
  32. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/args_normal.py +0 -0
  33. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/error_recovery.py +0 -0
  34. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/helpers.py +0 -0
  35. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/math_prompts.py +0 -0
  36. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/memory.py +0 -0
  37. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/model_config.py +0 -0
  38. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/model_family_config.py +0 -0
  39. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/models.py +0 -0
  40. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/openresponses.py +0 -0
  41. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/prompts.py +0 -0
  42. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/tool_cache.py +0 -0
  43. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/core/tool_parse.py +0 -0
  44. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/examples/__init__.py +0 -0
  45. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/model_discovery.py +0 -0
  46. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/orchestrator.py +0 -0
  47. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/py.typed +0 -0
  48. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/shared_args.py +0 -0
  49. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/__init__.py +0 -0
  50. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/acp/SKILL.md +0 -0
  51. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/datetime/SKILL.md +0 -0
  52. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/loader.py +0 -0
  53. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/SKILL.md +0 -0
  54. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/__init__.py +0 -0
  55. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/aggregate_benchmark.py +0 -0
  56. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/generate_report.py +0 -0
  57. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/improve_description.py +0 -0
  58. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/init_skill.py +0 -0
  59. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/package_skill.py +0 -0
  60. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/quick_validate.py +0 -0
  61. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/run_eval.py +0 -0
  62. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/run_loop.py +0 -0
  63. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/security_scan.py +0 -0
  64. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/test_package_skill.py +0 -0
  65. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/test_quick_validate.py +0 -0
  66. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/utils.py +0 -0
  67. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/skill-creator/scripts/validate.py +0 -0
  68. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/skills/web-search/SKILL.md +0 -0
  69. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/soul/__init__.py +0 -0
  70. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/soul/loader.py +0 -0
  71. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/soul/types.py +0 -0
  72. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/souls/nova-helper/IDENTITY.md +0 -0
  73. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/souls/nova-helper/SOUL.md +0 -0
  74. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/souls/nova-helper/STYLE.md +0 -0
  75. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/souls/nova-helper/soul.json +0 -0
  76. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/tools/__init__.py +0 -0
  77. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/tools/builtins.py +0 -0
  78. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/tools/registry.py +0 -0
  79. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova/tools/sandboxed_repl.py +0 -0
  80. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova.egg-info/SOURCES.txt +0 -0
  81. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova.egg-info/dependency_links.txt +0 -0
  82. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova.egg-info/entry_points.txt +0 -0
  83. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova.egg-info/requires.txt +0 -0
  84. {agentnova-0.3.6 → agentnova-0.3.7}/agentnova.egg-info/top_level.txt +0 -0
  85. {agentnova-0.3.6 → agentnova-0.3.7}/localclaw/__init__.py +0 -0
  86. {agentnova-0.3.6 → agentnova-0.3.7}/localclaw-redirect/localclaw/__init__.py +0 -0
  87. {agentnova-0.3.6 → agentnova-0.3.7}/setup.cfg +0 -0
  88. {agentnova-0.3.6 → agentnova-0.3.7}/tests/test_agent.py +0 -0
  89. {agentnova-0.3.6 → agentnova-0.3.7}/tests/test_spec_compliance.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentnova
3
- Version: 0.3.6
3
+ Version: 0.3.7
4
4
  Summary: ⚛️ AgentNova - A minimal, hackable agentic framework for local LLM inference
5
5
  Author-email: VTSTech <veritas@vts-tech.org>
6
6
  Maintainer-email: VTSTech <veritas@vts-tech.org>
@@ -32,7 +32,7 @@ Requires-Dist: black>=23.0; extra == "dev"
32
32
  Requires-Dist: ruff>=0.1.0; extra == "dev"
33
33
  Dynamic: license-file
34
34
 
35
- # ⚛️ AgentNova R03.6
35
+ # ⚛️ AgentNova R03.7
36
36
 
37
37
  **Status: Alpha**
38
38
 
@@ -63,7 +63,7 @@ Inspired by the architecture of OpenClaw, rebuilt from scratch for local-first o
63
63
 
64
64
  - **Zero dependencies** — Uses Python stdlib only (urllib for HTTP)
65
65
  - **Ollama + BitNet backends** — Switch with `--backend` flag
66
- - **Dual API support** — OpenResponses (`--api resp`) and Chat-Completions (`--api comp`)
66
+ - **Dual API support** — OpenResponses (`--api openre`) and OpenAI Chat-Completions (`--api openai`)
67
67
  - **Three-tier tool support** — Native, ReAct, or none (auto-detected)
68
68
  - **Small model optimized** — Fuzzy matching, argument normalization
69
69
  - **Built-in security** — Path validation, command blocklist, SSRF protection
@@ -99,8 +99,8 @@ agentnova chat -m qwen2.5:0.5b --tools calculator,shell
99
99
  # Autonomous agent mode
100
100
  agentnova agent -m qwen2.5:7b --tools calculator,shell,write_file
101
101
 
102
- # Use Chat-Completions API (OpenAI-compatible)
103
- agentnova chat -m qwen2.5:0.5b --api comp
102
+ # Use OpenAI Chat-Completions API
103
+ agentnova chat -m qwen2.5:0.5b --api openai
104
104
 
105
105
  # List available models
106
106
  agentnova models
@@ -138,7 +138,7 @@ from agentnova.backends import get_backend
138
138
  from agentnova.core.types import ApiMode
139
139
 
140
140
  # Use Chat-Completions mode with streaming
141
- backend = get_backend("ollama", api_mode=ApiMode.COMPLETIONS)
141
+ backend = get_backend("ollama", api_mode=ApiMode.OPENAI)
142
142
 
143
143
  for chunk in backend.generate_completions_stream(
144
144
  model="qwen2.5:0.5b",
@@ -261,7 +261,7 @@ agentnova config --urls # Show only URLs
261
261
 
262
262
  | Option | Description |
263
263
  |--------|-------------|
264
- | `--api resp\|comp` | API mode: OpenResponses (default) or Chat-Completions |
264
+ | `--api openre\|openai` | API mode: OpenResponses (default) or OpenAI Chat-Completions |
265
265
  | `--response-format text\|json` | Response format (Chat-Completions mode) |
266
266
  | `--truncation auto\|disabled` | Truncation behavior for long responses |
267
267
  | `--soul <path>` | Load Soul Spec persona package |
@@ -283,7 +283,7 @@ agentnova run "What is 2+2?"
283
283
 
284
284
  ## Tests & Examples
285
285
 
286
- AgentNova includes a suite of tests for validating agent capabilities:
286
+ AgentNova includes a comprehensive suite of tests for validating agent capabilities across reasoning, knowledge, and tool usage:
287
287
 
288
288
  ```bash
289
289
  # Basic agent test (no tools)
@@ -292,7 +292,7 @@ python -m agentnova.examples.00_basic_agent
292
292
  # Quick 5-question diagnostic
293
293
  python -m agentnova.examples.01_quick_diagnostic
294
294
 
295
- # Tool usage tests (calculator, shell, datetime)
295
+ # Tool usage tests (calculator, shell, datetime, file, python_repl)
296
296
  python -m agentnova.examples.02_tool_test
297
297
 
298
298
  # Logic and reasoning tests (BBH-style)
@@ -300,16 +300,45 @@ python -m agentnova.examples.03_reasoning_test
300
300
 
301
301
  # GSM8K math benchmark (50 questions)
302
302
  python -m agentnova.examples.04_gsm8k_benchmark
303
+
304
+ # Common sense reasoning (BIG-bench)
305
+ python -m agentnova.examples.05_common_sense
306
+
307
+ # Causal reasoning (BIG-bench)
308
+ python -m agentnova.examples.06_causal_reasoning
309
+
310
+ # Logical deduction (BIG-bench)
311
+ python -m agentnova.examples.07_logical_deduction
312
+
313
+ # Reading comprehension
314
+ python -m agentnova.examples.08_reading_comprehension
315
+
316
+ # General knowledge (BIG-bench)
317
+ python -m agentnova.examples.09_general_knowledge
318
+
319
+ # Implicit reasoning
320
+ python -m agentnova.examples.10_implicit_reasoning
321
+
322
+ # Analogical reasoning
323
+ python -m agentnova.examples.11_analogical_reasoning
303
324
  ```
304
325
 
305
326
  ### Test Categories
306
327
 
307
328
  | Test | Questions | Focus |
308
329
  |------|-----------|-------|
330
+ | Basic Agent | 1 | Single prompt, no tools |
309
331
  | Quick Diagnostic | 5 | Calculator tool, multi-step reasoning |
310
- | Tool Test | 10 | Calculator, shell, datetime tools |
311
- | Reasoning Test | 13 | Logic, deduction, patterns, spatial |
332
+ | Tool Test | 10 | Calculator, shell, datetime, file, python_repl tools |
333
+ | Reasoning Test | 14 | Logic, deduction, patterns, spatial |
312
334
  | GSM8K Benchmark | 50 | Math word problems |
335
+ | Common Sense | 25 | Physical properties, everyday reasoning |
336
+ | Causal Reasoning | 25 | Cause and effect relationships |
337
+ | Logical Deduction | 25 | Formal logic puzzles |
338
+ | Reading Comprehension | 25 | Passage-based Q&A |
339
+ | General Knowledge | 25 | Science, history, geography |
340
+ | Implicit Reasoning | 25 | Unstated assumptions and inference |
341
+ | Analogical Reasoning | 25 | Pattern matching and analogies |
313
342
 
314
343
  ### Benchmark Results (Quick Diagnostic)
315
344
 
@@ -318,8 +347,9 @@ python -m agentnova.examples.04_gsm8k_benchmark
318
347
  | functiongemma:270m | 5/5 (100%) | ~20s | native |
319
348
  | granite4:350m | 5/5 (100%) | ~50s | native |
320
349
  | qwen2.5:0.5b | 5/5 (100%) | 38s | native |
321
- | qwen2.5-coder:0.5b | 5/5 (100%) | 93s | react |
350
+ | qwen2.5-coder:0.5b | 5/5 (100%) | 93s | native |
322
351
  | qwen3:0.6b | 5/5 (100%) | 70s | react |
352
+ | deepseek-r1:1.5b | 5/5 (100%) | ~305s | native |
323
353
 
324
354
  All tested models achieve 100% on the Quick Diagnostic. Native models are ~2x faster than ReAct models due to direct API tool calling.
325
355
 
@@ -1,4 +1,4 @@
1
- # ⚛️ AgentNova R03.6
1
+ # ⚛️ AgentNova R03.7
2
2
 
3
3
  **Status: Alpha**
4
4
 
@@ -29,7 +29,7 @@ Inspired by the architecture of OpenClaw, rebuilt from scratch for local-first o
29
29
 
30
30
  - **Zero dependencies** — Uses Python stdlib only (urllib for HTTP)
31
31
  - **Ollama + BitNet backends** — Switch with `--backend` flag
32
- - **Dual API support** — OpenResponses (`--api resp`) and Chat-Completions (`--api comp`)
32
+ - **Dual API support** — OpenResponses (`--api openre`) and OpenAI Chat-Completions (`--api openai`)
33
33
  - **Three-tier tool support** — Native, ReAct, or none (auto-detected)
34
34
  - **Small model optimized** — Fuzzy matching, argument normalization
35
35
  - **Built-in security** — Path validation, command blocklist, SSRF protection
@@ -65,8 +65,8 @@ agentnova chat -m qwen2.5:0.5b --tools calculator,shell
65
65
  # Autonomous agent mode
66
66
  agentnova agent -m qwen2.5:7b --tools calculator,shell,write_file
67
67
 
68
- # Use Chat-Completions API (OpenAI-compatible)
69
- agentnova chat -m qwen2.5:0.5b --api comp
68
+ # Use OpenAI Chat-Completions API
69
+ agentnova chat -m qwen2.5:0.5b --api openai
70
70
 
71
71
  # List available models
72
72
  agentnova models
@@ -104,7 +104,7 @@ from agentnova.backends import get_backend
104
104
  from agentnova.core.types import ApiMode
105
105
 
106
106
  # Use Chat-Completions mode with streaming
107
- backend = get_backend("ollama", api_mode=ApiMode.COMPLETIONS)
107
+ backend = get_backend("ollama", api_mode=ApiMode.OPENAI)
108
108
 
109
109
  for chunk in backend.generate_completions_stream(
110
110
  model="qwen2.5:0.5b",
@@ -227,7 +227,7 @@ agentnova config --urls # Show only URLs
227
227
 
228
228
  | Option | Description |
229
229
  |--------|-------------|
230
- | `--api resp\|comp` | API mode: OpenResponses (default) or Chat-Completions |
230
+ | `--api openre\|openai` | API mode: OpenResponses (default) or OpenAI Chat-Completions |
231
231
  | `--response-format text\|json` | Response format (Chat-Completions mode) |
232
232
  | `--truncation auto\|disabled` | Truncation behavior for long responses |
233
233
  | `--soul <path>` | Load Soul Spec persona package |
@@ -249,7 +249,7 @@ agentnova run "What is 2+2?"
249
249
 
250
250
  ## Tests & Examples
251
251
 
252
- AgentNova includes a suite of tests for validating agent capabilities:
252
+ AgentNova includes a comprehensive suite of tests for validating agent capabilities across reasoning, knowledge, and tool usage:
253
253
 
254
254
  ```bash
255
255
  # Basic agent test (no tools)
@@ -258,7 +258,7 @@ python -m agentnova.examples.00_basic_agent
258
258
  # Quick 5-question diagnostic
259
259
  python -m agentnova.examples.01_quick_diagnostic
260
260
 
261
- # Tool usage tests (calculator, shell, datetime)
261
+ # Tool usage tests (calculator, shell, datetime, file, python_repl)
262
262
  python -m agentnova.examples.02_tool_test
263
263
 
264
264
  # Logic and reasoning tests (BBH-style)
@@ -266,16 +266,45 @@ python -m agentnova.examples.03_reasoning_test
266
266
 
267
267
  # GSM8K math benchmark (50 questions)
268
268
  python -m agentnova.examples.04_gsm8k_benchmark
269
+
270
+ # Common sense reasoning (BIG-bench)
271
+ python -m agentnova.examples.05_common_sense
272
+
273
+ # Causal reasoning (BIG-bench)
274
+ python -m agentnova.examples.06_causal_reasoning
275
+
276
+ # Logical deduction (BIG-bench)
277
+ python -m agentnova.examples.07_logical_deduction
278
+
279
+ # Reading comprehension
280
+ python -m agentnova.examples.08_reading_comprehension
281
+
282
+ # General knowledge (BIG-bench)
283
+ python -m agentnova.examples.09_general_knowledge
284
+
285
+ # Implicit reasoning
286
+ python -m agentnova.examples.10_implicit_reasoning
287
+
288
+ # Analogical reasoning
289
+ python -m agentnova.examples.11_analogical_reasoning
269
290
  ```
270
291
 
271
292
  ### Test Categories
272
293
 
273
294
  | Test | Questions | Focus |
274
295
  |------|-----------|-------|
296
+ | Basic Agent | 1 | Single prompt, no tools |
275
297
  | Quick Diagnostic | 5 | Calculator tool, multi-step reasoning |
276
- | Tool Test | 10 | Calculator, shell, datetime tools |
277
- | Reasoning Test | 13 | Logic, deduction, patterns, spatial |
298
+ | Tool Test | 10 | Calculator, shell, datetime, file, python_repl tools |
299
+ | Reasoning Test | 14 | Logic, deduction, patterns, spatial |
278
300
  | GSM8K Benchmark | 50 | Math word problems |
301
+ | Common Sense | 25 | Physical properties, everyday reasoning |
302
+ | Causal Reasoning | 25 | Cause and effect relationships |
303
+ | Logical Deduction | 25 | Formal logic puzzles |
304
+ | Reading Comprehension | 25 | Passage-based Q&A |
305
+ | General Knowledge | 25 | Science, history, geography |
306
+ | Implicit Reasoning | 25 | Unstated assumptions and inference |
307
+ | Analogical Reasoning | 25 | Pattern matching and analogies |
279
308
 
280
309
  ### Benchmark Results (Quick Diagnostic)
281
310
 
@@ -284,8 +313,9 @@ python -m agentnova.examples.04_gsm8k_benchmark
284
313
  | functiongemma:270m | 5/5 (100%) | ~20s | native |
285
314
  | granite4:350m | 5/5 (100%) | ~50s | native |
286
315
  | qwen2.5:0.5b | 5/5 (100%) | 38s | native |
287
- | qwen2.5-coder:0.5b | 5/5 (100%) | 93s | react |
316
+ | qwen2.5-coder:0.5b | 5/5 (100%) | 93s | native |
288
317
  | qwen3:0.6b | 5/5 (100%) | 70s | react |
318
+ | deepseek-r1:1.5b | 5/5 (100%) | ~305s | native |
289
319
 
290
320
  All tested models achieve 100% on the Quick Diagnostic. Native models are ~2x faster than ReAct models due to direct API tool calling.
291
321
 
@@ -319,4 +349,4 @@ Contributions welcome! Please read the contributing guidelines first.
319
349
 
320
350
  - Built for local inference with [Ollama](https://ollama.ai)
321
351
  - Optimized for small, efficient models
322
- - Inspired by ReAct and other agentic frameworks
352
+ - Inspired by ReAct and other agentic frameworks
@@ -28,7 +28,7 @@ Example Usage:
28
28
  agent = Agent(model="qwen2.5:0.5b", soul="/path/to/soul/package")
29
29
  """
30
30
 
31
- __version__ = "0.3.6"
31
+ __version__ = "0.3.7"
32
32
  __author__ = "VTSTech"
33
33
  __status__ = "Alpha"
34
34
 
@@ -314,7 +314,7 @@ class Agent:
314
314
  @property
315
315
  def _is_comp_mode(self) -> bool:
316
316
  """Check if backend is using Chat-Completions (comp) API mode."""
317
- return hasattr(self.backend, 'api_mode') and self.backend.api_mode == ApiMode.COMPLETIONS
317
+ return hasattr(self.backend, 'api_mode') and self.backend.api_mode == ApiMode.OPENAI
318
318
 
319
319
  def _log_openresponses(self, msg: str) -> None:
320
320
  """Log OpenResponses debug message only when not in comp mode."""
@@ -26,7 +26,7 @@ def get_backend(name: str, timeout: int | None = None, api_mode: ApiMode | str |
26
26
  Args:
27
27
  name: Backend name ("ollama", "bitnet")
28
28
  timeout: Request timeout in seconds (default: 120)
29
- api_mode: API mode ("resp" for OpenResponses/native, "comp" for Chat-Completions)
29
+ api_mode: API mode ("openre" for OpenResponses, "openai" for Chat-Completions)
30
30
  **kwargs: Backend-specific configuration
31
31
 
32
32
  Returns:
@@ -59,7 +59,7 @@ def get_backend(name: str, timeout: int | None = None, api_mode: ApiMode | str |
59
59
  return backend_class(**kwargs)
60
60
 
61
61
 
62
- def get_default_backend(name: str | None = None, api_mode: ApiMode | str | None = None) -> BaseBackend:
62
+ def get_default_backend(name: str | None = None, api_mode: ApiMode | str | None = None, timeout: int | None = None) -> BaseBackend:
63
63
  """
64
64
  Get a default backend instance.
65
65
 
@@ -67,13 +67,14 @@ def get_default_backend(name: str | None = None, api_mode: ApiMode | str | None
67
67
 
68
68
  Args:
69
69
  name: Backend name (optional, uses env var if not provided)
70
- api_mode: API mode ("resp" for OpenResponses/native, "comp" for Chat-Completions)
70
+ api_mode: API mode ("openre" for OpenResponses/native, "openai" for Chat-Completions)
71
+ timeout: Request timeout in seconds (default: 120)
71
72
 
72
73
  Returns:
73
74
  Backend instance with default configuration
74
75
  """
75
76
  backend_name = name or AGENTNOVA_BACKEND
76
- return get_backend(backend_name, api_mode=api_mode)
77
+ return get_backend(backend_name, api_mode=api_mode, timeout=timeout)
77
78
 
78
79
 
79
80
  def register_backend(name: str, backend_class: type[BaseBackend]) -> None:
@@ -32,7 +32,7 @@ class OllamaBackend(BaseBackend):
32
32
  host: str | None = None,
33
33
  port: int | None = None,
34
34
  config: BackendConfig | None = None,
35
- api_mode: ApiMode | str = ApiMode.RESPONSES,
35
+ api_mode: ApiMode | str = ApiMode.OPENRE,
36
36
  ):
37
37
  # Determine base URL - priority: base_url > host/port > env > default
38
38
  if base_url:
@@ -47,7 +47,7 @@ class OllamaBackend(BaseBackend):
47
47
  else:
48
48
  super().__init__(BackendConfig())
49
49
 
50
- # Set API mode (resp = OpenResponses/native, comp = Chat-Completions)
50
+ # Set API mode (openre = OpenResponses, openai = Chat-Completions)
51
51
  if isinstance(api_mode, str):
52
52
  api_mode = ApiMode(api_mode.lower())
53
53
  self._api_mode = api_mode
@@ -137,7 +137,7 @@ class OllamaBackend(BaseBackend):
137
137
  **kwargs: Additional options passed to Ollama
138
138
  """
139
139
  # Dispatch based on api_mode
140
- if self._api_mode == ApiMode.COMPLETIONS:
140
+ if self._api_mode == ApiMode.OPENAI:
141
141
  if os.environ.get("AGENTNOVA_DEBUG"):
142
142
  print(f" [Ollama] Dispatching to OpenAI-compatible API (mode={self._api_mode.value})")
143
143
  return self.generate_completions(
@@ -171,8 +171,8 @@ def create_parser() -> argparse.ArgumentParser:
171
171
  run_parser.add_argument("-m", "--model", default=None, help="Model to use")
172
172
  run_parser.add_argument("--tools", default="calculator", help="Comma-separated tool list")
173
173
  run_parser.add_argument("--backend", choices=["ollama", "bitnet"], default=None, help="Backend to use")
174
- run_parser.add_argument("--api", choices=["resp", "comp"], default="resp", dest="api_mode",
175
- help="API mode: 'resp' (OpenResponses/native) or 'comp' (Chat-Completions)")
174
+ run_parser.add_argument("--api", choices=["openre", "openai"], default="openre", dest="api_mode",
175
+ help="API mode: 'openre' (OpenResponses) or 'openai' (Chat-Completions)")
176
176
  run_parser.add_argument("--stream", action="store_true", help="Stream output")
177
177
  run_parser.add_argument("--debug", action="store_true", help="Enable debug output")
178
178
  run_parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
@@ -202,8 +202,8 @@ def create_parser() -> argparse.ArgumentParser:
202
202
  chat_parser.add_argument("-m", "--model", default=None, help="Model to use")
203
203
  chat_parser.add_argument("--tools", default="", help="Comma-separated tool list")
204
204
  chat_parser.add_argument("--backend", choices=["ollama", "bitnet"], default=None, help="Backend to use")
205
- chat_parser.add_argument("--api", choices=["resp", "comp"], default="resp", dest="api_mode",
206
- help="API mode: 'resp' (OpenResponses/native) or 'comp' (Chat-Completions)")
205
+ chat_parser.add_argument("--api", choices=["openre", "openai"], default="openre", dest="api_mode",
206
+ help="API mode: 'openre' (OpenResponses) or 'openai' (Chat-Completions)")
207
207
  chat_parser.add_argument("--debug", action="store_true", help="Enable debug output")
208
208
  chat_parser.add_argument("--force-react", action="store_true", help="Force ReAct mode")
209
209
  chat_parser.add_argument("--soul", default=None, help="Path to Soul Spec package (disabled by default)")
@@ -231,8 +231,8 @@ def create_parser() -> argparse.ArgumentParser:
231
231
  agent_parser.add_argument("-m", "--model", default=None, help="Model to use")
232
232
  agent_parser.add_argument("--tools", default="calculator,shell,write_file", help="Comma-separated tool list")
233
233
  agent_parser.add_argument("--backend", choices=["ollama", "bitnet"], default=None, help="Backend to use")
234
- agent_parser.add_argument("--api", choices=["resp", "comp"], default="resp", dest="api_mode",
235
- help="API mode: 'resp' (OpenResponses/native) or 'comp' (Chat-Completions)")
234
+ agent_parser.add_argument("--api", choices=["openre", "openai"], default="openre", dest="api_mode",
235
+ help="API mode: 'openre' (OpenResponses) or 'openai' (Chat-Completions)")
236
236
  agent_parser.add_argument("--debug", action="store_true", help="Enable debug output")
237
237
  agent_parser.add_argument("--force-react", action="store_true", help="Force ReAct mode for tool calling")
238
238
  agent_parser.add_argument("--soul", default=None, help="Path to Soul Spec package (disabled by default)")
@@ -271,8 +271,8 @@ def create_parser() -> argparse.ArgumentParser:
271
271
  test_parser.add_argument("-m", "--model", default=None,
272
272
  help="Model to test (supports patterns: 'qwen', 'g', ':0.5b')")
273
273
  test_parser.add_argument("--backend", choices=["ollama", "bitnet"], default=None, help="Backend to use")
274
- test_parser.add_argument("--api", choices=["resp", "comp"], default="resp", dest="api_mode",
275
- help="API mode: 'resp' (OpenResponses/native) or 'comp' (Chat-Completions)")
274
+ test_parser.add_argument("--api", choices=["openre", "openai"], default="openre", dest="api_mode",
275
+ help="API mode: 'openre' (OpenResponses) or 'openai' (Chat-Completions)")
276
276
  test_parser.add_argument("--debug", action="store_true", help="Enable debug output")
277
277
  test_parser.add_argument("--list", action="store_true", help="List available tests")
278
278
  test_parser.add_argument("--acp", action="store_true", help="Enable ACP logging to Agent Control Panel")
@@ -295,6 +295,10 @@ def create_parser() -> argparse.ArgumentParser:
295
295
  test_parser.add_argument("--soul", default=None, help="Path to Soul Spec package (disabled by default)")
296
296
  test_parser.add_argument("--soul-level", type=int, default=2, choices=[1, 2, 3],
297
297
  help="Soul progressive disclosure level (1=quick, 2=full, 3=deep)")
298
+ test_parser.add_argument("--tools-only", action="store_true", dest="tools_only",
299
+ help="Only run Phase 1 (direct tool tests, no model)")
300
+ test_parser.add_argument("--model-only", action="store_true", dest="model_only",
301
+ help="Only run Phase 2 (model tool calling tests)")
298
302
 
299
303
  # Version command
300
304
  subparsers.add_parser("version", help="Show version information")
@@ -360,7 +364,7 @@ def cmd_run(args: argparse.Namespace) -> int:
360
364
  config = get_config()
361
365
  model = args.model or config.default_model
362
366
  backend_name = args.backend or config.backend
363
- api_mode = getattr(args, 'api_mode', 'resp')
367
+ api_mode = getattr(args, 'api_mode', 'openre')
364
368
 
365
369
  # Initialize ACP if requested
366
370
  acp, should_stop = _init_acp(args, config, "AgentNova-Run")
@@ -386,7 +390,7 @@ def cmd_run(args: argparse.Namespace) -> int:
386
390
  force_react=getattr(args, 'force_react', False),
387
391
  soul=getattr(args, 'soul', None),
388
392
  soul_level=getattr(args, 'soul_level', 2),
389
- num_ctx=getattr(args, 'num_ctx', None) or config.num_ctx,
393
+ num_ctx=getattr(args, 'num_ctx', None) if getattr(args, 'num_ctx', None) is not None else config.num_ctx,
390
394
  temperature=getattr(args, 'temperature', None),
391
395
  top_p=getattr(args, 'top_p', None),
392
396
  num_predict=getattr(args, 'num_predict', None),
@@ -414,7 +418,7 @@ def cmd_chat(args: argparse.Namespace) -> int:
414
418
  config = get_config()
415
419
  model = args.model or config.default_model
416
420
  backend_name = args.backend or config.backend
417
- api_mode = getattr(args, 'api_mode', 'resp')
421
+ api_mode = getattr(args, 'api_mode', 'openre')
418
422
 
419
423
  # Initialize ACP if requested
420
424
  acp, should_stop = _init_acp(args, config, "AgentNova-Chat")
@@ -439,7 +443,7 @@ def cmd_chat(args: argparse.Namespace) -> int:
439
443
  debug=args.debug,
440
444
  soul=getattr(args, 'soul', None),
441
445
  soul_level=getattr(args, 'soul_level', 2),
442
- num_ctx=getattr(args, 'num_ctx', None) or config.num_ctx,
446
+ num_ctx=getattr(args, 'num_ctx', None) if getattr(args, 'num_ctx', None) is not None else config.num_ctx,
443
447
  temperature=getattr(args, 'temperature', None),
444
448
  top_p=getattr(args, 'top_p', None),
445
449
  num_predict=getattr(args, 'num_predict', None),
@@ -512,7 +516,7 @@ def cmd_agent(args: argparse.Namespace) -> int:
512
516
  config = get_config()
513
517
  model = args.model or config.default_model
514
518
  backend_name = args.backend or config.backend
515
- api_mode = getattr(args, 'api_mode', 'resp')
519
+ api_mode = getattr(args, 'api_mode', 'openre')
516
520
 
517
521
  # Initialize ACP if requested
518
522
  acp, should_stop = _init_acp(args, config, "AgentNova-Agent")
@@ -537,7 +541,7 @@ def cmd_agent(args: argparse.Namespace) -> int:
537
541
  force_react=getattr(args, 'force_react', False),
538
542
  soul=getattr(args, 'soul', None),
539
543
  soul_level=getattr(args, 'soul_level', 2),
540
- num_ctx=getattr(args, 'num_ctx', None) or config.num_ctx,
544
+ num_ctx=getattr(args, 'num_ctx', None) if getattr(args, 'num_ctx', None) is not None else config.num_ctx,
541
545
  temperature=getattr(args, 'temperature', None),
542
546
  top_p=getattr(args, 'top_p', None),
543
547
  num_predict=getattr(args, 'num_predict', None),
@@ -950,7 +954,7 @@ def cmd_test(args: argparse.Namespace) -> int:
950
954
  # Check backend
951
955
  config = get_config()
952
956
  backend_name = args.backend or config.backend
953
- api_mode = getattr(args, 'api_mode', 'resp')
957
+ api_mode = getattr(args, 'api_mode', 'openre')
954
958
  timeout = getattr(args, 'timeout', None)
955
959
  backend = get_backend(backend_name, timeout=timeout, api_mode=api_mode)
956
960
 
@@ -1014,7 +1018,7 @@ def cmd_test(args: argparse.Namespace) -> int:
1014
1018
  print(f"{dim('Model:')} {cyan(models_to_test[0])}")
1015
1019
  else:
1016
1020
  print(f"{dim('Models:')} {cyan(str(len(models_to_test)))} matching '{model_pattern}'")
1017
- num_ctx_val = getattr(args, 'num_ctx', None) or config.num_ctx
1021
+ num_ctx_val = getattr(args, 'num_ctx', None) if getattr(args, 'num_ctx', None) is not None else config.num_ctx
1018
1022
  if num_ctx_val:
1019
1023
  ctx_display = f"{num_ctx_val // 1024}K" if num_ctx_val >= 1024 else str(num_ctx_val)
1020
1024
  print(f"{dim('Context:')} {yellow(ctx_display)}")
@@ -1056,7 +1060,7 @@ def cmd_test(args: argparse.Namespace) -> int:
1056
1060
  test_argv.append("--debug")
1057
1061
  if args.backend:
1058
1062
  test_argv.extend(["--backend", args.backend])
1059
- if getattr(args, 'api_mode', 'resp') != 'resp':
1063
+ if getattr(args, 'api_mode', 'openre') != 'openre':
1060
1064
  test_argv.extend(["--api", args.api_mode])
1061
1065
  if getattr(args, 'force_react', False):
1062
1066
  test_argv.append("--force-react")
@@ -1077,6 +1081,10 @@ def cmd_test(args: argparse.Namespace) -> int:
1077
1081
  test_argv.extend(["--temp", str(args.temperature)])
1078
1082
  if getattr(args, 'top_p', None) is not None:
1079
1083
  test_argv.extend(["--top-p", str(args.top_p)])
1084
+ if getattr(args, 'tools_only', False):
1085
+ test_argv.append("--tools-only")
1086
+ if getattr(args, 'model_only', False):
1087
+ test_argv.append("--model-only")
1080
1088
 
1081
1089
  # Override sys.argv for the test module's argparse
1082
1090
  old_argv = sys.argv
@@ -74,15 +74,15 @@ class BackendType(Enum):
74
74
  class ApiMode(Enum):
75
75
  """API mode for backend communication.
76
76
 
77
- - RESPONSES: OpenResponses-style API (Ollama native /api/chat)
78
- - COMPLETIONS: OpenAI Chat-Completions compatible API (/v1/chat/completions)
77
+ - OPENRE: OpenResponses API (open spec for agentic workflows)
78
+ - OPENAI: OpenAI Chat-Completions API
79
79
 
80
80
  Ollama supports both endpoints:
81
- - /api/chat - Native Ollama format (default)
81
+ - /api/chat - OpenResponses format (default)
82
82
  - /v1/chat/completions - OpenAI-compatible format
83
83
  """
84
- RESPONSES = "resp" # OpenResponses / Ollama native format
85
- COMPLETIONS = "comp" # OpenAI Chat-Completions compatible format
84
+ OPENRE = "openre" # OpenResponses API (/api/chat)
85
+ OPENAI = "openai" # OpenAI Chat-Completions API (/v1/chat/completions)
86
86
 
87
87
 
88
88
  # Type aliases for clarity
@@ -28,8 +28,22 @@ def parse_args():
28
28
  parser.add_argument("-m", "--model", default=None, help="Model to use")
29
29
  parser.add_argument("--debug", action="store_true", help="Enable debug output")
30
30
  parser.add_argument("--backend", choices=["ollama", "bitnet"], default=None)
31
- parser.add_argument("--api", choices=["resp", "comp"], default="resp", dest="api_mode",
32
- help="API mode: 'resp' (OpenResponses/native) or 'comp' (Chat-Completions)")
31
+ parser.add_argument("--api", choices=["openre", "openai"], default="openre", dest="api_mode",
32
+ help="API mode: 'openre' (OpenResponses) or 'openai' (Chat-Completions (OpenAI))")
33
+ parser.add_argument("--soul", default=None, help="Path to Soul Spec package")
34
+ parser.add_argument("--soul-level", type=int, default=2, choices=[1, 2, 3],
35
+ help="Soul progressive disclosure level")
36
+ parser.add_argument("--num-ctx", type=int, default=None,
37
+ help="Context window size in tokens")
38
+ parser.add_argument("--num-predict", type=int, default=None,
39
+ help="Maximum tokens to generate")
40
+ parser.add_argument("--temp", type=float, default=None, dest="temperature",
41
+ help="Sampling temperature 0.0-2.0")
42
+ parser.add_argument("--top-p", type=float, default=None, dest="top_p",
43
+ help="Nucleus sampling probability 0.0-1.0")
44
+ parser.add_argument("--force-react", action="store_true", help="Force ReAct mode for tool calling")
45
+ parser.add_argument("--timeout", type=int, default=None,
46
+ help="Request timeout in seconds (default: 120)")
33
47
  return parser.parse_args()
34
48
 
35
49
 
@@ -39,8 +53,9 @@ def main():
39
53
 
40
54
  # Get backend (respects AGENTNOVA_BACKEND env var)
41
55
  backend_name = args.backend or config.backend
42
- api_mode = getattr(args, 'api_mode', 'resp')
43
- backend = get_default_backend(backend_name, api_mode=api_mode)
56
+ api_mode = getattr(args, 'api_mode', 'openre')
57
+ timeout = getattr(args, 'timeout', None)
58
+ backend = get_default_backend(backend_name, api_mode=api_mode, timeout=timeout)
44
59
 
45
60
  # Check if backend is running
46
61
  if not backend.is_running():
@@ -57,13 +72,22 @@ def main():
57
72
  model=args.model or config.default_model,
58
73
  backend=backend,
59
74
  debug=args.debug or config.debug,
75
+ soul=args.soul,
76
+ soul_level=args.soul_level,
77
+ force_react=getattr(args, 'force_react', False),
78
+ num_ctx=getattr(args, 'num_ctx', None),
79
+ num_predict=getattr(args, 'num_predict', None),
80
+ temperature=getattr(args, 'temperature', None),
81
+ top_p=getattr(args, 'top_p', None),
60
82
  )
61
83
 
62
84
  print(f"\n⚛️ AgentNova Basic Agent")
63
85
  print(f" Model: {args.model or config.default_model}")
64
86
  print(f" Backend: {backend_name}")
65
- if api_mode != 'resp':
87
+ if api_mode != 'openre':
66
88
  print(f" API Mode: {api_mode}")
89
+ if timeout:
90
+ print(f" Timeout: {timeout}s")
67
91
  print("-" * 40)
68
92
 
69
93
  # Single question
@@ -43,8 +43,8 @@ def parse_args():
43
43
  parser.add_argument("-m", "--model", default=None, help="Model to test")
44
44
  parser.add_argument("--debug", action="store_true", help="Enable debug output")
45
45
  parser.add_argument("--backend", choices=["ollama", "bitnet"], default=None)
46
- parser.add_argument("--api", choices=["resp", "comp"], default="resp", dest="api_mode",
47
- help="API mode: 'resp' (OpenResponses/native) or 'comp' (Chat-Completions)")
46
+ parser.add_argument("--api", choices=["openre", "openai"], default="openre", dest="api_mode",
47
+ help="API mode: 'openre' (OpenResponses) or 'openai' (Chat-Completions (OpenAI))")
48
48
  parser.add_argument("--force-react", action="store_true", help="Force ReAct mode for tool calling")
49
49
  parser.add_argument("--soul", default=None, help="Path to Soul Spec package")
50
50
  parser.add_argument("--soul-level", type=int, default=2, choices=[1, 2, 3],
@@ -222,7 +222,7 @@ def main():
222
222
 
223
223
  # Get backend with timeout
224
224
  backend_name = args.backend or config.backend
225
- api_mode = getattr(args, 'api_mode', 'resp')
225
+ api_mode = getattr(args, 'api_mode', 'openre')
226
226
  timeout = getattr(args, 'timeout', None)
227
227
  backend = get_backend(backend_name, timeout=timeout, api_mode=api_mode)
228
228
 
@@ -242,8 +242,8 @@ def main():
242
242
  print(f" Backend: {backend_name} ({backend.base_url})")
243
243
  print(f" Model: {model}")
244
244
  api_mode_display = {
245
- 'resp': '[OpenAI] OpenResponses (2025)',
246
- 'comp': '[OpenAI] ChatCompletions (2023)'
245
+ 'openre': '[OpenResponses]',
246
+ 'openai': '[OpenAI] ChatCompletions'
247
247
  }.get(api_mode, api_mode)
248
248
  print(f" API Mode: {api_mode_display}")
249
249
  if timeout:
@@ -252,7 +252,9 @@ def main():
252
252
  print(f" Force ReAct: True")
253
253
  if args.soul:
254
254
  print(f" Soul: {args.soul}")
255
- num_ctx = getattr(args, 'num_ctx', None) or getattr(config, 'num_ctx', None)
255
+ num_ctx = getattr(args, 'num_ctx', None)
256
+ if num_ctx is None:
257
+ num_ctx = getattr(config, 'num_ctx', None)
256
258
  if num_ctx:
257
259
  ctx_display = f"{num_ctx // 1024}K" if num_ctx >= 1024 else str(num_ctx)
258
260
  print(f" Context: {ctx_display}")
@@ -274,5 +276,4 @@ def main():
274
276
 
275
277
 
276
278
  if __name__ == "__main__":
277
- result = main()
278
- sys.exit(result.get("exit_code", 0))
279
+ result = main()