ollama-client 0.2.5 → 0.2.7

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +336 -91
  4. data/RELEASE_NOTES_v0.2.6.md +41 -0
  5. data/docs/AREAS_FOR_CONSIDERATION.md +325 -0
  6. data/docs/EXAMPLE_REORGANIZATION.md +412 -0
  7. data/docs/FEATURES_ADDED.md +12 -1
  8. data/docs/GETTING_STARTED.md +361 -0
  9. data/docs/INTEGRATION_TESTING.md +170 -0
  10. data/docs/NEXT_STEPS_SUMMARY.md +114 -0
  11. data/docs/PERSONAS.md +383 -0
  12. data/docs/QUICK_START.md +195 -0
  13. data/docs/TESTING.md +392 -170
  14. data/docs/TEST_CHECKLIST.md +450 -0
  15. data/examples/README.md +62 -63
  16. data/examples/basic_chat.rb +33 -0
  17. data/examples/basic_generate.rb +29 -0
  18. data/examples/mcp_executor.rb +39 -0
  19. data/examples/mcp_http_executor.rb +45 -0
  20. data/examples/tool_calling_parsing.rb +59 -0
  21. data/examples/tool_dto_example.rb +0 -0
  22. data/exe/ollama-client +128 -1
  23. data/lib/ollama/agent/planner.rb +7 -2
  24. data/lib/ollama/chat_session.rb +101 -0
  25. data/lib/ollama/client.rb +41 -35
  26. data/lib/ollama/config.rb +9 -4
  27. data/lib/ollama/document_loader.rb +1 -1
  28. data/lib/ollama/embeddings.rb +61 -28
  29. data/lib/ollama/errors.rb +1 -0
  30. data/lib/ollama/mcp/http_client.rb +149 -0
  31. data/lib/ollama/mcp/stdio_client.rb +146 -0
  32. data/lib/ollama/mcp/tools_bridge.rb +72 -0
  33. data/lib/ollama/mcp.rb +31 -0
  34. data/lib/ollama/options.rb +3 -1
  35. data/lib/ollama/personas.rb +287 -0
  36. data/lib/ollama/version.rb +1 -1
  37. data/lib/ollama_client.rb +17 -5
  38. metadata +22 -48
  39. data/examples/advanced_complex_schemas.rb +0 -366
  40. data/examples/advanced_edge_cases.rb +0 -241
  41. data/examples/advanced_error_handling.rb +0 -200
  42. data/examples/advanced_multi_step_agent.rb +0 -341
  43. data/examples/advanced_performance_testing.rb +0 -186
  44. data/examples/chat_console.rb +0 -143
  45. data/examples/complete_workflow.rb +0 -245
  46. data/examples/dhan_console.rb +0 -843
  47. data/examples/dhanhq/README.md +0 -236
  48. data/examples/dhanhq/agents/base_agent.rb +0 -74
  49. data/examples/dhanhq/agents/data_agent.rb +0 -66
  50. data/examples/dhanhq/agents/orchestrator_agent.rb +0 -120
  51. data/examples/dhanhq/agents/technical_analysis_agent.rb +0 -252
  52. data/examples/dhanhq/agents/trading_agent.rb +0 -81
  53. data/examples/dhanhq/analysis/market_structure.rb +0 -138
  54. data/examples/dhanhq/analysis/pattern_recognizer.rb +0 -192
  55. data/examples/dhanhq/analysis/trend_analyzer.rb +0 -88
  56. data/examples/dhanhq/builders/market_context_builder.rb +0 -67
  57. data/examples/dhanhq/dhanhq_agent.rb +0 -829
  58. data/examples/dhanhq/indicators/technical_indicators.rb +0 -158
  59. data/examples/dhanhq/scanners/intraday_options_scanner.rb +0 -492
  60. data/examples/dhanhq/scanners/swing_scanner.rb +0 -247
  61. data/examples/dhanhq/schemas/agent_schemas.rb +0 -61
  62. data/examples/dhanhq/services/base_service.rb +0 -46
  63. data/examples/dhanhq/services/data_service.rb +0 -118
  64. data/examples/dhanhq/services/trading_service.rb +0 -59
  65. data/examples/dhanhq/technical_analysis_agentic_runner.rb +0 -411
  66. data/examples/dhanhq/technical_analysis_runner.rb +0 -420
  67. data/examples/dhanhq/test_tool_calling.rb +0 -538
  68. data/examples/dhanhq/test_tool_calling_verbose.rb +0 -251
  69. data/examples/dhanhq/utils/instrument_helper.rb +0 -32
  70. data/examples/dhanhq/utils/parameter_cleaner.rb +0 -28
  71. data/examples/dhanhq/utils/parameter_normalizer.rb +0 -45
  72. data/examples/dhanhq/utils/rate_limiter.rb +0 -23
  73. data/examples/dhanhq/utils/trading_parameter_normalizer.rb +0 -72
  74. data/examples/dhanhq_agent.rb +0 -964
  75. data/examples/dhanhq_tools.rb +0 -1663
  76. data/examples/multi_step_agent_with_external_data.rb +0 -368
  77. data/examples/structured_outputs_chat.rb +0 -72
  78. data/examples/structured_tools.rb +0 -89
  79. data/examples/test_dhanhq_tool_calling.rb +0 -375
  80. data/examples/test_tool_calling.rb +0 -160
  81. data/examples/tool_calling_direct.rb +0 -124
  82. data/examples/tool_calling_pattern.rb +0 -269
  83. 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,91 @@
