ollama_agent 0.1.0 → 0.3.0

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/skills/ruby-code-review-levels/SKILL.md +115 -0
  3. data/.cursor/skills/self-improvement-sandbox-safety/SKILL.md +65 -0
  4. data/.env.example +25 -0
  5. data/CHANGELOG.md +40 -0
  6. data/README.md +135 -4
  7. data/docs/ARCHITECTURE.md +42 -0
  8. data/docs/PERFORMANCE.md +22 -0
  9. data/docs/SESSIONS.md +48 -0
  10. data/docs/TOOLS.md +53 -0
  11. data/docs/TOOL_RUNTIME.md +154 -0
  12. data/docs/superpowers/plans/2026-03-26-production-ready-ollama-agent.md +2454 -0
  13. data/docs/superpowers/specs/2026-03-26-production-ready-ollama-agent-design.md +400 -0
  14. data/lib/ollama_agent/agent/agent_config.rb +53 -0
  15. data/lib/ollama_agent/agent/client_wiring.rb +76 -0
  16. data/lib/ollama_agent/agent/prompt_wiring.rb +55 -0
  17. data/lib/ollama_agent/agent/session_wiring.rb +53 -0
  18. data/lib/ollama_agent/agent.rb +148 -73
  19. data/lib/ollama_agent/agent_prompt.rb +31 -1
  20. data/lib/ollama_agent/chat_stream_carry.rb +88 -0
  21. data/lib/ollama_agent/chat_stream_thinking_format.rb +29 -0
  22. data/lib/ollama_agent/cli.rb +394 -4
  23. data/lib/ollama_agent/console.rb +177 -5
  24. data/lib/ollama_agent/context/manager.rb +100 -0
  25. data/lib/ollama_agent/context/token_counter.rb +33 -0
  26. data/lib/ollama_agent/diff_path_validator.rb +32 -10
  27. data/lib/ollama_agent/env_config.rb +44 -0
  28. data/lib/ollama_agent/external_agents/TODO-plan.md +1948 -0
  29. data/lib/ollama_agent/external_agents/argv_interp.rb +21 -0
  30. data/lib/ollama_agent/external_agents/default_agents.yml +60 -0
  31. data/lib/ollama_agent/external_agents/delegate_logger.rb +31 -0
  32. data/lib/ollama_agent/external_agents/delegate_timeout_status.rb +12 -0
  33. data/lib/ollama_agent/external_agents/env_helpers.rb +38 -0
  34. data/lib/ollama_agent/external_agents/path_validator.rb +32 -0
  35. data/lib/ollama_agent/external_agents/probe.rb +122 -0
  36. data/lib/ollama_agent/external_agents/registry.rb +50 -0
  37. data/lib/ollama_agent/external_agents/runner.rb +118 -0
  38. data/lib/ollama_agent/external_agents.rb +9 -0
  39. data/lib/ollama_agent/global_dotenv.rb +39 -0
  40. data/lib/ollama_agent/model_env.rb +26 -0
  41. data/lib/ollama_agent/ollama_chat_thinking_stream.rb +41 -0
  42. data/lib/ollama_agent/ollama_connection.rb +6 -1
  43. data/lib/ollama_agent/patch_risk.rb +81 -0
  44. data/lib/ollama_agent/patch_support.rb +27 -1
  45. data/lib/ollama_agent/path_sandbox.rb +62 -0
  46. data/lib/ollama_agent/prompt_skills/clean_ruby.md +131 -0
  47. data/lib/ollama_agent/prompt_skills/code_review.md +112 -0
  48. data/lib/ollama_agent/prompt_skills/design_patterns.md +56 -0
  49. data/lib/ollama_agent/prompt_skills/manifest.yml +25 -0
  50. data/lib/ollama_agent/prompt_skills/ollama_agent_patterns.md +132 -0
  51. data/lib/ollama_agent/prompt_skills/rails_best_practices.md +41 -0
  52. data/lib/ollama_agent/prompt_skills/rails_style.md +138 -0
  53. data/lib/ollama_agent/prompt_skills/rspec.md +280 -0
  54. data/lib/ollama_agent/prompt_skills/rubocop.md +7 -0
  55. data/lib/ollama_agent/prompt_skills/ruby_style.md +121 -0
  56. data/lib/ollama_agent/prompt_skills/solid.md +270 -0
  57. data/lib/ollama_agent/prompt_skills/solid_ruby.md +223 -0
  58. data/lib/ollama_agent/prompt_skills.rb +169 -0
  59. data/lib/ollama_agent/repo_list.rb +4 -1
  60. data/lib/ollama_agent/resilience/audit_logger.rb +79 -0
  61. data/lib/ollama_agent/resilience/retry_middleware.rb +45 -0
  62. data/lib/ollama_agent/resilience/retry_policy.rb +51 -0
  63. data/lib/ollama_agent/ruby_index_tool_support.rb +17 -6
  64. data/lib/ollama_agent/runner.rb +123 -0
  65. data/lib/ollama_agent/sandboxed_tools/delegate_external.rb +62 -0
  66. data/lib/ollama_agent/sandboxed_tools/file_read_write.rb +100 -0
  67. data/lib/ollama_agent/sandboxed_tools/search_text.rb +60 -0
  68. data/lib/ollama_agent/sandboxed_tools.rb +55 -116
  69. data/lib/ollama_agent/search_backend.rb +93 -0
  70. data/lib/ollama_agent/self_improvement/analyzer.rb +34 -0
  71. data/lib/ollama_agent/self_improvement/improver.rb +340 -0
  72. data/lib/ollama_agent/self_improvement/modes.rb +25 -0
  73. data/lib/ollama_agent/self_improvement/ruby_mastery_context.rb +66 -0
  74. data/lib/ollama_agent/self_improvement.rb +5 -0
  75. data/lib/ollama_agent/session/session.rb +8 -0
  76. data/lib/ollama_agent/session/store.rb +68 -0
  77. data/lib/ollama_agent/streaming/console_streamer.rb +29 -0
  78. data/lib/ollama_agent/streaming/hooks.rb +39 -0
  79. data/lib/ollama_agent/tool_arguments.rb +13 -1
  80. data/lib/ollama_agent/tool_content_parser.rb +1 -1
  81. data/lib/ollama_agent/tool_runtime/executor.rb +34 -0
  82. data/lib/ollama_agent/tool_runtime/json_extractor.rb +62 -0
  83. data/lib/ollama_agent/tool_runtime/loop.rb +72 -0
  84. data/lib/ollama_agent/tool_runtime/memory.rb +32 -0
  85. data/lib/ollama_agent/tool_runtime/ollama_json_planner.rb +98 -0
  86. data/lib/ollama_agent/tool_runtime/plan_extractor.rb +12 -0
  87. data/lib/ollama_agent/tool_runtime/registry.rb +60 -0
  88. data/lib/ollama_agent/tool_runtime/tool.rb +24 -0
  89. data/lib/ollama_agent/tool_runtime.rb +24 -0
  90. data/lib/ollama_agent/tools/registry.rb +55 -0
  91. data/lib/ollama_agent/tools_schema.rb +74 -1
  92. data/lib/ollama_agent/user_prompt.rb +35 -0
  93. data/lib/ollama_agent/version.rb +1 -1
  94. data/lib/ollama_agent.rb +25 -0
  95. data/reproduce_429.rb +40 -0
  96. data/sig/ollama_agent.rbs +111 -1
  97. metadata +78 -2
