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,411 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # True Agentic Technical Analysis Runner
5
- # Uses Ollama::Agent::Executor for dynamic tool calling
6
- # LLM decides which tools to call and when, based on results
7
-
8
- require "json"
9
- require "date"
10
- require "dhan_hq"
11
- require_relative "../../lib/ollama_client"
12
- require_relative "../dhanhq_tools"
13
-
14
- # Load all modules
15
- require_relative "utils/instrument_helper"
16
- require_relative "utils/rate_limiter"
17
- require_relative "utils/parameter_normalizer"
18
- require_relative "utils/parameter_cleaner"
19
- require_relative "utils/trading_parameter_normalizer"
20
- require_relative "builders/market_context_builder"
21
- require_relative "schemas/agent_schemas"
22
- require_relative "services/base_service"
23
- require_relative "services/data_service"
24
- require_relative "services/trading_service"
25
- require_relative "indicators/technical_indicators"
26
- require_relative "analysis/market_structure"
27
- require_relative "analysis/pattern_recognizer"
28
- require_relative "analysis/trend_analyzer"
29
- require_relative "agents/base_agent"
30
- require_relative "agents/data_agent"
31
- require_relative "agents/trading_agent"
32
- require_relative "agents/technical_analysis_agent"
33
- require_relative "agents/orchestrator_agent"
34
- require_relative "scanners/swing_scanner"
35
- require_relative "scanners/intraday_options_scanner"
36
-
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
- exit 1
71
- end
72
-
73
- puts "=" * 60
74
- puts "TECHNICAL ANALYSIS - TRUE AGENTIC TOOL CALLING"
75
- puts "=" * 60
76
- puts
77
-
78
- # Initialize agent
79
- agent = DhanHQ::Agent.new
80
-
81
- # Define tools using structured Tool classes for better type safety and LLM understanding
82
- # This provides explicit schemas with descriptions, types, and enums
83
-
84
- # Swing Scan Tool - Structured definition
85
- swing_scan_tool = Ollama::Tool.new(
86
- type: "function",
87
- function: Ollama::Tool::Function.new(
88
- name: "swing_scan",
89
- description: "Scans for swing trading opportunities in EQUITY STOCKS only. " \
90
- "Use for stocks like RELIANCE, TCS, INFY, HDFC. " \
91
- "Do NOT use for indices (NIFTY, SENSEX, BANKNIFTY).",
92
- parameters: Ollama::Tool::Function::Parameters.new(
93
- type: "object",
94
- properties: {
95
- symbol: Ollama::Tool::Function::Parameters::Property.new(
96
- type: "string",
97
- description: "Stock symbol to scan (e.g., RELIANCE, TCS, INFY). Must be a stock, not an index."
98
- ),
99
- exchange_segment: Ollama::Tool::Function::Parameters::Property.new(
100
- type: "string",
101
- description: "Exchange segment (default: NSE_EQ)",
102
- enum: %w[NSE_EQ BSE_EQ]
103
- ),
104
- min_score: Ollama::Tool::Function::Parameters::Property.new(
105
- type: "integer",
106
- description: "Minimum score threshold (default: 40)"
107
- ),
108
- verbose: Ollama::Tool::Function::Parameters::Property.new(
109
- type: "boolean",
110
- description: "Verbose output (default: false)"
111
- )
112
- },
113
- required: %w[symbol]
114
- )
115
- )
116
- )
117
-
118
- # Options Scan Tool - Structured definition
119
- options_scan_tool = Ollama::Tool.new(
120
- type: "function",
121
- function: Ollama::Tool::Function.new(
122
- name: "options_scan",
123
- description: "Scans for intraday options buying opportunities in INDICES only. " \
124
- "Use for NIFTY, SENSEX, BANKNIFTY. Do NOT use for stocks.",
125
- parameters: Ollama::Tool::Function::Parameters.new(
126
- type: "object",
127
- properties: {
128
- symbol: Ollama::Tool::Function::Parameters::Property.new(
129
- type: "string",
130
- description: "Index symbol (NIFTY, SENSEX, or BANKNIFTY). Must be an index, not a stock.",
131
- enum: %w[NIFTY SENSEX BANKNIFTY]
132
- ),
133
- exchange_segment: Ollama::Tool::Function::Parameters::Property.new(
134
- type: "string",
135
- description: "Exchange segment (default: IDX_I)",
136
- enum: %w[IDX_I]
137
- ),
138
- min_score: Ollama::Tool::Function::Parameters::Property.new(
139
- type: "integer",
140
- description: "Minimum score threshold (default: 40)"
141
- ),
142
- verbose: Ollama::Tool::Function::Parameters::Property.new(
143
- type: "boolean",
144
- description: "Verbose output (default: false)"
145
- )
146
- },
147
- required: %w[symbol]
148
- )
149
- )
150
- )
151
-
152
- # Technical Analysis Tool - Structured definition
153
- technical_analysis_tool = Ollama::Tool.new(
154
- type: "function",
155
- function: Ollama::Tool::Function.new(
156
- name: "technical_analysis",
157
- description: "Performs full technical analysis including trend, indicators, and patterns. " \
158
- "Can be used for both stocks and indices.",
159
- parameters: Ollama::Tool::Function::Parameters.new(
160
- type: "object",
161
- properties: {
162
- symbol: Ollama::Tool::Function::Parameters::Property.new(
163
- type: "string",
164
- description: "Symbol to analyze (stock or index)"
165
- ),
166
- exchange_segment: Ollama::Tool::Function::Parameters::Property.new(
167
- type: "string",
168
- description: "Exchange segment (default: NSE_EQ)",
169
- enum: %w[NSE_EQ BSE_EQ NSE_FNO BSE_FNO IDX_I]
170
- )
171
- },
172
- required: %w[symbol]
173
- )
174
- )
175
- )
176
-
177
- # Define tools with structured Tool classes and callables
178
- tools = {
179
- "swing_scan" => {
180
- tool: swing_scan_tool,
181
- callable: lambda do |symbol:, exchange_segment: "NSE_EQ", min_score: 40, verbose: false|
182
- # CRITICAL: Only allow equity stocks, not indices
183
- if %w[NIFTY SENSEX BANKNIFTY].include?(symbol.to_s.upcase)
184
- return { error: "#{symbol} is an index, not a stock. Use options_scan for indices." }
185
- end
186
-
187
- begin
188
- candidates = agent.swing_scanner.scan_symbols(
189
- [symbol.to_s],
190
- exchange_segment: exchange_segment.to_s,
191
- min_score: min_score.to_i,
192
- verbose: verbose
193
- )
194
-
195
- {
196
- symbol: symbol,
197
- exchange_segment: exchange_segment,
198
- candidates_found: candidates.length,
199
- candidates: candidates.map do |c|
200
- {
201
- symbol: c[:symbol],
202
- score: c[:score],
203
- trend: c[:analysis][:trend][:trend],
204
- recommendation: c[:recommendation]
205
- }
206
- end
207
- }
208
- rescue StandardError => e
209
- { error: e.message, backtrace: e.backtrace.first(3) }
210
- end
211
- end
212
- },
213
-
214
- "options_scan" => {
215
- tool: options_scan_tool,
216
- callable: lambda do |symbol:, exchange_segment: "IDX_I", min_score: 40, verbose: false|
217
- # CRITICAL: Only allow indices, not stocks
218
- unless %w[NIFTY SENSEX BANKNIFTY].include?(symbol.to_s.upcase)
219
- error_message = "#{symbol} is not an index. Use swing_scan for stocks. " \
220
- "Options are only available for indices (NIFTY, SENSEX, BANKNIFTY)."
221
- return { error: error_message }
222
- end
223
-
224
- begin
225
- options_setups = agent.options_scanner.scan_for_options_setups(
226
- symbol.to_s,
227
- exchange_segment: exchange_segment.to_s,
228
- min_score: min_score.to_i,
229
- verbose: verbose
230
- )
231
-
232
- if options_setups[:error]
233
- { error: options_setups[:error] }
234
- else
235
- underlying_price = options_setups.dig(:underlying_analysis, :current_price)
236
- {
237
- symbol: symbol,
238
- exchange_segment: exchange_segment,
239
- underlying_price: underlying_price,
240
- setups_found: options_setups[:setups]&.length || 0,
241
- setups: options_setups[:setups]&.map do |s|
242
- {
243
- type: s[:type],
244
- strike: s[:strike],
245
- score: s[:score],
246
- iv: s[:iv],
247
- ltp: s[:ltp],
248
- recommendation: s[:recommendation]
249
- }
250
- end || []
251
- }
252
- end
253
- rescue StandardError => e
254
- { error: e.message, backtrace: e.backtrace.first(3) }
255
- end
256
- end
257
- },
258
-
259
- "technical_analysis" => {
260
- tool: technical_analysis_tool,
261
- callable: lambda do |symbol:, exchange_segment: "NSE_EQ"|
262
- analysis_result = agent.analysis_agent.analyze_symbol(
263
- symbol: symbol.to_s,
264
- exchange_segment: exchange_segment.to_s
265
- )
266
-
267
- if analysis_result[:error]
268
- { error: analysis_result[:error] }
269
- else
270
- analysis = analysis_result[:analysis]
271
- {
272
- symbol: symbol,
273
- exchange_segment: exchange_segment,
274
- trend: analysis[:trend]&.dig(:trend),
275
- trend_strength: analysis[:trend]&.dig(:strength),
276
- rsi: analysis[:indicators]&.dig(:rsi)&.round(2),
277
- macd: analysis[:indicators]&.dig(:macd)&.round(2),
278
- current_price: analysis[:current_price],
279
- patterns_count: analysis[:patterns]&.dig(:candlestick)&.length || 0,
280
- structure_break: analysis[:structure_break]&.dig(:broken) || false
281
- }
282
- end
283
- rescue StandardError => e
284
- { error: e.message, backtrace: e.backtrace.first(3) }
285
- end
286
- }
287
- }
288
-
289
- # Get user query and market context
290
- user_query = ARGV[0] || "Analyze SENSEX and find swing trading opportunities"
291
- market_context = ARGV[1] || "Current market: SENSEX, NIFTY and RELIANCE are active. Looking for trading opportunities."
292
-
293
- puts "🤔 Agentic Analysis - LLM will decide which tools to call dynamically"
294
- puts "─" * 60
295
- puts "User Query: #{user_query}"
296
- puts "Market Context: #{market_context}"
297
- puts
298
-
299
- # Build system prompt
300
- system_prompt = <<~PROMPT
301
- You are a trading analysis agent. Your job is to help users analyze markets and find trading opportunities.
302
-
303
- Available Tools:
304
- 1. swing_scan(symbol, exchange_segment="NSE_EQ", min_score=40, verbose=false)
305
- - Scans for swing trading opportunities in EQUITY STOCKS only
306
- - Use for: RELIANCE, TCS, INFY, HDFC, etc. (stocks, not indices)
307
- - Returns: List of swing candidates with scores
308
- - CRITICAL: Do NOT use for indices (NIFTY, SENSEX, BANKNIFTY)
309
-
310
- 2. options_scan(symbol, exchange_segment="IDX_I", min_score=40, verbose=false)
311
- - Scans for intraday options buying opportunities in INDICES only
312
- - Use for: NIFTY, SENSEX, BANKNIFTY (indices, not stocks)
313
- - Returns: List of options setups (calls/puts) with scores
314
- - CRITICAL: Do NOT use for stocks (RELIANCE, TCS, etc.)
315
-
316
- 3. technical_analysis(symbol, exchange_segment="NSE_EQ")
317
- - Performs full technical analysis (trend, indicators, patterns)
318
- - Can be used for both stocks and indices
319
- - Returns: Complete technical analysis results
320
-
321
- Rules:
322
- - For swing trading: Use swing_scan ONLY on equity stocks (NSE_EQ, BSE_EQ)
323
- - For options: Use options_scan ONLY on indices (IDX_I) like NIFTY, SENSEX, BANKNIFTY
324
- - You can call multiple tools in sequence
325
- - Use tool results to decide what to do next
326
- - If a tool returns an error, try a different approach or inform the user
327
-
328
- Market Context: #{market_context}
329
- PROMPT
330
-
331
- user_prompt = user_query
332
-
333
- # Create executor with tools
334
- executor = Ollama::Agent::Executor.new(
335
- Ollama::Client.new,
336
- tools: tools,
337
- max_steps: 20
338
- )
339
-
340
- def tool_messages(messages)
341
- messages.select { |message| message[:role] == "tool" }
342
- end
343
-
344
- def print_tool_results(messages)
345
- puts "Tool Results:"
346
- tool_messages(messages).each do |message|
347
- print_tool_message(message)
348
- end
349
- end
350
-
351
- def print_tool_message(message)
352
- tool_name = message[:name] || "unknown_tool"
353
- puts "- #{tool_name}"
354
- puts format_tool_content(message[:content])
355
- end
356
-
357
- def format_tool_content(content)
358
- parsed = parse_tool_content(content)
359
- return parsed if parsed.is_a?(String)
360
-
361
- JSON.pretty_generate(parsed)
362
- end
363
-
364
- def parse_tool_content(content)
365
- return content unless content.is_a?(String)
366
-
367
- JSON.parse(content)
368
- rescue JSON::ParserError
369
- content
370
- end
371
-
372
- def print_llm_summary(result)
373
- return unless ENV["SHOW_LLM_SUMMARY"] == "true"
374
-
375
- puts
376
- puts "LLM Summary (unverified):"
377
- puts result
378
- end
379
-
380
- def print_hallucination_warning
381
- puts "No tool results were produced."
382
- puts "LLM output suppressed to avoid hallucinated data."
383
- end
384
-
385
- # Run the agentic loop
386
- begin
387
- puts "🔄 Starting agentic tool-calling loop..."
388
- puts
389
-
390
- result = executor.run(
391
- system: system_prompt,
392
- user: user_prompt
393
- )
394
-
395
- puts
396
- puts "=" * 60
397
- puts "Agentic Analysis Complete"
398
- puts "=" * 60
399
- if tool_messages(executor.messages).empty?
400
- print_hallucination_warning
401
- else
402
- print_tool_results(executor.messages)
403
- print_llm_summary(result)
404
- end
405
- rescue Ollama::Error => e
406
- puts "❌ Error: #{e.message}"
407
- puts e.backtrace.first(5).join("\n") if e.backtrace
408
- rescue StandardError => e
409
- puts "❌ Unexpected error: #{e.message}"
410
- puts e.backtrace.first(5).join("\n") if e.backtrace
411
- end