ollama-client 0.2.4 → 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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -1
  3. data/README.md +560 -106
  4. data/docs/EXAMPLE_REORGANIZATION.md +412 -0
  5. data/docs/GETTING_STARTED.md +361 -0
  6. data/docs/INTEGRATION_TESTING.md +170 -0
  7. data/docs/NEXT_STEPS_SUMMARY.md +114 -0
  8. data/docs/PERSONAS.md +383 -0
  9. data/docs/QUICK_START.md +195 -0
  10. data/docs/README.md +2 -3
  11. data/docs/RELEASE_GUIDE.md +376 -0
  12. data/docs/TESTING.md +392 -170
  13. data/docs/TEST_CHECKLIST.md +450 -0
  14. data/docs/ruby_guide.md +6232 -0
  15. data/examples/README.md +51 -66
  16. data/examples/basic_chat.rb +33 -0
  17. data/examples/basic_generate.rb +29 -0
  18. data/examples/tool_calling_parsing.rb +59 -0
  19. data/exe/ollama-client +128 -1
  20. data/lib/ollama/agent/planner.rb +7 -2
  21. data/lib/ollama/chat_session.rb +101 -0
  22. data/lib/ollama/client.rb +43 -21
  23. data/lib/ollama/config.rb +4 -1
  24. data/lib/ollama/document_loader.rb +163 -0
  25. data/lib/ollama/embeddings.rb +42 -13
  26. data/lib/ollama/errors.rb +1 -0
  27. data/lib/ollama/personas.rb +287 -0
  28. data/lib/ollama/version.rb +1 -1
  29. data/lib/ollama_client.rb +8 -0
  30. metadata +31 -53
  31. data/docs/GEM_RELEASE_GUIDE.md +0 -794
  32. data/docs/GET_RUBYGEMS_SECRET.md +0 -151
  33. data/docs/QUICK_OTP_SETUP.md +0 -80
  34. data/docs/QUICK_RELEASE.md +0 -106
  35. data/docs/RUBYGEMS_OTP_SETUP.md +0 -199
  36. data/examples/advanced_complex_schemas.rb +0 -366
  37. data/examples/advanced_edge_cases.rb +0 -241
  38. data/examples/advanced_error_handling.rb +0 -200
  39. data/examples/advanced_multi_step_agent.rb +0 -341
  40. data/examples/advanced_performance_testing.rb +0 -186
  41. data/examples/chat_console.rb +0 -143
  42. data/examples/complete_workflow.rb +0 -245
  43. data/examples/dhan_console.rb +0 -843
  44. data/examples/dhanhq/README.md +0 -236
  45. data/examples/dhanhq/agents/base_agent.rb +0 -74
  46. data/examples/dhanhq/agents/data_agent.rb +0 -66
  47. data/examples/dhanhq/agents/orchestrator_agent.rb +0 -120
  48. data/examples/dhanhq/agents/technical_analysis_agent.rb +0 -252
  49. data/examples/dhanhq/agents/trading_agent.rb +0 -81
  50. data/examples/dhanhq/analysis/market_structure.rb +0 -138
  51. data/examples/dhanhq/analysis/pattern_recognizer.rb +0 -192
  52. data/examples/dhanhq/analysis/trend_analyzer.rb +0 -88
  53. data/examples/dhanhq/builders/market_context_builder.rb +0 -67
  54. data/examples/dhanhq/dhanhq_agent.rb +0 -829
  55. data/examples/dhanhq/indicators/technical_indicators.rb +0 -158
  56. data/examples/dhanhq/scanners/intraday_options_scanner.rb +0 -492
  57. data/examples/dhanhq/scanners/swing_scanner.rb +0 -247
  58. data/examples/dhanhq/schemas/agent_schemas.rb +0 -61
  59. data/examples/dhanhq/services/base_service.rb +0 -46
  60. data/examples/dhanhq/services/data_service.rb +0 -118
  61. data/examples/dhanhq/services/trading_service.rb +0 -59
  62. data/examples/dhanhq/technical_analysis_agentic_runner.rb +0 -411
  63. data/examples/dhanhq/technical_analysis_runner.rb +0 -420
  64. data/examples/dhanhq/test_tool_calling.rb +0 -538
  65. data/examples/dhanhq/test_tool_calling_verbose.rb +0 -251
  66. data/examples/dhanhq/utils/instrument_helper.rb +0 -32
  67. data/examples/dhanhq/utils/parameter_cleaner.rb +0 -28
  68. data/examples/dhanhq/utils/parameter_normalizer.rb +0 -45
  69. data/examples/dhanhq/utils/rate_limiter.rb +0 -23
  70. data/examples/dhanhq/utils/trading_parameter_normalizer.rb +0 -72
  71. data/examples/dhanhq_agent.rb +0 -964
  72. data/examples/dhanhq_tools.rb +0 -1663
  73. data/examples/multi_step_agent_with_external_data.rb +0 -368
  74. data/examples/structured_outputs_chat.rb +0 -72
  75. data/examples/structured_tools.rb +0 -89
  76. data/examples/test_dhanhq_tool_calling.rb +0 -375
  77. data/examples/test_tool_calling.rb +0 -160
  78. data/examples/tool_calling_direct.rb +0 -124
  79. data/examples/tool_calling_pattern.rb +0 -269
  80. data/exe/dhan_console +0 -4
