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
@@ -1,420 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Technical Analysis Runner - Run only technical analysis examples
5
- # Usage: ruby examples/dhanhq/technical_analysis_runner.rb
6
-
7
- require "json"
8
- require "date"
9
- require "dhan_hq"
10
- require_relative "../../lib/ollama_client"
11
- require_relative "../dhanhq_tools"
12
-
13
- # Load all modules
14
- require_relative "utils/instrument_helper"
15
- require_relative "utils/rate_limiter"
16
- require_relative "utils/parameter_normalizer"
17
- require_relative "utils/parameter_cleaner"
18
- require_relative "utils/trading_parameter_normalizer"
19
- require_relative "builders/market_context_builder"
20
- require_relative "schemas/agent_schemas"
21
- require_relative "services/base_service"
22
- require_relative "services/data_service"
23
- require_relative "services/trading_service"
24
- require_relative "indicators/technical_indicators"
25
- require_relative "analysis/market_structure"
26
- require_relative "analysis/pattern_recognizer"
27
- require_relative "analysis/trend_analyzer"
28
- require_relative "agents/base_agent"
29
- require_relative "agents/data_agent"
30
- require_relative "agents/trading_agent"
31
- require_relative "agents/technical_analysis_agent"
32
- require_relative "agents/orchestrator_agent"
33
- require_relative "scanners/swing_scanner"
34
- require_relative "scanners/intraday_options_scanner"
35
-
36
- # Load the Agent class definition
37
- module DhanHQ
38
- # Main agent orchestrator
39
- class Agent
40
- def initialize(ollama_client: nil, trading_ollama_client: nil)
41
- @ollama_client = ollama_client || Ollama::Client.new
42
- @trading_ollama_client = trading_ollama_client || create_trading_client
43
- @data_agent = Agents::DataAgent.new(ollama_client: @ollama_client)
44
- @trading_agent = Agents::TradingAgent.new(ollama_client: @trading_ollama_client)
45
- @analysis_agent = Agents::TechnicalAnalysisAgent.new(ollama_client: @ollama_client)
46
- @orchestrator_agent = Agents::OrchestratorAgent.new(ollama_client: @ollama_client)
47
- @swing_scanner = Scanners::SwingScanner.new
48
- @options_scanner = Scanners::IntradayOptionsScanner.new
49
- end
50
-
51
- attr_reader :data_agent, :trading_agent, :analysis_agent, :orchestrator_agent, :swing_scanner, :options_scanner
52
-
53
- private
54
-
55
- def create_trading_client
56
- config = Ollama::Config.new
57
- config.timeout = 60
58
- Ollama::Client.new(config: config)
59
- end
60
- end
61
- end
62
-
63
- # Configure DhanHQ
64
- begin
65
- DhanHQ.configure_with_env
66
- puts "✅ DhanHQ configured"
67
- rescue StandardError => e
68
- puts "⚠️ DhanHQ configuration error: #{e.message}"
69
- puts " Make sure CLIENT_ID and ACCESS_TOKEN are set in ENV"
70
- puts " Continuing with mock data for demonstration..."
71
- end
72
-
73
- puts "=" * 60
74
- puts "TECHNICAL ANALYSIS - LLM-POWERED ORCHESTRATION"
75
- puts "=" * 60
76
- puts
77
-
78
- # Helper methods (defined before use)
79
- def perform_technical_analysis(agent, symbol, exchange_segment)
80
- analysis_result = agent.analysis_agent.analyze_symbol(
81
- symbol: symbol,
82
- exchange_segment: exchange_segment
83
- )
84
-
85
- return log_analysis_error(analysis_result) if analysis_result[:error]
86
-
87
- analysis = analysis_result[:analysis]
88
- return log_analysis_empty if analysis.nil? || analysis.empty?
89
-
90
- log_analysis_summary(analysis)
91
- rescue StandardError => e
92
- puts " ❌ Error: #{e.message}"
93
- end
94
-
95
- def perform_swing_scan(agent, symbol, exchange_segment)
96
- candidates = agent.swing_scanner.scan_symbols(
97
- [symbol],
98
- exchange_segment: exchange_segment,
99
- min_score: 40,
100
- verbose: false
101
- )
102
-
103
- if candidates.empty?
104
- puts " ⚠️ No swing candidates found (score < 40)"
105
- else
106
- candidate = candidates.first
107
- puts " ✅ Swing Candidate Found"
108
- puts " 📈 Score: #{candidate[:score]}/100"
109
- puts " 📊 Trend: #{candidate[:analysis][:trend][:trend]}"
110
- puts " 💡 #{candidate[:recommendation] || 'Analysis complete'}"
111
- end
112
- rescue StandardError => e
113
- puts " ❌ Error: #{e.message}"
114
- end
115
-
116
- def perform_options_scan(agent, symbol, exchange_segment)
117
- options_setups = agent.options_scanner.scan_for_options_setups(
118
- symbol,
119
- exchange_segment: exchange_segment,
120
- min_score: 40,
121
- verbose: true
122
- )
123
-
124
- if options_setups[:error]
125
- puts " ⚠️ #{options_setups[:error]}"
126
- elsif options_setups[:setups] && !options_setups[:setups].empty?
127
- puts " ✅ Found #{options_setups[:setups].length} options setups"
128
- options_setups[:setups].first(3).each do |setup|
129
- puts " 📊 #{setup[:type].to_s.upcase} @ #{setup[:strike]}: Score #{setup[:score]}/100"
130
- end
131
- else
132
- puts " ⚠️ No options setups found (min_score: 40)"
133
- puts " Try lowering min_score or check verbose output above for details"
134
- end
135
- rescue StandardError => e
136
- puts " ❌ Error: #{e.message}"
137
- puts " #{e.backtrace.first(3).join("\n ")}" if e.backtrace
138
- end
139
-
140
- # Initialize agent
141
- agent = DhanHQ::Agent.new
142
-
143
- # ============================================================
144
- # LLM-POWERED ORCHESTRATION
145
- # ============================================================
146
- puts "🤔 Letting LLM decide what to analyze..."
147
- puts "─" * 60
148
-
149
- # Get market context first (can be fetched from real data)
150
- market_context = ARGV[1] || "Current market: SENSEX, NIFTY and RELIANCE are active. Looking for trading opportunities."
151
-
152
- user_query = ARGV[0] || "Analyze SENSEX and find swing trading opportunities"
153
-
154
- puts "User Query: #{user_query}"
155
- puts "Market Context: #{market_context}"
156
- puts
157
-
158
- begin
159
- plan = agent.orchestrator_agent.decide_analysis_plan(
160
- market_context: market_context,
161
- user_query: user_query
162
- )
163
-
164
- if plan[:error]
165
- puts " ⚠️ Error creating plan: #{plan[:error]}"
166
- puts " Falling back to default analysis..."
167
- plan = {
168
- "analysis_plan" => [
169
- { "symbol" => "RELIANCE", "exchange_segment" => "NSE_EQ", "analysis_type" => "technical_analysis",
170
- "priority" => 1 },
171
- { "symbol" => "TCS", "exchange_segment" => "NSE_EQ", "analysis_type" => "swing_scan", "priority" => 2 },
172
- { "symbol" => "NIFTY", "exchange_segment" => "IDX_I", "analysis_type" => "options_scan", "priority" => 3 }
173
- ],
174
- "reasoning" => "Default fallback plan"
175
- }
176
- end
177
-
178
- puts "📋 Analysis Plan:"
179
- puts " Reasoning: #{plan['reasoning'] || 'N/A'}"
180
- puts " Tasks: #{plan['analysis_plan']&.length || 0} analysis tasks"
181
- puts
182
-
183
- # Sort by priority
184
- tasks = (plan["analysis_plan"] || []).sort_by { |t| t["priority"] || 999 }
185
-
186
- tasks.each_with_index do |task, idx|
187
- symbol = task["symbol"]
188
- exchange_segment = task["exchange_segment"]
189
- analysis_type = task["analysis_type"]
190
-
191
- puts "=" * 60
192
- puts "Task #{idx + 1}: #{analysis_type} for #{symbol} (#{exchange_segment})"
193
- puts "─" * 60
194
-
195
- if analysis_type == "all"
196
- perform_technical_analysis(agent, symbol, exchange_segment)
197
- perform_swing_scan(agent, symbol, exchange_segment)
198
- perform_options_scan(agent, symbol, exchange_segment)
199
- else
200
- case analysis_type
201
- when "technical_analysis"
202
- perform_technical_analysis(agent, symbol, exchange_segment)
203
- when "swing_scan"
204
- perform_swing_scan(agent, symbol, exchange_segment)
205
- when "options_scan"
206
- perform_options_scan(agent, symbol, exchange_segment)
207
- end
208
- end
209
-
210
- puts
211
- end
212
- rescue StandardError => e
213
- puts " ❌ Error: #{e.message}"
214
- puts " #{e.backtrace.first(3).join("\n ")}" if e.backtrace
215
- end
216
-
217
- def log_analysis_error(analysis_result)
218
- puts " ⚠️ Error: #{analysis_result[:error]}"
219
- end
220
-
221
- def log_analysis_empty
222
- puts " ⚠️ Error: Analysis returned empty result"
223
- end
224
-
225
- def log_analysis_summary(analysis)
226
- puts " ✅ Analysis Complete"
227
- puts trend_summary_text(analysis)
228
- puts rsi_summary_text(analysis)
229
- puts macd_summary_text(analysis)
230
- puts current_price_text(analysis)
231
- puts patterns_summary_text(analysis)
232
- puts structure_break_text(analysis)
233
- end
234
-
235
- def trend_summary_text(analysis)
236
- trend = analysis[:trend] || {}
237
- trend_name = trend[:trend] || "N/A"
238
- strength = trend[:strength] || 0
239
- " 📊 Trend: #{trend_name} (#{strength}% strength)"
240
- end
241
-
242
- def rsi_summary_text(analysis)
243
- rsi = analysis[:indicators]&.dig(:rsi)
244
- rsi_text = rsi ? rsi.round(2) : "N/A"
245
- " 📊 RSI: #{rsi_text}"
246
- end
247
-
248
- def macd_summary_text(analysis)
249
- macd = analysis[:indicators]&.dig(:macd)
250
- macd_text = macd ? macd.round(2) : "N/A"
251
- " 📊 MACD: #{macd_text}"
252
- end
253
-
254
- def current_price_text(analysis)
255
- current_price = analysis[:current_price] || "N/A"
256
- " 📊 Current Price: #{current_price}"
257
- end
258
-
259
- def patterns_summary_text(analysis)
260
- candlestick_patterns = analysis[:patterns]&.dig(:candlestick) || []
261
- " 📊 Patterns: #{candlestick_patterns.length} patterns"
262
- end
263
-
264
- def structure_break_text(analysis)
265
- broken = analysis[:structure_break]&.dig(:broken)
266
- " 📊 Structure Break: #{broken ? 'Yes' : 'No'}"
267
- end
268
-
269
- def format_score_breakdown(details)
270
- "Trend=#{details[:trend]}, RSI=#{details[:rsi]}, MACD=#{details[:macd]}, " \
271
- "Structure=#{details[:structure]}, Patterns=#{details[:patterns]}"
272
- end
273
-
274
- def format_option_setup_details(setup)
275
- iv = setup[:iv]&.round(2) || "N/A"
276
- oi = setup[:oi] || "N/A"
277
- volume = setup[:volume] || "N/A"
278
- "IV: #{iv}% | OI: #{oi} | Volume: #{volume}"
279
- end
280
-
281
- # Uncomment below to also run manual examples for comparison
282
- if ENV["SHOW_MANUAL_EXAMPLES"] == "true"
283
- puts "=" * 60
284
- puts "TECHNICAL ANALYSIS EXAMPLES (Manual - for comparison)"
285
- puts "=" * 60
286
- puts
287
-
288
- # ============================================================
289
- # MANUAL EXAMPLES (for comparison)
290
- # ============================================================
291
- puts "Example 1: Technical Analysis for RELIANCE"
292
- puts "─" * 60
293
-
294
- begin
295
- analysis_result = agent.analysis_agent.analyze_symbol(
296
- symbol: "RELIANCE",
297
- exchange_segment: "NSE_EQ"
298
- )
299
-
300
- if analysis_result[:error]
301
- puts " ⚠️ Error: #{analysis_result[:error]}"
302
- elsif analysis_result[:analysis].nil? || analysis_result[:analysis].empty?
303
- puts " ⚠️ Error: Analysis returned empty result"
304
- else
305
- analysis = analysis_result[:analysis]
306
- puts " ✅ Analysis Complete"
307
- puts " 📊 Trend: #{analysis[:trend]&.dig(:trend) || 'N/A'} (#{analysis[:trend]&.dig(:strength) || 0}% strength)"
308
- puts " 📊 RSI: #{analysis[:indicators]&.dig(:rsi)&.round(2) || 'N/A'}"
309
- puts " 📊 MACD: #{analysis[:indicators]&.dig(:macd)&.round(2) || 'N/A'}"
310
- puts " 📊 Current Price: #{analysis[:current_price] || 'N/A'}"
311
- puts " 📊 Patterns Detected: #{analysis[:patterns]&.dig(:candlestick)&.length || 0} candlestick patterns"
312
- puts " 📊 Structure Break: #{analysis[:structure_break]&.dig(:broken) ? 'Yes' : 'No'}"
313
-
314
- # Generate swing trading recommendation
315
- begin
316
- recommendation = agent.analysis_agent.generate_recommendation(
317
- analysis_result,
318
- trading_style: :swing
319
- )
320
-
321
- if recommendation && !recommendation[:error] && recommendation.is_a?(Hash)
322
- puts "\n 💡 Swing Trading Recommendation:"
323
- puts " Action: #{recommendation['recommendation']&.upcase || 'N/A'}"
324
- puts " Entry: #{recommendation['entry_price'] || 'N/A'}"
325
- puts " Stop Loss: #{recommendation['stop_loss'] || 'N/A'}"
326
- puts " Target: #{recommendation['target_price'] || 'N/A'}"
327
- puts " Risk/Reward: #{recommendation['risk_reward_ratio']&.round(2) || 'N/A'}"
328
- puts " Confidence: #{(recommendation['confidence'] * 100).round}%" if recommendation["confidence"]
329
- end
330
- rescue StandardError => e
331
- puts " ⚠️ Could not generate recommendation: #{e.message}"
332
- end
333
- end
334
- rescue StandardError => e
335
- puts " ❌ Error: #{e.message}"
336
- puts " #{e.backtrace.first(3).join("\n ")}" if e.backtrace
337
- end
338
-
339
- puts
340
- puts "Example 2: Swing Trading Scanner"
341
- puts "─" * 60
342
-
343
- begin
344
- # Scan a few symbols for swing opportunities
345
- symbols_to_scan = ["RELIANCE", "TCS", "INFY"]
346
- puts " 🔍 Scanning #{symbols_to_scan.length} symbols for swing opportunities..."
347
-
348
- candidates = agent.swing_scanner.scan_symbols(
349
- symbols_to_scan,
350
- exchange_segment: "NSE_EQ",
351
- min_score: 40,
352
- verbose: true
353
- )
354
-
355
- if candidates.empty?
356
- puts " ⚠️ No swing candidates found above minimum score (40/100)"
357
- puts " Try lowering min_score or check rejected candidates above"
358
- else
359
- puts " ✅ Found #{candidates.length} swing candidates:"
360
- candidates.each do |candidate|
361
- puts " 📈 #{candidate[:symbol]}: Score #{candidate[:score]}/100"
362
- if candidate[:score_details]
363
- details = candidate[:score_details]
364
- puts " Breakdown: #{format_score_breakdown(details)}"
365
- end
366
- trend = candidate[:analysis][:trend]
367
- puts " Trend: #{trend[:trend]} (#{trend[:strength]}% strength)"
368
- puts " #{candidate[:interpretation]}"
369
- end
370
- end
371
- rescue StandardError => e
372
- puts " ❌ Error: #{e.message}"
373
- puts " #{e.backtrace.first(3).join("\n ")}" if e.backtrace
374
- end
375
-
376
- puts
377
- puts "Example 3: Intraday Options Scanner"
378
- puts "─" * 60
379
-
380
- begin
381
- puts " 🔍 Scanning NIFTY for intraday options opportunities..."
382
-
383
- options_setups = agent.options_scanner.scan_for_options_setups(
384
- "NIFTY",
385
- exchange_segment: "IDX_I",
386
- min_score: 40,
387
- verbose: true
388
- )
389
-
390
- if options_setups[:error]
391
- puts " ⚠️ #{options_setups[:error]}"
392
- elsif options_setups[:setups] && !options_setups[:setups].empty?
393
- puts " ✅ Found #{options_setups[:setups].length} options setups:"
394
- options_setups[:setups].each do |setup|
395
- puts " 📊 #{setup[:type].to_s.upcase} @ #{setup[:strike]}"
396
- puts " #{format_option_setup_details(setup)}"
397
- puts " Score: #{setup[:score]}/100 | Recommendation: #{setup[:recommendation]}"
398
- end
399
- else
400
- puts " ⚠️ No options setups found above minimum score (40/100)"
401
- puts " Check rejected setups above or try lowering min_score"
402
- end
403
- rescue StandardError => e
404
- puts " ❌ Error: #{e.message}"
405
- puts " #{e.backtrace.first(3).join("\n ")}" if e.backtrace
406
- end
407
-
408
- end
409
-
410
- puts
411
- puts "=" * 60
412
- puts "Technical Analysis Summary:"
413
- puts " ✅ Trend Analysis: Uptrend/Downtrend/Sideways detection"
414
- puts " ✅ Technical Indicators: RSI, MACD, Moving Averages, etc."
415
- puts " ✅ Pattern Recognition: Candlestick & Chart patterns"
416
- puts " ✅ Market Structure: SMC concepts, Order blocks, Liquidity zones"
417
- puts " ✅ Swing Trading Scanner: Find swing opportunities"
418
- puts " ✅ Intraday Options Scanner: Find options buying setups"
419
- puts " ✅ LLM Orchestration: AI decides what to analyze"
420
- puts "=" * 60