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.
- checksums.yaml +4 -4
- data/.cursor/skills/ruby-code-review-levels/SKILL.md +115 -0
- data/.cursor/skills/self-improvement-sandbox-safety/SKILL.md +65 -0
- data/.env.example +25 -0
- data/CHANGELOG.md +40 -0
- data/README.md +135 -4
- data/docs/ARCHITECTURE.md +42 -0
- data/docs/PERFORMANCE.md +22 -0
- data/docs/SESSIONS.md +48 -0
- data/docs/TOOLS.md +53 -0
- data/docs/TOOL_RUNTIME.md +154 -0
- data/docs/superpowers/plans/2026-03-26-production-ready-ollama-agent.md +2454 -0
- data/docs/superpowers/specs/2026-03-26-production-ready-ollama-agent-design.md +400 -0
- data/lib/ollama_agent/agent/agent_config.rb +53 -0
- data/lib/ollama_agent/agent/client_wiring.rb +76 -0
- data/lib/ollama_agent/agent/prompt_wiring.rb +55 -0
- data/lib/ollama_agent/agent/session_wiring.rb +53 -0
- data/lib/ollama_agent/agent.rb +148 -73
- data/lib/ollama_agent/agent_prompt.rb +31 -1
- data/lib/ollama_agent/chat_stream_carry.rb +88 -0
- data/lib/ollama_agent/chat_stream_thinking_format.rb +29 -0
- data/lib/ollama_agent/cli.rb +394 -4
- data/lib/ollama_agent/console.rb +177 -5
- data/lib/ollama_agent/context/manager.rb +100 -0
- data/lib/ollama_agent/context/token_counter.rb +33 -0
- data/lib/ollama_agent/diff_path_validator.rb +32 -10
- data/lib/ollama_agent/env_config.rb +44 -0
- data/lib/ollama_agent/external_agents/TODO-plan.md +1948 -0
- data/lib/ollama_agent/external_agents/argv_interp.rb +21 -0
- data/lib/ollama_agent/external_agents/default_agents.yml +60 -0
- data/lib/ollama_agent/external_agents/delegate_logger.rb +31 -0
- data/lib/ollama_agent/external_agents/delegate_timeout_status.rb +12 -0
- data/lib/ollama_agent/external_agents/env_helpers.rb +38 -0
- data/lib/ollama_agent/external_agents/path_validator.rb +32 -0
- data/lib/ollama_agent/external_agents/probe.rb +122 -0
- data/lib/ollama_agent/external_agents/registry.rb +50 -0
- data/lib/ollama_agent/external_agents/runner.rb +118 -0
- data/lib/ollama_agent/external_agents.rb +9 -0
- data/lib/ollama_agent/global_dotenv.rb +39 -0
- data/lib/ollama_agent/model_env.rb +26 -0
- data/lib/ollama_agent/ollama_chat_thinking_stream.rb +41 -0
- data/lib/ollama_agent/ollama_connection.rb +6 -1
- data/lib/ollama_agent/patch_risk.rb +81 -0
- data/lib/ollama_agent/patch_support.rb +27 -1
- data/lib/ollama_agent/path_sandbox.rb +62 -0
- data/lib/ollama_agent/prompt_skills/clean_ruby.md +131 -0
- data/lib/ollama_agent/prompt_skills/code_review.md +112 -0
- data/lib/ollama_agent/prompt_skills/design_patterns.md +56 -0
- data/lib/ollama_agent/prompt_skills/manifest.yml +25 -0
- data/lib/ollama_agent/prompt_skills/ollama_agent_patterns.md +132 -0
- data/lib/ollama_agent/prompt_skills/rails_best_practices.md +41 -0
- data/lib/ollama_agent/prompt_skills/rails_style.md +138 -0
- data/lib/ollama_agent/prompt_skills/rspec.md +280 -0
- data/lib/ollama_agent/prompt_skills/rubocop.md +7 -0
- data/lib/ollama_agent/prompt_skills/ruby_style.md +121 -0
- data/lib/ollama_agent/prompt_skills/solid.md +270 -0
- data/lib/ollama_agent/prompt_skills/solid_ruby.md +223 -0
- data/lib/ollama_agent/prompt_skills.rb +169 -0
- data/lib/ollama_agent/repo_list.rb +4 -1
- data/lib/ollama_agent/resilience/audit_logger.rb +79 -0
- data/lib/ollama_agent/resilience/retry_middleware.rb +45 -0
- data/lib/ollama_agent/resilience/retry_policy.rb +51 -0
- data/lib/ollama_agent/ruby_index_tool_support.rb +17 -6
- data/lib/ollama_agent/runner.rb +123 -0
- data/lib/ollama_agent/sandboxed_tools/delegate_external.rb +62 -0
- data/lib/ollama_agent/sandboxed_tools/file_read_write.rb +100 -0
- data/lib/ollama_agent/sandboxed_tools/search_text.rb +60 -0
- data/lib/ollama_agent/sandboxed_tools.rb +55 -116
- data/lib/ollama_agent/search_backend.rb +93 -0
- data/lib/ollama_agent/self_improvement/analyzer.rb +34 -0
- data/lib/ollama_agent/self_improvement/improver.rb +340 -0
- data/lib/ollama_agent/self_improvement/modes.rb +25 -0
- data/lib/ollama_agent/self_improvement/ruby_mastery_context.rb +66 -0
- data/lib/ollama_agent/self_improvement.rb +5 -0
- data/lib/ollama_agent/session/session.rb +8 -0
- data/lib/ollama_agent/session/store.rb +68 -0
- data/lib/ollama_agent/streaming/console_streamer.rb +29 -0
- data/lib/ollama_agent/streaming/hooks.rb +39 -0
- data/lib/ollama_agent/tool_arguments.rb +13 -1
- data/lib/ollama_agent/tool_content_parser.rb +1 -1
- data/lib/ollama_agent/tool_runtime/executor.rb +34 -0
- data/lib/ollama_agent/tool_runtime/json_extractor.rb +62 -0
- data/lib/ollama_agent/tool_runtime/loop.rb +72 -0
- data/lib/ollama_agent/tool_runtime/memory.rb +32 -0
- data/lib/ollama_agent/tool_runtime/ollama_json_planner.rb +98 -0
- data/lib/ollama_agent/tool_runtime/plan_extractor.rb +12 -0
- data/lib/ollama_agent/tool_runtime/registry.rb +60 -0
- data/lib/ollama_agent/tool_runtime/tool.rb +24 -0
- data/lib/ollama_agent/tool_runtime.rb +24 -0
- data/lib/ollama_agent/tools/registry.rb +55 -0
- data/lib/ollama_agent/tools_schema.rb +74 -1
- data/lib/ollama_agent/user_prompt.rb +35 -0
- data/lib/ollama_agent/version.rb +1 -1
- data/lib/ollama_agent.rb +25 -0
- data/reproduce_429.rb +40 -0
- data/sig/ollama_agent.rbs +111 -1
- 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
|
+
```
|