ollama-client 0.2.5 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +138 -76
- data/docs/EXAMPLE_REORGANIZATION.md +412 -0
- data/docs/GETTING_STARTED.md +361 -0
- data/docs/INTEGRATION_TESTING.md +170 -0
- data/docs/NEXT_STEPS_SUMMARY.md +114 -0
- data/docs/PERSONAS.md +383 -0
- data/docs/QUICK_START.md +195 -0
- data/docs/TESTING.md +392 -170
- data/docs/TEST_CHECKLIST.md +450 -0
- data/examples/README.md +51 -66
- data/examples/basic_chat.rb +33 -0
- data/examples/basic_generate.rb +29 -0
- data/examples/tool_calling_parsing.rb +59 -0
- data/exe/ollama-client +128 -1
- data/lib/ollama/agent/planner.rb +7 -2
- data/lib/ollama/chat_session.rb +101 -0
- data/lib/ollama/client.rb +41 -35
- data/lib/ollama/config.rb +4 -1
- data/lib/ollama/document_loader.rb +1 -1
- data/lib/ollama/embeddings.rb +41 -26
- data/lib/ollama/errors.rb +1 -0
- data/lib/ollama/personas.rb +287 -0
- data/lib/ollama/version.rb +1 -1
- data/lib/ollama_client.rb +7 -0
- metadata +14 -48
- data/examples/advanced_complex_schemas.rb +0 -366
- data/examples/advanced_edge_cases.rb +0 -241
- data/examples/advanced_error_handling.rb +0 -200
- data/examples/advanced_multi_step_agent.rb +0 -341
- data/examples/advanced_performance_testing.rb +0 -186
- data/examples/chat_console.rb +0 -143
- data/examples/complete_workflow.rb +0 -245
- data/examples/dhan_console.rb +0 -843
- data/examples/dhanhq/README.md +0 -236
- data/examples/dhanhq/agents/base_agent.rb +0 -74
- data/examples/dhanhq/agents/data_agent.rb +0 -66
- data/examples/dhanhq/agents/orchestrator_agent.rb +0 -120
- data/examples/dhanhq/agents/technical_analysis_agent.rb +0 -252
- data/examples/dhanhq/agents/trading_agent.rb +0 -81
- data/examples/dhanhq/analysis/market_structure.rb +0 -138
- data/examples/dhanhq/analysis/pattern_recognizer.rb +0 -192
- data/examples/dhanhq/analysis/trend_analyzer.rb +0 -88
- data/examples/dhanhq/builders/market_context_builder.rb +0 -67
- data/examples/dhanhq/dhanhq_agent.rb +0 -829
- data/examples/dhanhq/indicators/technical_indicators.rb +0 -158
- data/examples/dhanhq/scanners/intraday_options_scanner.rb +0 -492
- data/examples/dhanhq/scanners/swing_scanner.rb +0 -247
- data/examples/dhanhq/schemas/agent_schemas.rb +0 -61
- data/examples/dhanhq/services/base_service.rb +0 -46
- data/examples/dhanhq/services/data_service.rb +0 -118
- data/examples/dhanhq/services/trading_service.rb +0 -59
- data/examples/dhanhq/technical_analysis_agentic_runner.rb +0 -411
- data/examples/dhanhq/technical_analysis_runner.rb +0 -420
- data/examples/dhanhq/test_tool_calling.rb +0 -538
- data/examples/dhanhq/test_tool_calling_verbose.rb +0 -251
- data/examples/dhanhq/utils/instrument_helper.rb +0 -32
- data/examples/dhanhq/utils/parameter_cleaner.rb +0 -28
- data/examples/dhanhq/utils/parameter_normalizer.rb +0 -45
- data/examples/dhanhq/utils/rate_limiter.rb +0 -23
- data/examples/dhanhq/utils/trading_parameter_normalizer.rb +0 -72
- data/examples/dhanhq_agent.rb +0 -964
- data/examples/dhanhq_tools.rb +0 -1663
- data/examples/multi_step_agent_with_external_data.rb +0 -368
- data/examples/structured_outputs_chat.rb +0 -72
- data/examples/structured_tools.rb +0 -89
- data/examples/test_dhanhq_tool_calling.rb +0 -375
- data/examples/test_tool_calling.rb +0 -160
- data/examples/tool_calling_direct.rb +0 -124
- data/examples/tool_calling_pattern.rb +0 -269
- 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
|