@@ -0,0 +1,154 @@
1
+ # ToolRuntime guide
2
+
3
+ `OllamaAgent::ToolRuntime` is a small **Ruby-first** agent loop for apps that define their **own** tools (outside the coding sandbox). Each step, a **planner** (usually an LLM) proposes a single JSON object `{"tool":"name","args":{...}}`; the runtime resolves the name, runs your tool, records the outcome, and repeats until a tool signals **done** or a safety limit trips.
4
+
5
+ It does **not** replace the CLI or `OllamaAgent::Agent` / `Runner` (those use Ollama **native** `/api/chat` tool calling for `read_file`, `edit_file`, etc.). Use ToolRuntime when you want **JSON-shaped plans** and **plain Ruby tool classes** in another domain (e.g. a separate gem that talks to your own APIs).
6
+
7
+ See also: [ARCHITECTURE.md](ARCHITECTURE.md) (coding-agent stack), [TOOLS.md](TOOLS.md) (custom tools **on** the coding agent).
8
+
9
+ ---
10
+
11
+ ## When to use it
12
+
13
+ | Use ToolRuntime | Use `Runner` / `Agent` instead |
14
+ |-----------------|-------------------------------|
15
+ | You implement `ToolRuntime::Tool` subclasses and a per-run `Registry` | You want file/search/patch tools under a project root |
16
+ | You want a **swappable** planner (`next_step(context:, memory:, registry:)`) | You want the stock Ollama tool-calling chat loop |
17
+ | You are OK prompting the model to emit **one JSON object per step** | You rely on Ollama’s structured `tool_calls` |
18
+
19
+ ---
20
+
21
+ ## Pieces you wire together
22
+
23
+ 1. **`Tool`** — Subclass, implement `name`, `description`, `schema`, `call(args)`. `args` uses string keys (JSON-style).
24
+ 2. **`Registry.new([tool, ...])`** — Looks up the planner’s `"tool"` string; duplicate names are rejected.
25
+ 3. **`Memory`** — Short transcript; optional `memory.tool_descriptions = "..."` is prepended to the planner prompt (extra hints).
26
+ 4. **`Executor`** — Optional `validator: object` with `validate(tool_name, args)` → args (or raise). Tool exceptions become `{ "status" => "error", "error" => "..." }`.
27
+ 5. **`OllamaJsonPlanner`** — Calls `client.chat` with a user prompt listing tools + context + prior steps; parses **one** JSON object from the reply (see `JsonExtractor`).
28
+ 6. **`Loop`** — `run(context:)` runs until a tool returns a Hash with `"status" => "done"` or `max_steps` is exceeded (`MaxStepsExceeded`).
29
+
30
+ **Return value:** `Loop#run` returns the **last tool result** (the Hash/object from the final `call` that ended the loop).
31
+
32
+ ---
33
+
34
+ ## Client and model (same rules as the CLI)
35
+
36
+ The planner uses your `Ollama::Client`. If you use **Ollama Cloud** env vars, build the config like the coding agent:
37
+
38
+ ```ruby
39
+ require "ollama_agent"
40
+ require "ollama_client"
41
+
42
+ config = Ollama::Config.new
43
+ OllamaAgent::OllamaConnection.apply_env_to_config(config)
44
+ client = Ollama::Client.new(config: config)
45
+ ```
46
+
47
+ `OllamaJsonPlanner` resolves the model like `Agent`: explicit `model:` keyword, else `ENV["OLLAMA_AGENT_MODEL"]`, else `Ollama::Config.new.model`.
48
+
49
+ **Common mistake:** `Ollama::Client.new(config: Ollama::Config.new)` without `apply_env_to_config` talks to **localhost** only. If `OLLAMA_AGENT_MODEL` is still a **cloud-only** tag, you get **404 model not found**. Either apply `apply_env_to_config`, or unset the env var, or pass `model: "your-local-tag"`.
50
+
51
+ The model should follow instructions to output **only** one JSON object per step (no markdown fences). If it drifts, you’ll see `JsonParseError`.
52
+
53
+ ---
54
+
55
+ ## Minimal runnable example
56
+
57
+ ```ruby
58
+ require "ollama_agent"
59
+ require "ollama_client"
60
+
61
+ class EchoTool < OllamaAgent::ToolRuntime::Tool
62
+ def name = "echo"
63
+ def description = "Echo a message string"
64
+ def schema = { "type" => "object", "properties" => { "msg" => { "type" => "string" } } }
65
+
66
+ def call(args)
67
+ return { "status" => "done", "echo" => args["msg"] } if args["msg"] == "bye"
68
+
69
+ { "status" => "ok", "echo" => args["msg"] }
70
+ end
71
+ end
72
+
73
+ registry = OllamaAgent::ToolRuntime::Registry.new([EchoTool.new])
74
+ memory = OllamaAgent::ToolRuntime::Memory.new
75
+
76
+ config = Ollama::Config.new
77
+ OllamaAgent::OllamaConnection.apply_env_to_config(config)
78
+ client = Ollama::Client.new(config: config)
79
+
80
+ planner = OllamaAgent::ToolRuntime::OllamaJsonPlanner.new(client: client)
81
+
82
+ last = OllamaAgent::ToolRuntime::Loop.new(
83
+ planner: planner,
84
+ registry: registry,
85
+ executor: OllamaAgent::ToolRuntime::Executor.new,
86
+ memory: memory,
87
+ max_steps: 10
88
+ ).run(context: "Use echo until you can call echo with msg bye to finish.")
89
+
90
+ puts last.inspect
91
+ puts memory.recent.inspect
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Inspecting what happened
97
+
98
+ - **`memory.recent`** — Array of `{ thought:, action:, result: }` (symbols). `thought` is the parsed plan Hash; `action` is `{ tool: <Tool instance>, args: Hash }`; `result` is what `Executor` returned.
99
+ - **`last` from `run`** — Final tool return value only (convenient for “answer” extraction).
100
+
101
+ ---
102
+
103
+ ## Custom planner
104
+
105
+ Anything that responds to:
106
+
107
+ ```ruby
108
+ def next_step(context:, memory:, registry:)
109
+ # return a Hash like { "tool" => "echo", "args" => { "msg" => "hi" } }
110
+ end
111
+ ```
112
+
113
+ can be passed as `planner:` to `Loop`. That lets you swap in scripted tests, another LLM, or a hybrid without changing `Loop`.
114
+
115
+ ---
116
+
117
+ ## Validator example
118
+
119
+ ```ruby
120
+ validator = Class.new do
121
+ def validate(tool_name, args)
122
+ raise ArgumentError, "missing msg" if tool_name == "echo" && args["msg"].to_s.empty?
123
+
124
+ args
125
+ end
126
+ end.new
127
+
128
+ executor = OllamaAgent::ToolRuntime::Executor.new(validator: validator)
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Errors you may see
134
+
135
+ | Error | Typical cause |
136
+ |-------|----------------|
137
+ | `Ollama::NotFoundError` (404 model) | Wrong host/model pair; see **Client and model** above |
138
+ | `OllamaAgent::ToolRuntime::JsonParseError` | Model did not return parseable JSON for one object |
139
+ | `InvalidPlanError` | Planner returned an unknown `"tool"` name |
140
+ | `MaxStepsExceeded` | No tool returned `"status" => "done"` before `max_steps` planner calls |
141
+
142
+ ---
143
+
144
+ ## Tests as examples
145
+
146
+ Executable examples live under:
147
+
148
+ `spec/ollama_agent/tool_runtime/`
149
+
150
+ Run:
151
+
152
+ ```bash
153
+ bundle exec rspec spec/ollama_agent/tool_runtime
154
+ ```