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,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