1
1
  # Examples
2
2
 
3
- This directory contains working examples demonstrating various features of the ollama-client gem.
3
+ This directory contains **minimal examples** demonstrating `ollama-client` usage. These examples focus on **transport and protocol correctness**, not agent behavior.
4
4
 
5
- ## Quick Start Examples
5
+ ## Minimal Examples
6
6
 
7
- ### Basic Tool Calling
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
- ### DhanHQ Market Data
13
- - **[test_dhanhq_tool_calling.rb](test_dhanhq_tool_calling.rb)** - DhanHQ tools with updated intraday & indicators
14
- - **[dhanhq_tools.rb](dhanhq_tools.rb)** - DhanHQ API wrapper tools
15
- - **[dhan_console.rb](dhan_console.rb)** - Interactive DhanHQ console with planning
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
- ### Multi-Step Agents
18
- - **[multi_step_agent_e2e.rb](multi_step_agent_e2e.rb)** - End-to-end multi-step agent example
19
- - **[multi_step_agent_with_external_data.rb](multi_step_agent_with_external_data.rb)** - Agent with external data integration
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
- ### Structured Data
22
- - **[structured_outputs_chat.rb](structured_outputs_chat.rb)** - Structured outputs with schemas
23
- - **[structured_tools.rb](structured_tools.rb)** - Structured tool definitions
24
- - **[tool_dto_example.rb](tool_dto_example.rb)** - Using DTOs for tool definitions
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
- ### Advanced Features
27
- - **[advanced_multi_step_agent.rb](advanced_multi_step_agent.rb)** - Complex multi-step workflows
28
- - **[advanced_error_handling.rb](advanced_error_handling.rb)** - Error handling patterns
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
24
+ - **[tool_dto_example.rb](tool_dto_example.rb)** - Tool DTO serialization
25
+ - Demonstrates Tool class serialization/deserialization
26
+ - Shows DTO functionality
32
27
 
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
28
+ - **[mcp_executor.rb](mcp_executor.rb)** - MCP tools with Executor (local stdio)
29
+ - Connects to a local MCP server (stdio)
30
+ - Requires Node.js/npx for the filesystem server example
36
31
 
37
- ### Complete Workflows
38
- - **[complete_workflow.rb](complete_workflow.rb)** - Complete agent workflow example
32
+ - **[mcp_http_executor.rb](mcp_http_executor.rb)** - MCP tools with Executor (remote URL)
33
+ - Connects to a remote MCP server via HTTP (e.g. [gitmcp.io](https://gitmcp.io)/owner/repo)
34
+ - Use the same URL you would add to Cursor’s `mcp.json`
39
35
 
40
- ## DhanHQ Examples
36
+ ## Running Examples
41
37
 
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
38
+ All examples are standalone and can be run directly:
47
39
 
48
- See [dhanhq/README.md](dhanhq/README.md) for details.
40
+ ```bash
41
+ # Basic generate
42
+ ruby examples/basic_generate.rb
49
43
 
50
- ## Running Examples
44
+ # Basic chat
45
+ ruby examples/basic_chat.rb
51
46
 
52
- Most examples are standalone and can be run directly:
47
+ # Tool calling parsing
48
+ ruby examples/tool_calling_parsing.rb
53
49
 
54
- ```bash
55
- # Basic tool calling
56
- ruby examples/test_tool_calling.rb
50
+ # Tool DTO
51
+ ruby examples/tool_dto_example.rb
57
52
 
58
- # DhanHQ with intraday data
59
- ruby examples/test_dhanhq_tool_calling.rb
53
+ # MCP Executor (local stdio; requires Node.js/npx)
54
+ ruby examples/mcp_executor.rb
60
55
 
61
- # Interactive console
62
- ruby examples/chat_console.rb
56
+ # MCP Executor (remote URL, e.g. GitMCP)
57
+ ruby examples/mcp_http_executor.rb
63
58
  ```
64
59
 
65
60
  ### Requirements
66
61
 
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
62
  - Ollama server running (default: `http://localhost:11434`)
75
63
  - Set `OLLAMA_BASE_URL` if using a different URL
76
64
  - Set `OLLAMA_MODEL` if not using default model
77
65
 
78
- ## Learning Path
66
+ ## Full Agent Examples
67
+
68
+ For complete agent examples (trading agents, coding agents, RAG agents, multi-step workflows, tool execution patterns, etc.), see:
69
+
70
+ **[ollama-agent-examples](https://github.com/shubhamtaywade82/ollama-agent-examples)**
71
+
72
+ This separation keeps `ollama-client` focused on the transport layer while providing comprehensive examples for agent developers.
73
+
74
+ ## What These Examples Demonstrate
75
+
76
+ These minimal examples prove:
77
+
78
+ ✅ **Transport layer** - HTTP requests/responses
79
+ ✅ **Protocol correctness** - Request shaping, response parsing
80
+ ✅ **Schema enforcement** - JSON validation
81
+ ✅ **Tool-call parsing** - Detecting and extracting tool calls
79
82
 
80
- 1. **Start here:** `test_tool_calling.rb` - Learn basic tool calling
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
83
+ These examples do **NOT** demonstrate:
85
84
 
86
- ## Contributing
85
+ Agent loops
86
+ ❌ Tool execution
87
+ ❌ Convergence logic
88
+ ❌ Policy decisions
89
+ ❌ Domain-specific logic
87
90
 
88
- When adding new examples:
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
91
+ **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!