ollama-client 0.2.5 → 0.2.6
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/CHANGELOG.md +13 -0
- data/README.md +138 -76
- data/docs/EXAMPLE_REORGANIZATION.md +412 -0
- data/docs/GETTING_STARTED.md +361 -0
- data/docs/INTEGRATION_TESTING.md +170 -0
- data/docs/NEXT_STEPS_SUMMARY.md +114 -0
- data/docs/PERSONAS.md +383 -0
- data/docs/QUICK_START.md +195 -0
- data/docs/TESTING.md +392 -170
- data/docs/TEST_CHECKLIST.md +450 -0
- data/examples/README.md +51 -66
- data/examples/basic_chat.rb +33 -0
- data/examples/basic_generate.rb +29 -0
- data/examples/tool_calling_parsing.rb +59 -0
- data/exe/ollama-client +128 -1
- data/lib/ollama/agent/planner.rb +7 -2
- data/lib/ollama/chat_session.rb +101 -0
- data/lib/ollama/client.rb +41 -35
- data/lib/ollama/config.rb +4 -1
- data/lib/ollama/document_loader.rb +1 -1
- data/lib/ollama/embeddings.rb +41 -26
- data/lib/ollama/errors.rb +1 -0
- data/lib/ollama/personas.rb +287 -0
- data/lib/ollama/version.rb +1 -1
- data/lib/ollama_client.rb +7 -0
- metadata +14 -48
- data/examples/advanced_complex_schemas.rb +0 -366
- data/examples/advanced_edge_cases.rb +0 -241
- data/examples/advanced_error_handling.rb +0 -200
- data/examples/advanced_multi_step_agent.rb +0 -341
- data/examples/advanced_performance_testing.rb +0 -186
- data/examples/chat_console.rb +0 -143
- data/examples/complete_workflow.rb +0 -245
- data/examples/dhan_console.rb +0 -843
- data/examples/dhanhq/README.md +0 -236
- data/examples/dhanhq/agents/base_agent.rb +0 -74
- data/examples/dhanhq/agents/data_agent.rb +0 -66
- data/examples/dhanhq/agents/orchestrator_agent.rb +0 -120
- data/examples/dhanhq/agents/technical_analysis_agent.rb +0 -252
- data/examples/dhanhq/agents/trading_agent.rb +0 -81
- data/examples/dhanhq/analysis/market_structure.rb +0 -138
- data/examples/dhanhq/analysis/pattern_recognizer.rb +0 -192
- data/examples/dhanhq/analysis/trend_analyzer.rb +0 -88
- data/examples/dhanhq/builders/market_context_builder.rb +0 -67
- data/examples/dhanhq/dhanhq_agent.rb +0 -829
- data/examples/dhanhq/indicators/technical_indicators.rb +0 -158
- data/examples/dhanhq/scanners/intraday_options_scanner.rb +0 -492
- data/examples/dhanhq/scanners/swing_scanner.rb +0 -247
- data/examples/dhanhq/schemas/agent_schemas.rb +0 -61
- data/examples/dhanhq/services/base_service.rb +0 -46
- data/examples/dhanhq/services/data_service.rb +0 -118
- data/examples/dhanhq/services/trading_service.rb +0 -59
- data/examples/dhanhq/technical_analysis_agentic_runner.rb +0 -411
- data/examples/dhanhq/technical_analysis_runner.rb +0 -420
- data/examples/dhanhq/test_tool_calling.rb +0 -538
- data/examples/dhanhq/test_tool_calling_verbose.rb +0 -251
- data/examples/dhanhq/utils/instrument_helper.rb +0 -32
- data/examples/dhanhq/utils/parameter_cleaner.rb +0 -28
- data/examples/dhanhq/utils/parameter_normalizer.rb +0 -45
- data/examples/dhanhq/utils/rate_limiter.rb +0 -23
- data/examples/dhanhq/utils/trading_parameter_normalizer.rb +0 -72
- data/examples/dhanhq_agent.rb +0 -964
- data/examples/dhanhq_tools.rb +0 -1663
- data/examples/multi_step_agent_with_external_data.rb +0 -368
- data/examples/structured_outputs_chat.rb +0 -72
- data/examples/structured_tools.rb +0 -89
- data/examples/test_dhanhq_tool_calling.rb +0 -375
- data/examples/test_tool_calling.rb +0 -160
- data/examples/tool_calling_direct.rb +0 -124
- data/examples/tool_calling_pattern.rb +0 -269
- data/exe/dhan_console +0 -4
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
# Test Checklist: `ollama-client` Client-Only Testing
|
|
2
|
+
|
|
3
|
+
This checklist ensures comprehensive testing of the `ollama-client` transport layer without leaking agent concerns.
|
|
4
|
+
|
|
5
|
+
## Category A: `/generate` Mode Tests (Stateless, Deterministic)
|
|
6
|
+
|
|
7
|
+
### ✅ G1 — Basic Generate
|
|
8
|
+
- [ ] Response is a Hash
|
|
9
|
+
- [ ] JSON is parsed correctly
|
|
10
|
+
- [ ] No `tool_calls` present
|
|
11
|
+
- [ ] No streaming artifacts
|
|
12
|
+
- [ ] Schema validation passes (if schema provided)
|
|
13
|
+
|
|
14
|
+
**Test File:** `spec/ollama/client_generate_spec.rb`
|
|
15
|
+
|
|
16
|
+
**Example Test:**
|
|
17
|
+
```ruby
|
|
18
|
+
it "parses JSON response from generate endpoint" do
|
|
19
|
+
stub_request(:post, "http://localhost:11434/api/generate")
|
|
20
|
+
.to_return(
|
|
21
|
+
status: 200,
|
|
22
|
+
body: { response: '{"status":"ok"}' }.to_json
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
result = client.generate(
|
|
26
|
+
prompt: "Output a JSON object with a single key 'status' and value 'ok'.",
|
|
27
|
+
schema: { "type" => "object", "required" => ["status"] }
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
expect(result).to be_a(Hash)
|
|
31
|
+
expect(result["status"]).to eq("ok")
|
|
32
|
+
expect(result).not_to have_key("tool_calls")
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### ✅ G2 — Strict Schema Enforcement
|
|
39
|
+
- [ ] Raises error if schema violated
|
|
40
|
+
- [ ] Rejects extra fields (if strict mode enabled)
|
|
41
|
+
- [ ] Validates required fields
|
|
42
|
+
- [ ] Validates type constraints
|
|
43
|
+
- [ ] Validates enum constraints
|
|
44
|
+
- [ ] Validates number min/max
|
|
45
|
+
- [ ] Validates string minLength/maxLength
|
|
46
|
+
|
|
47
|
+
**Test File:** `spec/ollama/client_generate_spec.rb` or `spec/ollama/schema_validator_spec.rb`
|
|
48
|
+
|
|
49
|
+
**Example Test:**
|
|
50
|
+
```ruby
|
|
51
|
+
it "rejects responses that violate schema" do
|
|
52
|
+
stub_request(:post, "http://localhost:11434/api/generate")
|
|
53
|
+
.to_return(
|
|
54
|
+
status: 200,
|
|
55
|
+
body: { response: '{"count":"not-a-number"}' }.to_json
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
schema = {
|
|
59
|
+
"type" => "object",
|
|
60
|
+
"required" => ["count"],
|
|
61
|
+
"properties" => {
|
|
62
|
+
"count" => { "type" => "number" }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
expect do
|
|
67
|
+
client.generate(prompt: "Output JSON with key 'count' as a number.", schema: schema)
|
|
68
|
+
end.to raise_error(Ollama::SchemaViolationError)
|
|
69
|
+
end
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### ❌ G3 — Tool Attempt in Generate (Must Fail)
|
|
75
|
+
- [ ] No `tool_calls` parsed
|
|
76
|
+
- [ ] No silent acceptance of tool intent
|
|
77
|
+
- [ ] Either ignored or explicit error
|
|
78
|
+
- [ ] `/generate` remains non-agentic
|
|
79
|
+
|
|
80
|
+
**Test File:** `spec/ollama/client_generate_spec.rb`
|
|
81
|
+
|
|
82
|
+
**Example Test:**
|
|
83
|
+
```ruby
|
|
84
|
+
it "ignores tool calls in generate mode" do
|
|
85
|
+
stub_request(:post, "http://localhost:11434/api/generate")
|
|
86
|
+
.to_return(
|
|
87
|
+
status: 200,
|
|
88
|
+
body: { response: '{"action":"call read_file tool on foo.rb"}' }.to_json
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
result = client.generate(
|
|
92
|
+
prompt: "Call the read_file tool on foo.rb",
|
|
93
|
+
schema: { "type" => "object" }
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
expect(result).not_to have_key("tool_calls")
|
|
97
|
+
expect(result).not_to have_key("tool_use")
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Category B: `/chat` Mode Tests (Stateful, Tool-Aware)
|
|
104
|
+
|
|
105
|
+
### ✅ C1 — Simple Chat
|
|
106
|
+
- [ ] Response contains assistant message
|
|
107
|
+
- [ ] Message history preserved in request
|
|
108
|
+
- [ ] Role is correct
|
|
109
|
+
- [ ] Content is parsed correctly
|
|
110
|
+
|
|
111
|
+
**Test File:** `spec/ollama/client_chat_spec.rb` or `spec/ollama/client_chat_raw_spec.rb`
|
|
112
|
+
|
|
113
|
+
**Example Test:**
|
|
114
|
+
```ruby
|
|
115
|
+
it "handles simple chat messages" do
|
|
116
|
+
stub_request(:post, "http://localhost:11434/api/chat")
|
|
117
|
+
.to_return(
|
|
118
|
+
status: 200,
|
|
119
|
+
body: {
|
|
120
|
+
message: { role: "assistant", content: "Hello!" }
|
|
121
|
+
}.to_json
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
response = client.chat_raw(
|
|
125
|
+
messages: [{ role: "user", content: "Say hello." }],
|
|
126
|
+
allow_chat: true
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
expect(response.message.content).to eq("Hello!")
|
|
130
|
+
expect(response.message.role).to eq("assistant")
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### ✅ C2 — Tool-Call Parsing (Critical)
|
|
137
|
+
- [ ] `tool_calls` extracted correctly
|
|
138
|
+
- [ ] Tool name parsed
|
|
139
|
+
- [ ] Arguments parsed as hash
|
|
140
|
+
- [ ] **No execution happens** (client must not execute tools)
|
|
141
|
+
- [ ] Multiple tool calls handled
|
|
142
|
+
- [ ] Tool call ID preserved (if present)
|
|
143
|
+
|
|
144
|
+
**Test File:** `spec/ollama/client_chat_raw_spec.rb`
|
|
145
|
+
|
|
146
|
+
**Example Test:**
|
|
147
|
+
```ruby
|
|
148
|
+
it "extracts tool calls from chat response" do
|
|
149
|
+
stub_request(:post, "http://localhost:11434/api/chat")
|
|
150
|
+
.to_return(
|
|
151
|
+
status: 200,
|
|
152
|
+
body: {
|
|
153
|
+
message: {
|
|
154
|
+
role: "assistant",
|
|
155
|
+
content: "I'll call the ping tool.",
|
|
156
|
+
tool_calls: [
|
|
157
|
+
{
|
|
158
|
+
type: "function",
|
|
159
|
+
function: {
|
|
160
|
+
name: "ping",
|
|
161
|
+
arguments: { "x" => 1 }.to_json
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
}.to_json
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
response = client.chat_raw(
|
|
170
|
+
messages: [{ role: "user", content: "If a tool named 'ping' exists, call it with { 'x': 1 }." }],
|
|
171
|
+
tools: [tool_definition],
|
|
172
|
+
allow_chat: true
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
tool_calls = response.message.tool_calls
|
|
176
|
+
expect(tool_calls).not_to be_empty
|
|
177
|
+
expect(tool_calls.first["function"]["name"]).to eq("ping")
|
|
178
|
+
expect(JSON.parse(tool_calls.first["function"]["arguments"])).to eq("x" => 1)
|
|
179
|
+
end
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
### ✅ C3 — Tool Result Round-Trip Formatting
|
|
185
|
+
- [ ] Client serializes tool message correctly
|
|
186
|
+
- [ ] Ollama accepts tool result message
|
|
187
|
+
- [ ] Response parsed cleanly after tool result
|
|
188
|
+
- [ ] Tool name preserved
|
|
189
|
+
- [ ] Tool content serialized as JSON string
|
|
190
|
+
|
|
191
|
+
**Test File:** `spec/ollama/client_chat_raw_spec.rb`
|
|
192
|
+
|
|
193
|
+
**Example Test:**
|
|
194
|
+
```ruby
|
|
195
|
+
it "serializes tool result messages correctly" do
|
|
196
|
+
messages = [
|
|
197
|
+
{ role: "user", content: "Call ping tool" },
|
|
198
|
+
{ role: "assistant", content: "", tool_calls: [...] },
|
|
199
|
+
{ role: "tool", name: "ping", content: { ok: true }.to_json }
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
stub_request(:post, "http://localhost:11434/api/chat")
|
|
203
|
+
.with(body: hash_including(messages: messages))
|
|
204
|
+
.to_return(
|
|
205
|
+
status: 200,
|
|
206
|
+
body: { message: { role: "assistant", content: "Done!" } }.to_json
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
response = client.chat_raw(messages: messages, allow_chat: true)
|
|
210
|
+
expect(response.message.content).to eq("Done!")
|
|
211
|
+
end
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Category C: Protocol Adapters (Anthropic / Native)
|
|
217
|
+
|
|
218
|
+
### ✅ A1 — Anthropic Message Shape
|
|
219
|
+
- [ ] Messages serialized as content blocks
|
|
220
|
+
- [ ] Tool calls emitted as `tool_use` (if Anthropic mode)
|
|
221
|
+
- [ ] Tool results serialized as `tool_result`
|
|
222
|
+
- [ ] Request payload matches Anthropic format
|
|
223
|
+
|
|
224
|
+
**Test File:** `spec/ollama/client_chat_raw_spec.rb` or new `spec/ollama/protocol_adapter_spec.rb`
|
|
225
|
+
|
|
226
|
+
**Example Test:**
|
|
227
|
+
```ruby
|
|
228
|
+
it "serializes messages in Anthropic format" do
|
|
229
|
+
stub_request(:post, "http://localhost:11434/api/chat")
|
|
230
|
+
.with do |req|
|
|
231
|
+
body = JSON.parse(req.body)
|
|
232
|
+
expect(body["messages"]).to be_an(Array)
|
|
233
|
+
expect(body["messages"].first).to include("role", "content")
|
|
234
|
+
end
|
|
235
|
+
.to_return(status: 200, body: { message: {} }.to_json)
|
|
236
|
+
|
|
237
|
+
client.chat_raw(
|
|
238
|
+
messages: [{ role: "user", content: "Test" }],
|
|
239
|
+
allow_chat: true
|
|
240
|
+
)
|
|
241
|
+
end
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
### ✅ A2 — Anthropic Response Parsing
|
|
247
|
+
- [ ] Client normalizes Anthropic format into internal `tool_calls`
|
|
248
|
+
- [ ] Protocol adapter correctness
|
|
249
|
+
- [ ] Tool use ID preserved
|
|
250
|
+
- [ ] Tool input parsed correctly
|
|
251
|
+
|
|
252
|
+
**Test File:** `spec/ollama/client_chat_raw_spec.rb` or new `spec/ollama/protocol_adapter_spec.rb`
|
|
253
|
+
|
|
254
|
+
**Example Test:**
|
|
255
|
+
```ruby
|
|
256
|
+
it "normalizes Anthropic-style responses into internal format" do
|
|
257
|
+
anthropic_response = {
|
|
258
|
+
content: [
|
|
259
|
+
{
|
|
260
|
+
type: "tool_use",
|
|
261
|
+
id: "call_123",
|
|
262
|
+
name: "search",
|
|
263
|
+
input: { q: "foo" }
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
stub_request(:post, "http://localhost:11434/api/chat")
|
|
269
|
+
.to_return(status: 200, body: anthropic_response.to_json)
|
|
270
|
+
|
|
271
|
+
response = client.chat_raw(
|
|
272
|
+
messages: [{ role: "user", content: "Search for foo" }],
|
|
273
|
+
allow_chat: true
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
tool_calls = response.message.tool_calls
|
|
277
|
+
expect(tool_calls).not_to be_empty
|
|
278
|
+
expect(tool_calls.first["function"]["name"]).to eq("search")
|
|
279
|
+
end
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Category D: Failure Modes (Non-Negotiable)
|
|
285
|
+
|
|
286
|
+
### ✅ F1 — Ollama Down
|
|
287
|
+
- [ ] Connection refused raises correct exception
|
|
288
|
+
- [ ] No hangs
|
|
289
|
+
- [ ] Retries handled correctly (if retryable)
|
|
290
|
+
- [ ] Error message is clear
|
|
291
|
+
|
|
292
|
+
**Test File:** `spec/ollama/errors_spec.rb` or `spec/ollama/client_spec.rb`
|
|
293
|
+
|
|
294
|
+
**Example Test:**
|
|
295
|
+
```ruby
|
|
296
|
+
it "handles connection refused gracefully" do
|
|
297
|
+
stub_request(:post, "http://localhost:11434/api/generate")
|
|
298
|
+
.to_raise(Errno::ECONNREFUSED)
|
|
299
|
+
|
|
300
|
+
start_time = Time.now
|
|
301
|
+
expect do
|
|
302
|
+
client.generate(prompt: "test", schema: schema)
|
|
303
|
+
end.to raise_error(Ollama::Error)
|
|
304
|
+
|
|
305
|
+
# Verify no hangs
|
|
306
|
+
expect(Time.now - start_time).to be < 5
|
|
307
|
+
end
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
### ✅ F2 — Invalid JSON from Model
|
|
313
|
+
- [ ] Client raises parse error
|
|
314
|
+
- [ ] Does not silently continue
|
|
315
|
+
- [ ] Retries handled (if retryable)
|
|
316
|
+
- [ ] Error message includes context
|
|
317
|
+
|
|
318
|
+
**Test File:** `spec/ollama/errors_spec.rb`
|
|
319
|
+
|
|
320
|
+
**Example Test:**
|
|
321
|
+
```ruby
|
|
322
|
+
it "raises error on invalid JSON response" do
|
|
323
|
+
stub_request(:post, "http://localhost:11434/api/generate")
|
|
324
|
+
.to_return(status: 200, body: { response: "not json at all" }.to_json)
|
|
325
|
+
|
|
326
|
+
expect do
|
|
327
|
+
client.generate(prompt: "test", schema: schema)
|
|
328
|
+
end.to raise_error(Ollama::InvalidJSONError)
|
|
329
|
+
end
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
### ✅ F3 — Streaming Interruption
|
|
335
|
+
- [ ] Partial stream handled
|
|
336
|
+
- [ ] Client terminates cleanly
|
|
337
|
+
- [ ] No corrupted state
|
|
338
|
+
- [ ] Error raised appropriately
|
|
339
|
+
|
|
340
|
+
**Test File:** `spec/ollama/client_chat_raw_spec.rb` (if streaming tests exist)
|
|
341
|
+
|
|
342
|
+
**Example Test:**
|
|
343
|
+
```ruby
|
|
344
|
+
it "handles partial stream gracefully" do
|
|
345
|
+
stub_request(:post, "http://localhost:11434/api/chat")
|
|
346
|
+
.to_return(
|
|
347
|
+
status: 200,
|
|
348
|
+
body: "data: {\"message\":{\"content\":\"partial\"}}\n",
|
|
349
|
+
headers: { "Content-Type" => "text/event-stream" }
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# Simulate stream interruption
|
|
353
|
+
expect do
|
|
354
|
+
client.chat_raw(messages: [{ role: "user", content: "test" }], allow_chat: true)
|
|
355
|
+
end.to raise_error(Ollama::Error)
|
|
356
|
+
end
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Additional Test Areas
|
|
362
|
+
|
|
363
|
+
### Retry Logic
|
|
364
|
+
- [ ] Retries up to `config.retries` times
|
|
365
|
+
- [ ] Only retries retryable errors (5xx, 408, 429)
|
|
366
|
+
- [ ] Raises `RetryExhaustedError` after max retries
|
|
367
|
+
- [ ] Succeeds if retry succeeds
|
|
368
|
+
- [ ] Non-retryable errors (400, 404) fail immediately
|
|
369
|
+
|
|
370
|
+
### Error Handling
|
|
371
|
+
- [ ] **404 (NotFoundError)**: Model not found, no retries, includes suggestions
|
|
372
|
+
- [ ] **500 (HTTPError)**: Retryable, retries up to config limit
|
|
373
|
+
- [ ] **400 (HTTPError)**: Non-retryable, fails immediately
|
|
374
|
+
- [ ] **TimeoutError**: Retries on timeout
|
|
375
|
+
- [ ] **InvalidJSONError**: Retries on JSON parse errors
|
|
376
|
+
- [ ] **SchemaViolationError**: Retries on schema validation failures
|
|
377
|
+
- [ ] **Connection Errors**: Retries on network failures
|
|
378
|
+
|
|
379
|
+
### Edge Cases
|
|
380
|
+
- [ ] JSON wrapped in markdown code blocks
|
|
381
|
+
- [ ] Plain JSON responses
|
|
382
|
+
- [ ] Empty model lists
|
|
383
|
+
- [ ] Missing response fields
|
|
384
|
+
- [ ] Malformed JSON
|
|
385
|
+
- [ ] Empty prompts
|
|
386
|
+
- [ ] Very long prompts
|
|
387
|
+
- [ ] Special characters in prompts
|
|
388
|
+
|
|
389
|
+
### Model Suggestions
|
|
390
|
+
- [ ] Suggests similar models on 404
|
|
391
|
+
- [ ] Fuzzy matching on model names
|
|
392
|
+
- [ ] Limits suggestions to 5 models
|
|
393
|
+
- [ ] Handles model listing failures gracefully
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## What NOT to Test (Agent Concerns)
|
|
398
|
+
|
|
399
|
+
❌ **Do not test:**
|
|
400
|
+
- Infinite loops
|
|
401
|
+
- Retries based on content
|
|
402
|
+
- Agent stopping behavior
|
|
403
|
+
- Tool side effects
|
|
404
|
+
- Correctness of answers
|
|
405
|
+
- Agent convergence logic
|
|
406
|
+
- Policy decisions
|
|
407
|
+
- Tool execution
|
|
408
|
+
- Agent state management
|
|
409
|
+
|
|
410
|
+
**Those belong to `agent-runtime` and app repos.**
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Test Coverage Goals
|
|
415
|
+
|
|
416
|
+
- **Transport layer**: 100% coverage
|
|
417
|
+
- **Protocol parsing**: 100% coverage
|
|
418
|
+
- **Error handling**: 100% coverage
|
|
419
|
+
- **Schema validation**: 100% coverage
|
|
420
|
+
- **Tool-call parsing**: 100% coverage
|
|
421
|
+
|
|
422
|
+
**Note:** We don't aim for 100% line coverage of agent logic because agent logic doesn't belong in this gem.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Running the Checklist
|
|
427
|
+
|
|
428
|
+
1. Review each category
|
|
429
|
+
2. Mark tests as complete when implemented
|
|
430
|
+
3. Add test file references
|
|
431
|
+
4. Update this checklist as new test categories are identified
|
|
432
|
+
5. Remove tests that leak agent concerns
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Test File Organization
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
spec/
|
|
440
|
+
├── ollama/
|
|
441
|
+
│ ├── client_spec.rb # Basic initialization, config
|
|
442
|
+
│ ├── client_generate_spec.rb # Category A (G1-G3)
|
|
443
|
+
│ ├── client_chat_spec.rb # Category B (C1-C3) - basic chat
|
|
444
|
+
│ ├── client_chat_raw_spec.rb # Category B (C1-C3) - tool calls
|
|
445
|
+
│ ├── protocol_adapter_spec.rb # Category C (A1-A2) - if needed
|
|
446
|
+
│ ├── errors_spec.rb # Category D (F1-F3)
|
|
447
|
+
│ ├── schema_validator_spec.rb # Category A (G2)
|
|
448
|
+
│ ├── client_list_models_spec.rb # Model listing
|
|
449
|
+
│ └── client_model_suggestions_spec.rb # Model suggestions
|
|
450
|
+
```
|
data/examples/README.md
CHANGED
|
@@ -1,92 +1,77 @@
|
|
|
1
1
|
# Examples
|
|
2
2
|
|
|
3
|
-
This directory contains
|
|
3
|
+
This directory contains **minimal examples** demonstrating `ollama-client` usage. These examples focus on **transport and protocol correctness**, not agent behavior.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Minimal Examples
|
|
6
6
|
|
|
7
|
-
### Basic
|
|
8
|
-
- **[test_tool_calling.rb](test_tool_calling.rb)** - Simple tool calling demo with weather tool
|
|
9
|
-
- **[tool_calling_pattern.rb](tool_calling_pattern.rb)** - Recommended patterns for tool calling
|
|
10
|
-
- **[tool_calling_direct.rb](tool_calling_direct.rb)** - Direct tool calling without Executor
|
|
7
|
+
### Basic Client Usage
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
9
|
+
- **[basic_generate.rb](basic_generate.rb)** - Basic `/generate` usage with schema validation
|
|
10
|
+
- Demonstrates stateless, deterministic JSON output
|
|
11
|
+
- Shows schema enforcement
|
|
12
|
+
- No agent logic
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
14
|
+
- **[basic_chat.rb](basic_chat.rb)** - Basic `/chat` usage
|
|
15
|
+
- Demonstrates stateful message handling
|
|
16
|
+
- Shows multi-turn conversation structure
|
|
17
|
+
- No agent loops
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
- **
|
|
19
|
+
- **[tool_calling_parsing.rb](tool_calling_parsing.rb)** - Tool-call parsing (no execution)
|
|
20
|
+
- Demonstrates tool-call detection and extraction
|
|
21
|
+
- Shows how to parse tool calls from LLM response
|
|
22
|
+
- **Does NOT execute tools** - that's agent responsibility
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
- **[advanced_edge_cases.rb](advanced_edge_cases.rb)** - Edge case handling
|
|
30
|
-
- **[advanced_complex_schemas.rb](advanced_complex_schemas.rb)** - Complex schema definitions
|
|
31
|
-
- **[advanced_performance_testing.rb](advanced_performance_testing.rb)** - Performance testing
|
|
32
|
-
|
|
33
|
-
### Interactive Consoles
|
|
34
|
-
- **[chat_console.rb](chat_console.rb)** - Simple chat console with streaming
|
|
35
|
-
- **[dhan_console.rb](dhan_console.rb)** - DhanHQ market data console with formatted tool results
|
|
36
|
-
|
|
37
|
-
### Complete Workflows
|
|
38
|
-
- **[complete_workflow.rb](complete_workflow.rb)** - Complete agent workflow example
|
|
39
|
-
|
|
40
|
-
## DhanHQ Examples
|
|
41
|
-
|
|
42
|
-
The `dhanhq/` subdirectory contains more specialized DhanHQ examples:
|
|
43
|
-
- Technical analysis agents
|
|
44
|
-
- Market scanners (intraday options, swing trading)
|
|
45
|
-
- Pattern recognition and trend analysis
|
|
46
|
-
- Multi-agent orchestration
|
|
47
|
-
|
|
48
|
-
See [dhanhq/README.md](dhanhq/README.md) for details.
|
|
24
|
+
- **[tool_dto_example.rb](tool_dto_example.rb)** - Tool DTO serialization
|
|
25
|
+
- Demonstrates Tool class serialization/deserialization
|
|
26
|
+
- Shows DTO functionality
|
|
49
27
|
|
|
50
28
|
## Running Examples
|
|
51
29
|
|
|
52
|
-
|
|
30
|
+
All examples are standalone and can be run directly:
|
|
53
31
|
|
|
54
32
|
```bash
|
|
55
|
-
# Basic
|
|
56
|
-
ruby examples/
|
|
33
|
+
# Basic generate
|
|
34
|
+
ruby examples/basic_generate.rb
|
|
35
|
+
|
|
36
|
+
# Basic chat
|
|
37
|
+
ruby examples/basic_chat.rb
|
|
57
38
|
|
|
58
|
-
#
|
|
59
|
-
ruby examples/
|
|
39
|
+
# Tool calling parsing
|
|
40
|
+
ruby examples/tool_calling_parsing.rb
|
|
60
41
|
|
|
61
|
-
#
|
|
62
|
-
ruby examples/
|
|
42
|
+
# Tool DTO
|
|
43
|
+
ruby examples/tool_dto_example.rb
|
|
63
44
|
```
|
|
64
45
|
|
|
65
46
|
### Requirements
|
|
66
47
|
|
|
67
|
-
Some examples require additional setup:
|
|
68
|
-
|
|
69
|
-
**DhanHQ Examples:**
|
|
70
|
-
- Set `DHANHQ_CLIENT_ID` and `DHANHQ_ACCESS_TOKEN` environment variables
|
|
71
|
-
- Or create `.env` file with credentials
|
|
72
|
-
|
|
73
|
-
**Ollama:**
|
|
74
48
|
- Ollama server running (default: `http://localhost:11434`)
|
|
75
49
|
- Set `OLLAMA_BASE_URL` if using a different URL
|
|
76
50
|
- Set `OLLAMA_MODEL` if not using default model
|
|
77
51
|
|
|
78
|
-
##
|
|
52
|
+
## Full Agent Examples
|
|
53
|
+
|
|
54
|
+
For complete agent examples (trading agents, coding agents, RAG agents, multi-step workflows, tool execution patterns, etc.), see:
|
|
55
|
+
|
|
56
|
+
**[ollama-agent-examples](https://github.com/shubhamtaywade82/ollama-agent-examples)**
|
|
57
|
+
|
|
58
|
+
This separation keeps `ollama-client` focused on the transport layer while providing comprehensive examples for agent developers.
|
|
59
|
+
|
|
60
|
+
## What These Examples Demonstrate
|
|
61
|
+
|
|
62
|
+
These minimal examples prove:
|
|
63
|
+
|
|
64
|
+
✅ **Transport layer** - HTTP requests/responses
|
|
65
|
+
✅ **Protocol correctness** - Request shaping, response parsing
|
|
66
|
+
✅ **Schema enforcement** - JSON validation
|
|
67
|
+
✅ **Tool-call parsing** - Detecting and extracting tool calls
|
|
79
68
|
|
|
80
|
-
|
|
81
|
-
2. **Structured data:** `structured_outputs_chat.rb` - Schema-based outputs
|
|
82
|
-
3. **Multi-step:** `multi_step_agent_e2e.rb` - Complex agent workflows
|
|
83
|
-
4. **Market data:** `test_dhanhq_tool_calling.rb` - Real-world API integration
|
|
84
|
-
5. **Interactive:** `dhan_console.rb` - Full-featured console with planning
|
|
69
|
+
These examples do **NOT** demonstrate:
|
|
85
70
|
|
|
86
|
-
|
|
71
|
+
❌ Agent loops
|
|
72
|
+
❌ Tool execution
|
|
73
|
+
❌ Convergence logic
|
|
74
|
+
❌ Policy decisions
|
|
75
|
+
❌ Domain-specific logic
|
|
87
76
|
|
|
88
|
-
|
|
89
|
-
- Include clear comments explaining what the example demonstrates
|
|
90
|
-
- Add `#!/usr/bin/env ruby` shebang at the top
|
|
91
|
-
- Use `frozen_string_literal: true`
|
|
92
|
-
- Update this README with a description
|
|
77
|
+
**Those belong in the separate agent examples repository.**
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Basic /chat usage
|
|
5
|
+
# Demonstrates client transport layer - stateful message handling
|
|
6
|
+
|
|
7
|
+
require_relative "../lib/ollama_client"
|
|
8
|
+
|
|
9
|
+
client = Ollama::Client.new
|
|
10
|
+
|
|
11
|
+
# Simple chat message
|
|
12
|
+
response = client.chat_raw(
|
|
13
|
+
messages: [{ role: "user", content: "Say hello." }],
|
|
14
|
+
allow_chat: true
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
puts "Response: #{response.message.content}"
|
|
18
|
+
puts "Role: #{response.message.role}"
|
|
19
|
+
|
|
20
|
+
# Multi-turn conversation
|
|
21
|
+
messages = [
|
|
22
|
+
{ role: "user", content: "What is Ruby?" }
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
response1 = client.chat_raw(messages: messages, allow_chat: true)
|
|
26
|
+
puts "\nFirst response: #{response1.message.content}"
|
|
27
|
+
|
|
28
|
+
# Continue conversation
|
|
29
|
+
messages << { role: "assistant", content: response1.message.content }
|
|
30
|
+
messages << { role: "user", content: "Tell me more about its use cases" }
|
|
31
|
+
|
|
32
|
+
response2 = client.chat_raw(messages: messages, allow_chat: true)
|
|
33
|
+
puts "\nSecond response: #{response2.message.content}"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example: Basic /generate usage with schema validation
|
|
5
|
+
# Demonstrates client transport layer - stateless, deterministic JSON output
|
|
6
|
+
|
|
7
|
+
require_relative "../lib/ollama_client"
|
|
8
|
+
|
|
9
|
+
client = Ollama::Client.new
|
|
10
|
+
|
|
11
|
+
# Define schema for structured output
|
|
12
|
+
schema = {
|
|
13
|
+
"type" => "object",
|
|
14
|
+
"required" => ["status"],
|
|
15
|
+
"properties" => {
|
|
16
|
+
"status" => { "type" => "string" }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Generate structured JSON response
|
|
21
|
+
result = client.generate(
|
|
22
|
+
prompt: "Output a JSON object with a single key 'status' and value 'ok'.",
|
|
23
|
+
schema: schema
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
puts "Result: #{result.inspect}"
|
|
27
|
+
puts "Status: #{result['status']}" # => "ok"
|
|
28
|
+
|
|
29
|
+
# The result is guaranteed to match your schema!
|