@@ -1,200 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Advanced Example: Comprehensive Error Handling and Recovery Patterns
5
- # Demonstrates: All error types, retry strategies, fallback mechanisms, observability
6
-
7
- require "json"
8
- require_relative "../lib/ollama_client"
9
-
10
- class ResilientAgent
11
- def initialize(client:)
12
- @client = client
13
- @stats = {
14
- total_calls: 0,
15
- successes: 0,
16
- retries: 0,
17
- failures: 0,
18
- errors_by_type: {}
19
- }
20
- end
21
-
22
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
23
- def execute_with_resilience(prompt:, schema:, max_attempts: 3)
24
- @stats[:total_calls] += 1
25
- attempt = 0
26
-
27
- loop do
28
- attempt += 1
29
- puts "\n📞 Attempt #{attempt}/#{max_attempts}"
30
-
31
- begin
32
- result = @client.generate(prompt: prompt, schema: schema)
33
- @stats[:successes] += 1
34
- puts "✅ Success on attempt #{attempt}"
35
- return { success: true, result: result, attempts: attempt }
36
- rescue Ollama::NotFoundError => e
37
- @stats[:failures] += 1
38
- @stats[:errors_by_type]["NotFoundError"] ||= 0
39
- @stats[:errors_by_type]["NotFoundError"] += 1
40
-
41
- puts "❌ Model not found: #{e.message}"
42
- puts " Suggestions: #{e.suggestions.join(', ')}" if e.suggestions && !e.suggestions.empty?
43
- # Don't retry 404s
44
- return { success: false, error: e, error_type: "NotFoundError", attempts: attempt }
45
- rescue Ollama::HTTPError => e
46
- @stats[:failures] += 1
47
- @stats[:errors_by_type]["HTTPError"] ||= 0
48
- @stats[:errors_by_type]["HTTPError"] += 1
49
-
50
- puts "❌ HTTP Error (#{e.status_code}): #{e.message}"
51
- if e.retryable?
52
- @stats[:retries] += 1
53
- puts " → Retryable, will retry..."
54
- if attempt > max_attempts
55
- return { success: false, error: e, error_type: "HTTPError", retryable: true,
56
- attempts: attempt }
57
- end
58
-
59
- sleep(2**attempt) # Exponential backoff
60
- next
61
- else
62
- puts " → Non-retryable, aborting"
63
- return { success: false, error: e, error_type: "HTTPError", retryable: false, attempts: attempt }
64
- end
65
- rescue Ollama::TimeoutError => e
66
- @stats[:failures] += 1
67
- @stats[:errors_by_type]["TimeoutError"] ||= 0
68
- @stats[:errors_by_type]["TimeoutError"] += 1
69
-
70
- puts "⏱️ Timeout: #{e.message}"
71
- @stats[:retries] += 1
72
- return { success: false, error: e, error_type: "TimeoutError", attempts: attempt } unless attempt < max_attempts
73
-
74
- puts " → Retrying with exponential backoff..."
75
- sleep(2**attempt)
76
- next
77
- rescue Ollama::SchemaViolationError => e
78
- @stats[:failures] += 1
79
- @stats[:errors_by_type]["SchemaViolationError"] ||= 0
80
- @stats[:errors_by_type]["SchemaViolationError"] += 1
81
-
82
- puts "🔴 Schema violation: #{e.message}"
83
- # Schema violations are usually not worth retrying (model issue)
84
- # But we could try with a simpler schema as fallback
85
- unless attempt < max_attempts
86
- return { success: false, error: e, error_type: "SchemaViolationError", attempts: attempt }
87
- end
88
-
89
- puts " → Attempting with simplified schema..."
90
- simplified_schema = create_fallback_schema(schema)
91
- return execute_with_resilience(
92
- prompt: prompt,
93
- schema: simplified_schema,
94
- max_attempts: 1
95
- )
96
- rescue Ollama::InvalidJSONError => e
97
- @stats[:failures] += 1
98
- @stats[:errors_by_type]["InvalidJSONError"] ||= 0
99
- @stats[:errors_by_type]["InvalidJSONError"] += 1
100
-
101
- puts "📄 Invalid JSON: #{e.message}"
102
- @stats[:retries] += 1
103
- unless attempt < max_attempts
104
- return { success: false, error: e, error_type: "InvalidJSONError", attempts: attempt }
105
- end
106
-
107
- puts " → Retrying..."
108
- sleep(1)
109
- next
110
- rescue Ollama::RetryExhaustedError => e
111
- @stats[:failures] += 1
112
- @stats[:errors_by_type]["RetryExhaustedError"] ||= 0
113
- @stats[:errors_by_type]["RetryExhaustedError"] += 1
114
-
115
- puts "🔄 Retries exhausted: #{e.message}"
116
- return { success: false, error: e, error_type: "RetryExhaustedError", attempts: attempt }
117
- rescue Ollama::Error => e
118
- @stats[:failures] += 1
119
- @stats[:errors_by_type]["Error"] ||= 0
120
- @stats[:errors_by_type]["Error"] += 1
121
-
122
- puts "❌ General error: #{e.message}"
123
- return { success: false, error: e, error_type: "Error", attempts: attempt }
124
- rescue StandardError => e
125
- @stats[:failures] += 1
126
- @stats[:errors_by_type]["StandardError"] ||= 0
127
- @stats[:errors_by_type]["StandardError"] += 1
128
-
129
- puts "💥 Unexpected error: #{e.class}: #{e.message}"
130
- return { success: false, error: e, error_type: "StandardError", attempts: attempt }
131
- end
132
- end
133
- end
134
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
135
-
136
- def create_fallback_schema(_original_schema)
137
- # Create a minimal fallback schema
138
- {
139
- "type" => "object",
140
- "additionalProperties" => true
141
- }
142
- end
143
-
144
- def display_stats
145
- puts "\n" + "=" * 60
146
- puts "Execution Statistics"
147
- puts "=" * 60
148
- puts "Total calls: #{@stats[:total_calls]}"
149
- puts "Successes: #{@stats[:successes]}"
150
- puts "Failures: #{@stats[:failures]}"
151
- puts "Retries: #{@stats[:retries]}"
152
- success_rate = @stats[:total_calls].positive? ? (@stats[:successes].to_f / @stats[:total_calls] * 100).round(2) : 0
153
- puts "Success rate: #{success_rate}%"
154
- puts "\nErrors by type:"
155
- @stats[:errors_by_type].each do |type, count|
156
- puts " #{type}: #{count}"
157
- end
158
- end
159
- end
160
-
161
- # Test scenarios
162
- if __FILE__ == $PROGRAM_NAME
163
- client = Ollama::Client.new
164
- agent = ResilientAgent.new(client: client)
165
-
166
- schema = {
167
- "type" => "object",
168
- "required" => ["status", "message"],
169
- "properties" => {
170
- "status" => { "type" => "string" },
171
- "message" => { "type" => "string" }
172
- }
173
- }
174
-
175
- puts "=" * 60
176
- puts "Test 1: Normal execution"
177
- puts "=" * 60
178
- result1 = agent.execute_with_resilience(
179
- prompt: "Respond with status 'ok' and a greeting message",
180
- schema: schema
181
- )
182
- puts "Result: #{result1[:success] ? 'SUCCESS' : 'FAILED'}"
183
-
184
- puts "\n" + "=" * 60
185
- puts "Test 2: Invalid model (should trigger NotFoundError)"
186
- puts "=" * 60
187
- # Temporarily use invalid model
188
- invalid_client = Ollama::Client.new(
189
- config: Ollama::Config.new.tap { |c| c.model = "nonexistent-model:999" }
190
- )
191
- invalid_agent = ResilientAgent.new(client: invalid_client)
192
- result2 = invalid_agent.execute_with_resilience(
193
- prompt: "Test",
194
- schema: schema
195
- )
196
- puts "Result: #{result2[:success] ? 'SUCCESS' : 'FAILED'}"
197
-
198
- agent.display_stats
199
- end
200
-
@@ -1,341 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Advanced Example: Multi-Step Agent with Complex Decision Making
5
- # Demonstrates: Nested schemas, state management, error recovery, confidence thresholds
6
-
7
- require "json"
8
- require_relative "../lib/ollama_client"
9
-
10
- class MultiStepAgent
11
- def initialize(client:)
12
- @client = client
13
- @state = {
14
- steps_completed: [],
15
- data_collected: {},
16
- errors: []
17
- }
18
-
19
- # Complex nested schema for decision making
20
- @decision_schema = {
21
- "type" => "object",
22
- "required" => ["step", "action", "reasoning", "confidence", "next_steps"],
23
- "properties" => {
24
- "step" => {
25
- "type" => "integer",
26
- "description" => "Current step number in the workflow"
27
- },
28
- "action" => {
29
- "type" => "object",
30
- "required" => ["type", "parameters"],
31
- "properties" => {
32
- "type" => {
33
- "type" => "string",
34
- "enum" => ["collect", "analyze", "transform", "validate", "complete"]
35
- },
36
- "parameters" => {
37
- "type" => "object",
38
- "additionalProperties" => true
39
- }
40
- }
41
- },
42
- "reasoning" => {
43
- "type" => "string",
44
- "description" => "Why this action was chosen"
45
- },
46
- "confidence" => {
47
- "type" => "number",
48
- "minimum" => 0,
49
- "maximum" => 1,
50
- "description" => "Confidence level (0.0 to 1.0, where 1.0 is 100% confident)"
51
- },
52
- "next_steps" => {
53
- "type" => "array",
54
- "items" => {
55
- "type" => "string"
56
- },
57
- "minItems" => 0,
58
- "maxItems" => 5
59
- },
60
- "risk_assessment" => {
61
- "type" => "object",
62
- "properties" => {
63
- "level" => {
64
- "type" => "string",
65
- "enum" => ["low", "medium", "high"]
66
- },
67
- "factors" => {
68
- "type" => "array",
69
- "items" => { "type" => "string" }
70
- }
71
- }
72
- }
73
- }
74
- }
75
- end
76
-
77
- def execute_workflow(goal:)
78
- puts "🚀 Starting multi-step workflow"
79
- puts "Goal: #{goal}\n\n"
80
-
81
- max_steps = 10
82
- step_count = 0
83
-
84
- loop do
85
- step_count += 1
86
- break if step_count > max_steps
87
-
88
- puts "─" * 60
89
- puts "Step #{step_count}/#{max_steps}"
90
- puts "─" * 60
91
-
92
- context = build_context(goal: goal)
93
-
94
- begin
95
- decision = @client.generate(
96
- prompt: build_prompt(goal: goal, context: context),
97
- schema: @decision_schema
98
- )
99
-
100
- # Validate confidence threshold
101
- if decision["confidence"] < 0.5
102
- puts "⚠️ Low confidence (#{(decision["confidence"] * 100).round}%) - requesting manual review"
103
- break
104
- end
105
-
106
- display_decision(decision)
107
- result = execute_action(decision)
108
-
109
- # Update state
110
- @state[:steps_completed] << {
111
- step: decision["step"],
112
- action: decision["action"]["type"],
113
- result: result
114
- }
115
-
116
- # Check if workflow is complete
117
- if decision["action"]["type"] == "complete"
118
- puts "\n✅ Workflow completed successfully!"
119
- break
120
- end
121
-
122
- # Prevent infinite loops - if we've done the same action 3+ times, force progression
123
- recent_actions = @state[:steps_completed].last(3).map { |s| s[:action] }
124
- if recent_actions.length == 3 && recent_actions.uniq.length == 1
125
- puts "⚠️ Detected repetitive actions - forcing workflow progression"
126
- # Force next phase
127
- case recent_actions.first
128
- when "collect"
129
- puts " → Moving to analysis phase"
130
- decision["action"]["type"] = "analyze"
131
- decision["action"]["parameters"] = { "target" => "collected_data" }
132
- result = execute_action(decision)
133
- @state[:steps_completed] << {
134
- step: decision["step"],
135
- action: "analyze",
136
- result: result
137
- }
138
- when "analyze"
139
- puts " → Moving to validation phase"
140
- decision["action"]["type"] = "validate"
141
- decision["action"]["parameters"] = { "type" => "results" }
142
- result = execute_action(decision)
143
- @state[:steps_completed] << {
144
- step: decision["step"],
145
- action: "validate",
146
- result: result
147
- }
148
- when "validate"
149
- puts " → Completing workflow"
150
- decision["action"]["type"] = "complete"
151
- result = execute_action(decision)
152
- @state[:steps_completed] << {
153
- step: decision["step"],
154
- action: "complete",
155
- result: result
156
- }
157
- puts "\n✅ Workflow completed successfully!"
158
- break
159
- end
160
- end
161
-
162
- # Handle risk
163
- if decision["risk_assessment"] && decision["risk_assessment"]["level"] == "high"
164
- puts "⚠️ High risk detected - proceeding with caution"
165
- end
166
- rescue Ollama::SchemaViolationError => e
167
- puts "❌ Schema violation: #{e.message}"
168
- @state[:errors] << { step: step_count, error: "schema_violation", message: e.message }
169
- break
170
- rescue Ollama::RetryExhaustedError => e
171
- puts "❌ Retries exhausted: #{e.message}"
172
- @state[:errors] << { step: step_count, error: "retry_exhausted", message: e.message }
173
- break
174
- rescue Ollama::Error => e
175
- puts "❌ Error: #{e.message}"
176
- @state[:errors] << { step: step_count, error: "general", message: e.message }
177
- # Try to recover or break
178
- break if step_count > 3 # Don't loop forever
179
- end
180
-
181
- puts
182
- sleep 0.5 # Small delay for readability
183
- end
184
-
185
- display_summary
186
- @state
187
- end
188
-
189
- private
190
-
191
- def build_context(goal:) # rubocop:disable Lint/UnusedMethodArgument
192
- {
193
- steps_completed: @state[:steps_completed].map { |s| s[:action] },
194
- data_collected: @state[:data_collected].keys,
195
- error_count: @state[:errors].length,
196
- step_count: @state[:steps_completed].length
197
- }
198
- end
199
-
200
- def build_prompt(goal:, context:)
201
- step_count = context[:step_count]
202
- completed_actions = context[:steps_completed]
203
- collected_data = context[:data_collected]
204
-
205
- # Determine current phase based on what's been done
206
- phase = if completed_actions.empty?
207
- "collection"
208
- elsif completed_actions.include?("collect") && !completed_actions.include?("analyze")
209
- "analysis"
210
- elsif completed_actions.include?("analyze") && !completed_actions.include?("validate")
211
- "validation"
212
- else
213
- "completion"
214
- end
215
-
216
- <<~PROMPT
217
- Goal: #{goal}
218
-
219
- Current Phase: #{phase}
220
- Steps completed: #{step_count}
221
- Actions taken: #{completed_actions.join(", ") || "none"}
222
- Data collected: #{collected_data.join(", ") || "none"}
223
- Errors encountered: #{context[:error_count]}
224
-
225
- Workflow Phases (in order):
226
- 1. COLLECTION: Collect initial data (user data, patterns, etc.)
227
- 2. ANALYSIS: Analyze collected data for patterns and insights
228
- 3. VALIDATION: Validate the analysis results
229
- 4. COMPLETION: Finish the workflow
230
-
231
- Current State Analysis:
232
- - You are in the #{phase} phase
233
- - You have completed #{step_count} steps
234
- - You have collected: #{collected_data.any? ? collected_data.join(", ") : "nothing yet"}
235
-
236
- Decision Guidelines:
237
- - If in COLLECTION phase and no data collected: use action "collect" with specific data_type (e.g., "user_data", "patterns")
238
- - If data collected but not analyzed: use action "analyze" with target
239
- - If analyzed but not validated: use action "validate"
240
- - If all phases done: use action "complete"
241
- - AVOID repeating the same action multiple times unless necessary
242
- - Progress through phases: collect → analyze → validate → complete
243
-
244
- Provide a structured decision with high confidence (>0.7) if possible.
245
- Set step number to #{step_count + 1}.
246
- PROMPT
247
- end
248
-
249
- def display_decision(decision)
250
- puts "\n📋 Decision:"
251
- puts " Step: #{decision['step']}"
252
- puts " Action: #{decision['action']['type']}"
253
- puts " Reasoning: #{decision['reasoning']}"
254
- puts " Confidence: #{(decision['confidence'] * 100).round}%"
255
- if decision["risk_assessment"]
256
- puts " Risk Level: #{decision['risk_assessment']['level']}"
257
- if decision["risk_assessment"]["factors"]
258
- puts " Risk Factors: #{decision['risk_assessment']['factors'].join(', ')}"
259
- end
260
- end
261
- return unless decision["next_steps"] && !decision["next_steps"].empty?
262
-
263
- puts " Next Steps: #{decision['next_steps'].join(' → ')}"
264
- end
265
-
266
- def execute_action(decision)
267
- action_type = decision["action"]["type"]
268
- params = decision["action"]["parameters"] || {}
269
-
270
- case action_type
271
- when "collect"
272
- data_key = params["data_type"] || params["key"] || "user_data"
273
- # Prevent collecting the same generic data repeatedly
274
- data_key = "user_data" if should_collect_user_data?(data_key)
275
- puts " 📥 Collecting: #{data_key}"
276
- @state[:data_collected][data_key] = "collected_at_#{Time.now.to_i}"
277
- { status: "collected", key: data_key }
278
-
279
- when "analyze"
280
- target = params["target"] || "collected_data"
281
- puts " 🔍 Analyzing: #{target}"
282
- # Mark that analysis has been done
283
- @state[:data_collected]["analysis_complete"] = true
284
- { status: "analyzed", target: target, insights: "Patterns identified in collected data" }
285
-
286
- when "transform"
287
- transformation = params["type"] || "default"
288
- puts " 🔄 Transforming: #{transformation}"
289
- { status: "transformed", type: transformation }
290
-
291
- when "validate"
292
- validation_type = params["type"] || "results"
293
- puts " ✓ Validating: #{validation_type}"
294
- # Mark that validation has been done
295
- @state[:data_collected]["validation_complete"] = true
296
- { status: "validated", type: validation_type, result: "All checks passed" }
297
-
298
- when "complete"
299
- puts " ✅ Completing workflow"
300
- { status: "complete" }
301
-
302
- else
303
- { status: "unknown_action" }
304
- end
305
- end
306
-
307
- def should_collect_user_data?(data_key)
308
- @state[:data_collected].key?(data_key) &&
309
- data_key.match?(/^(missing|unknown|data)$/i) &&
310
- !@state[:data_collected].key?("user_data")
311
- end
312
-
313
- def display_summary
314
- puts "\n" + "=" * 60
315
- puts "Workflow Summary"
316
- puts "=" * 60
317
- puts "Steps completed: #{@state[:steps_completed].length}"
318
- puts "Data collected: #{@state[:data_collected].keys.join(', ') || 'none'}"
319
- puts "Errors: #{@state[:errors].length}"
320
- return unless @state[:errors].any?
321
-
322
- puts "\nErrors:"
323
- @state[:errors].each do |error|
324
- puts " Step #{error[:step]}: #{error[:error]} - #{error[:message]}"
325
- end
326
- end
327
- end
328
-
329
- # Run example
330
- if __FILE__ == $PROGRAM_NAME
331
- # Use longer timeout for multi-step workflows
332
- config = Ollama::Config.new
333
- config.timeout = 60 # 60 seconds for complex operations
334
- client = Ollama::Client.new(config: config)
335
-
336
- agent = MultiStepAgent.new(client: client)
337
- agent.execute_workflow(
338
- goal: "Collect user data, analyze patterns, validate results, and generate report"
339
- )
340
- end
341
-