ollama-client 0.2.2 → 0.2.4
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 +8 -0
- data/README.md +7 -1
- data/docs/CLOUD.md +29 -0
- data/docs/CONSOLE_IMPROVEMENTS.md +256 -0
- data/docs/GEM_RELEASE_GUIDE.md +794 -0
- data/docs/GET_RUBYGEMS_SECRET.md +151 -0
- data/docs/QUICK_OTP_SETUP.md +80 -0
- data/docs/QUICK_RELEASE.md +106 -0
- data/docs/README.md +43 -0
- data/docs/RUBYGEMS_OTP_SETUP.md +199 -0
- data/docs/SCHEMA_FIXES.md +147 -0
- data/docs/TEST_UPDATES.md +107 -0
- data/examples/README.md +92 -0
- data/examples/advanced_complex_schemas.rb +6 -3
- data/examples/advanced_multi_step_agent.rb +2 -1
- data/examples/chat_console.rb +12 -3
- data/examples/complete_workflow.rb +14 -4
- data/examples/dhan_console.rb +103 -8
- data/examples/dhanhq/agents/technical_analysis_agent.rb +6 -1
- data/examples/dhanhq/schemas/agent_schemas.rb +2 -2
- data/examples/dhanhq_agent.rb +23 -13
- data/examples/dhanhq_tools.rb +311 -246
- data/examples/multi_step_agent_with_external_data.rb +368 -0
- data/{test_dhanhq_tool_calling.rb → examples/test_dhanhq_tool_calling.rb} +99 -6
- data/lib/ollama/agent/executor.rb +30 -30
- data/lib/ollama/client.rb +73 -80
- data/lib/ollama/dto.rb +7 -7
- data/lib/ollama/options.rb +17 -9
- data/lib/ollama/response.rb +4 -6
- data/lib/ollama/tool/function/parameters.rb +1 -0
- data/lib/ollama/version.rb +1 -1
- metadata +24 -9
- /data/{FEATURES_ADDED.md → docs/FEATURES_ADDED.md} +0 -0
- /data/{HANDLERS_ANALYSIS.md → docs/HANDLERS_ANALYSIS.md} +0 -0
- /data/{PRODUCTION_FIXES.md → docs/PRODUCTION_FIXES.md} +0 -0
- /data/{TESTING.md → docs/TESTING.md} +0 -0
- /data/{test_tool_calling.rb → examples/test_tool_calling.rb} +0 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Schema Validation Fixes
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
LLMs were returning confidence values as percentages (e.g., 95, 100) instead of decimals (e.g., 0.95, 1.0), causing schema validation errors:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
The property '#/confidence' did not have a maximum value of 1, inclusively
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Root Cause
|
|
12
|
+
|
|
13
|
+
JSON schemas defined numeric constraints (0-1) but lacked explicit descriptions explaining the expected format. LLMs naturally interpret "confidence" as percentages without guidance.
|
|
14
|
+
|
|
15
|
+
## Solution
|
|
16
|
+
|
|
17
|
+
Added explicit descriptions to all numeric fields with constrained ranges, clarifying the expected format.
|
|
18
|
+
|
|
19
|
+
### Fixed Fields
|
|
20
|
+
|
|
21
|
+
#### Confidence Fields (0.0 to 1.0)
|
|
22
|
+
Updated in 8 locations across the codebase:
|
|
23
|
+
|
|
24
|
+
- `examples/complete_workflow.rb` (2 instances)
|
|
25
|
+
- TaskPlanner schema
|
|
26
|
+
- DataAnalyzer schema
|
|
27
|
+
- `examples/dhanhq_agent.rb` (2 instances)
|
|
28
|
+
- `examples/dhanhq/schemas/agent_schemas.rb` (2 instances)
|
|
29
|
+
- `examples/dhanhq/agents/technical_analysis_agent.rb` (1 instance)
|
|
30
|
+
- `examples/advanced_multi_step_agent.rb` (1 instance)
|
|
31
|
+
|
|
32
|
+
**Before:**
|
|
33
|
+
```ruby
|
|
34
|
+
"confidence" => {
|
|
35
|
+
"type" => "number",
|
|
36
|
+
"minimum" => 0,
|
|
37
|
+
"maximum" => 1
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**After:**
|
|
42
|
+
```ruby
|
|
43
|
+
"confidence" => {
|
|
44
|
+
"type" => "number",
|
|
45
|
+
"minimum" => 0,
|
|
46
|
+
"maximum" => 1,
|
|
47
|
+
"description" => "Confidence in this decision (0.0 to 1.0, where 1.0 is 100% confident)"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Other Constrained Fields
|
|
52
|
+
|
|
53
|
+
- `reproducibility_score` in `advanced_complex_schemas.rb`
|
|
54
|
+
- Added: "Reproducibility score (0.0 to 1.0, where 1.0 means fully reproducible)"
|
|
55
|
+
|
|
56
|
+
- `profit_margin` in `advanced_complex_schemas.rb`
|
|
57
|
+
- Added: "Profit margin as percentage (0 to 100)"
|
|
58
|
+
|
|
59
|
+
- `overall_score` in `advanced_complex_schemas.rb`
|
|
60
|
+
- Added: "Overall quality score (0 to 100)"
|
|
61
|
+
|
|
62
|
+
## Best Practices
|
|
63
|
+
|
|
64
|
+
### When Defining Schemas
|
|
65
|
+
|
|
66
|
+
1. **Always include descriptions** for numeric fields with constraints
|
|
67
|
+
2. **Be explicit about format**: decimal (0.0-1.0) vs percentage (0-100)
|
|
68
|
+
3. **Provide examples** in descriptions when helpful
|
|
69
|
+
4. **Use clear units**: "as percentage", "as decimal", "where 1.0 is 100%"
|
|
70
|
+
|
|
71
|
+
### When Writing Prompts
|
|
72
|
+
|
|
73
|
+
**Schema descriptions alone may not be enough!** Always reinforce numeric formats in the prompt itself:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
# ❌ BAD: Relies only on schema description
|
|
77
|
+
result = @client.generate(
|
|
78
|
+
prompt: "Analyze this data: #{data}",
|
|
79
|
+
schema: @schema
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# ✅ GOOD: Explicit examples in prompt
|
|
83
|
+
result = @client.generate(
|
|
84
|
+
prompt: "Analyze this data: #{data}\n\nIMPORTANT: Express confidence as a decimal between 0.0 and 1.0 (e.g., 0.85 for 85% confidence, not 85).",
|
|
85
|
+
schema: @schema
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Why this matters:**
|
|
90
|
+
- LLMs may not always read schema descriptions carefully
|
|
91
|
+
- Concrete examples in prompts are more effective
|
|
92
|
+
- Prevents "100" vs "1.0" confusion
|
|
93
|
+
|
|
94
|
+
### Example Patterns
|
|
95
|
+
|
|
96
|
+
**Decimal confidence (0-1):**
|
|
97
|
+
```ruby
|
|
98
|
+
"confidence" => {
|
|
99
|
+
"type" => "number",
|
|
100
|
+
"minimum" => 0,
|
|
101
|
+
"maximum" => 1,
|
|
102
|
+
"description" => "Confidence level (0.0 to 1.0, where 1.0 is 100% confident)"
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Percentage score (0-100):**
|
|
107
|
+
```ruby
|
|
108
|
+
"score" => {
|
|
109
|
+
"type" => "number",
|
|
110
|
+
"minimum" => 0,
|
|
111
|
+
"maximum" => 100,
|
|
112
|
+
"description" => "Quality score as percentage (0 to 100)"
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Probability (0-1):**
|
|
117
|
+
```ruby
|
|
118
|
+
"probability" => {
|
|
119
|
+
"type" => "number",
|
|
120
|
+
"minimum" => 0,
|
|
121
|
+
"maximum" => 1,
|
|
122
|
+
"description" => "Probability value (0.0 to 1.0)"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Testing
|
|
127
|
+
|
|
128
|
+
All modified files passed syntax validation:
|
|
129
|
+
```bash
|
|
130
|
+
ruby -c examples/complete_workflow.rb # Syntax OK
|
|
131
|
+
ruby -c examples/dhanhq_agent.rb # Syntax OK
|
|
132
|
+
ruby -c examples/advanced_multi_step_agent.rb # Syntax OK
|
|
133
|
+
ruby -c examples/advanced_complex_schemas.rb # Syntax OK
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Impact
|
|
137
|
+
|
|
138
|
+
- **Examples now run without schema validation errors**
|
|
139
|
+
- **LLMs receive clear guidance** on numeric formats
|
|
140
|
+
- **Consistent patterns** across all example schemas
|
|
141
|
+
- **Better developer experience** with self-documenting schemas
|
|
142
|
+
|
|
143
|
+
## Related Files
|
|
144
|
+
|
|
145
|
+
See also:
|
|
146
|
+
- [Testing Guide](TESTING.md) - How to test structured outputs
|
|
147
|
+
- [Features Added](FEATURES_ADDED.md) - Schema validation features
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Test File Updates
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
Updated test examples to reflect recent enhancements to DhanHQ tool calling, particularly around historical data and technical indicators.
|
|
6
|
+
|
|
7
|
+
## Files Updated
|
|
8
|
+
|
|
9
|
+
### 1. `examples/test_dhanhq_tool_calling.rb`
|
|
10
|
+
|
|
11
|
+
#### Changes Made:
|
|
12
|
+
|
|
13
|
+
1. **Updated `historical_data_tool` definition** (lines 66-101):
|
|
14
|
+
- Changed `interval` enum from `["daily", "weekly", "monthly"]` to `["1", "5", "15", "25", "60"]`
|
|
15
|
+
- Added proper description: "Minute interval for intraday data. Omit for daily data."
|
|
16
|
+
- Added `calculate_indicators` boolean parameter
|
|
17
|
+
- Updated description to mention technical indicators (RSI, MACD, SMA, EMA, Bollinger Bands, ATR)
|
|
18
|
+
- Added `required` fields: `from_date` and `to_date`
|
|
19
|
+
|
|
20
|
+
2. **Added Test 5: Historical Data with Technical Indicators**
|
|
21
|
+
- Tests the new `calculate_indicators` parameter
|
|
22
|
+
- Verifies LLM includes all required parameters including interval
|
|
23
|
+
- Shows how to request intraday data with indicators
|
|
24
|
+
|
|
25
|
+
3. **Added Test 6: Intraday Data with 5-minute intervals**
|
|
26
|
+
- Tests intraday data specifically
|
|
27
|
+
- Uses current date dynamically
|
|
28
|
+
- Validates interval parameter is one of the valid values (1, 5, 15, 25, 60)
|
|
29
|
+
- Shows date range handling
|
|
30
|
+
|
|
31
|
+
4. **Enhanced Summary Section**
|
|
32
|
+
- Added notes about historical data enhancements
|
|
33
|
+
- Documents interval usage for intraday vs daily data
|
|
34
|
+
- Explains `calculate_indicators` feature and its benefits
|
|
35
|
+
|
|
36
|
+
## Key Features Tested
|
|
37
|
+
|
|
38
|
+
### Intraday Data
|
|
39
|
+
- ✅ Interval parameter with values: "1", "5", "15", "25", "60"
|
|
40
|
+
- ✅ Date handling (from_date, to_date)
|
|
41
|
+
- ✅ 5-minute intervals (most commonly used for intraday analysis)
|
|
42
|
+
|
|
43
|
+
### Technical Indicators
|
|
44
|
+
- ✅ `calculate_indicators` parameter
|
|
45
|
+
- ✅ Returns RSI, MACD, SMA, EMA, Bollinger Bands, ATR
|
|
46
|
+
- ✅ Reduces response size vs raw OHLCV data
|
|
47
|
+
|
|
48
|
+
### Tool Calling
|
|
49
|
+
- ✅ chat_raw() method for accessing tool_calls
|
|
50
|
+
- ✅ Structured Tool classes
|
|
51
|
+
- ✅ Parameter validation
|
|
52
|
+
|
|
53
|
+
## Example Usage
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
# Get intraday data with indicators
|
|
57
|
+
historical_data_tool = Ollama::Tool.new(
|
|
58
|
+
type: "function",
|
|
59
|
+
function: Ollama::Tool::Function.new(
|
|
60
|
+
name: "get_historical_data",
|
|
61
|
+
description: "Get historical price data (OHLCV) or technical indicators...",
|
|
62
|
+
parameters: Ollama::Tool::Function::Parameters.new(
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
interval: Ollama::Tool::Function::Parameters::Property.new(
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Minute interval for intraday data",
|
|
68
|
+
enum: %w[1 5 15 25 60] # Updated from ["daily", "weekly", "monthly"]
|
|
69
|
+
),
|
|
70
|
+
calculate_indicators: Ollama::Tool::Function::Parameters::Property.new(
|
|
71
|
+
type: "boolean",
|
|
72
|
+
description: "If true, returns technical indicators instead of raw data"
|
|
73
|
+
),
|
|
74
|
+
# ... other properties
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Request with intraday and indicators
|
|
81
|
+
response = client.chat_raw(
|
|
82
|
+
messages: [Ollama::Agent::Messages.user(
|
|
83
|
+
"Get NIFTY intraday data with 5-minute intervals and technical indicators"
|
|
84
|
+
)],
|
|
85
|
+
tools: historical_data_tool
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Testing
|
|
90
|
+
|
|
91
|
+
Run the updated tests:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Tool calling examples
|
|
95
|
+
ruby examples/test_dhanhq_tool_calling.rb
|
|
96
|
+
ruby examples/test_tool_calling.rb
|
|
97
|
+
|
|
98
|
+
# DhanHQ specific tests
|
|
99
|
+
ruby examples/dhanhq/test_tool_calling.rb
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Notes
|
|
103
|
+
|
|
104
|
+
- The `interval` parameter is now correctly defined for intraday minute intervals
|
|
105
|
+
- The old values ("daily", "weekly", "monthly") were incorrect for the DhanHQ API
|
|
106
|
+
- The new `calculate_indicators` parameter provides technical analysis without large response sizes
|
|
107
|
+
- Tests now include date handling examples using `Date.today`
|
data/examples/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
This directory contains working examples demonstrating various features of the ollama-client gem.
|
|
4
|
+
|
|
5
|
+
## Quick Start Examples
|
|
6
|
+
|
|
7
|
+
### Basic Tool Calling
|
|
8
|
+
- **[test_tool_calling.rb](test_tool_calling.rb)** - Simple tool calling demo with weather tool
|
|
9
|
+
- **[tool_calling_pattern.rb](tool_calling_pattern.rb)** - Recommended patterns for tool calling
|
|
10
|
+
- **[tool_calling_direct.rb](tool_calling_direct.rb)** - Direct tool calling without Executor
|
|
11
|
+
|
|
12
|
+
### DhanHQ Market Data
|
|
13
|
+
- **[test_dhanhq_tool_calling.rb](test_dhanhq_tool_calling.rb)** - DhanHQ tools with updated intraday & indicators
|
|
14
|
+
- **[dhanhq_tools.rb](dhanhq_tools.rb)** - DhanHQ API wrapper tools
|
|
15
|
+
- **[dhan_console.rb](dhan_console.rb)** - Interactive DhanHQ console with planning
|
|
16
|
+
|
|
17
|
+
### Multi-Step Agents
|
|
18
|
+
- **[multi_step_agent_e2e.rb](multi_step_agent_e2e.rb)** - End-to-end multi-step agent example
|
|
19
|
+
- **[multi_step_agent_with_external_data.rb](multi_step_agent_with_external_data.rb)** - Agent with external data integration
|
|
20
|
+
|
|
21
|
+
### Structured Data
|
|
22
|
+
- **[structured_outputs_chat.rb](structured_outputs_chat.rb)** - Structured outputs with schemas
|
|
23
|
+
- **[structured_tools.rb](structured_tools.rb)** - Structured tool definitions
|
|
24
|
+
- **[tool_dto_example.rb](tool_dto_example.rb)** - Using DTOs for tool definitions
|
|
25
|
+
|
|
26
|
+
### Advanced Features
|
|
27
|
+
- **[advanced_multi_step_agent.rb](advanced_multi_step_agent.rb)** - Complex multi-step workflows
|
|
28
|
+
- **[advanced_error_handling.rb](advanced_error_handling.rb)** - Error handling patterns
|
|
29
|
+
- **[advanced_edge_cases.rb](advanced_edge_cases.rb)** - Edge case handling
|
|
30
|
+
- **[advanced_complex_schemas.rb](advanced_complex_schemas.rb)** - Complex schema definitions
|
|
31
|
+
- **[advanced_performance_testing.rb](advanced_performance_testing.rb)** - Performance testing
|
|
32
|
+
|
|
33
|
+
### Interactive Consoles
|
|
34
|
+
- **[chat_console.rb](chat_console.rb)** - Simple chat console with streaming
|
|
35
|
+
- **[dhan_console.rb](dhan_console.rb)** - DhanHQ market data console with formatted tool results
|
|
36
|
+
|
|
37
|
+
### Complete Workflows
|
|
38
|
+
- **[complete_workflow.rb](complete_workflow.rb)** - Complete agent workflow example
|
|
39
|
+
|
|
40
|
+
## DhanHQ Examples
|
|
41
|
+
|
|
42
|
+
The `dhanhq/` subdirectory contains more specialized DhanHQ examples:
|
|
43
|
+
- Technical analysis agents
|
|
44
|
+
- Market scanners (intraday options, swing trading)
|
|
45
|
+
- Pattern recognition and trend analysis
|
|
46
|
+
- Multi-agent orchestration
|
|
47
|
+
|
|
48
|
+
See [dhanhq/README.md](dhanhq/README.md) for details.
|
|
49
|
+
|
|
50
|
+
## Running Examples
|
|
51
|
+
|
|
52
|
+
Most examples are standalone and can be run directly:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Basic tool calling
|
|
56
|
+
ruby examples/test_tool_calling.rb
|
|
57
|
+
|
|
58
|
+
# DhanHQ with intraday data
|
|
59
|
+
ruby examples/test_dhanhq_tool_calling.rb
|
|
60
|
+
|
|
61
|
+
# Interactive console
|
|
62
|
+
ruby examples/chat_console.rb
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Requirements
|
|
66
|
+
|
|
67
|
+
Some examples require additional setup:
|
|
68
|
+
|
|
69
|
+
**DhanHQ Examples:**
|
|
70
|
+
- Set `DHANHQ_CLIENT_ID` and `DHANHQ_ACCESS_TOKEN` environment variables
|
|
71
|
+
- Or create `.env` file with credentials
|
|
72
|
+
|
|
73
|
+
**Ollama:**
|
|
74
|
+
- Ollama server running (default: `http://localhost:11434`)
|
|
75
|
+
- Set `OLLAMA_BASE_URL` if using a different URL
|
|
76
|
+
- Set `OLLAMA_MODEL` if not using default model
|
|
77
|
+
|
|
78
|
+
## Learning Path
|
|
79
|
+
|
|
80
|
+
1. **Start here:** `test_tool_calling.rb` - Learn basic tool calling
|
|
81
|
+
2. **Structured data:** `structured_outputs_chat.rb` - Schema-based outputs
|
|
82
|
+
3. **Multi-step:** `multi_step_agent_e2e.rb` - Complex agent workflows
|
|
83
|
+
4. **Market data:** `test_dhanhq_tool_calling.rb` - Real-world API integration
|
|
84
|
+
5. **Interactive:** `dhan_console.rb` - Full-featured console with planning
|
|
85
|
+
|
|
86
|
+
## Contributing
|
|
87
|
+
|
|
88
|
+
When adding new examples:
|
|
89
|
+
- Include clear comments explaining what the example demonstrates
|
|
90
|
+
- Add `#!/usr/bin/env ruby` shebang at the top
|
|
91
|
+
- Use `frozen_string_literal: true`
|
|
92
|
+
- Update this README with a description
|
|
@@ -35,7 +35,8 @@ class FinancialAnalyzer
|
|
|
35
35
|
"profit_margin" => {
|
|
36
36
|
"type" => "number",
|
|
37
37
|
"minimum" => 0,
|
|
38
|
-
"maximum" => 100
|
|
38
|
+
"maximum" => 100,
|
|
39
|
+
"description" => "Profit margin as percentage (0 to 100)"
|
|
39
40
|
},
|
|
40
41
|
"growth_rate" => {
|
|
41
42
|
"type" => "number"
|
|
@@ -121,7 +122,8 @@ class CodeReviewer
|
|
|
121
122
|
"overall_score" => {
|
|
122
123
|
"type" => "integer",
|
|
123
124
|
"minimum" => 0,
|
|
124
|
-
"maximum" => 100
|
|
125
|
+
"maximum" => 100,
|
|
126
|
+
"description" => "Overall quality score (0 to 100)"
|
|
125
127
|
},
|
|
126
128
|
"issues" => {
|
|
127
129
|
"type" => "array",
|
|
@@ -262,7 +264,8 @@ class ResearchAnalyzer
|
|
|
262
264
|
"reproducibility_score" => {
|
|
263
265
|
"type" => "number",
|
|
264
266
|
"minimum" => 0,
|
|
265
|
-
"maximum" => 1
|
|
267
|
+
"maximum" => 1,
|
|
268
|
+
"description" => "Reproducibility score (0.0 to 1.0, where 1.0 means fully reproducible)"
|
|
266
269
|
}
|
|
267
270
|
}
|
|
268
271
|
}
|
data/examples/chat_console.rb
CHANGED
|
@@ -39,8 +39,8 @@ MAX_HISTORY = 200
|
|
|
39
39
|
COLOR_RESET = "\e[0m"
|
|
40
40
|
COLOR_USER = "\e[32m"
|
|
41
41
|
COLOR_LLM = "\e[36m"
|
|
42
|
-
USER_PROMPT = "#{COLOR_USER}you>#{COLOR_RESET} "
|
|
43
|
-
LLM_PROMPT = "#{COLOR_LLM}llm>#{COLOR_RESET} "
|
|
42
|
+
USER_PROMPT = "#{COLOR_USER}you>#{COLOR_RESET} ".freeze
|
|
43
|
+
LLM_PROMPT = "#{COLOR_LLM}llm>#{COLOR_RESET} ".freeze
|
|
44
44
|
|
|
45
45
|
def build_reader
|
|
46
46
|
TTY::Reader.new
|
|
@@ -86,7 +86,10 @@ end
|
|
|
86
86
|
|
|
87
87
|
def chat_response(client, messages, config)
|
|
88
88
|
content = +""
|
|
89
|
-
|
|
89
|
+
prompt_printed = false
|
|
90
|
+
|
|
91
|
+
print "#{COLOR_LLM}...#{COLOR_RESET}"
|
|
92
|
+
$stdout.flush
|
|
90
93
|
|
|
91
94
|
client.chat_raw(
|
|
92
95
|
messages: messages,
|
|
@@ -97,8 +100,14 @@ def chat_response(client, messages, config)
|
|
|
97
100
|
token = chunk.dig("message", "content").to_s
|
|
98
101
|
next if token.empty?
|
|
99
102
|
|
|
103
|
+
unless prompt_printed
|
|
104
|
+
print "\r#{LLM_PROMPT}"
|
|
105
|
+
prompt_printed = true
|
|
106
|
+
end
|
|
107
|
+
|
|
100
108
|
content << token
|
|
101
109
|
print token
|
|
110
|
+
$stdout.flush
|
|
102
111
|
end
|
|
103
112
|
|
|
104
113
|
puts
|
|
@@ -30,7 +30,7 @@ class TaskPlanner
|
|
|
30
30
|
"type" => "number",
|
|
31
31
|
"minimum" => 0,
|
|
32
32
|
"maximum" => 1,
|
|
33
|
-
"description" => "Confidence in this decision"
|
|
33
|
+
"description" => "Confidence in this decision (0.0 to 1.0, where 1.0 is 100% confident)"
|
|
34
34
|
},
|
|
35
35
|
"next_step" => {
|
|
36
36
|
"type" => "string",
|
|
@@ -49,8 +49,13 @@ class TaskPlanner
|
|
|
49
49
|
puts "Context: #{context}\n\n"
|
|
50
50
|
|
|
51
51
|
begin
|
|
52
|
+
prompt = "Given this context: #{context}\n\n" \
|
|
53
|
+
"Decide the next action to take.\n\n" \
|
|
54
|
+
"IMPORTANT: Use decimal values for confidence " \
|
|
55
|
+
"(e.g., 0.95 for 95% confident, 0.80 for 80% confident, 1.0 for 100% confident)."
|
|
56
|
+
|
|
52
57
|
result = @client.generate(
|
|
53
|
-
prompt:
|
|
58
|
+
prompt: prompt,
|
|
54
59
|
schema: @task_schema
|
|
55
60
|
)
|
|
56
61
|
|
|
@@ -132,7 +137,8 @@ class DataAnalyzer
|
|
|
132
137
|
"confidence" => {
|
|
133
138
|
"type" => "number",
|
|
134
139
|
"minimum" => 0,
|
|
135
|
-
"maximum" => 1
|
|
140
|
+
"maximum" => 1,
|
|
141
|
+
"description" => "Confidence level (0.0 to 1.0, where 1.0 is 100% confident)"
|
|
136
142
|
},
|
|
137
143
|
"key_points" => {
|
|
138
144
|
"type" => "array",
|
|
@@ -157,8 +163,12 @@ class DataAnalyzer
|
|
|
157
163
|
puts "Data: #{data}\n\n"
|
|
158
164
|
|
|
159
165
|
begin
|
|
166
|
+
prompt = "Analyze this data and provide insights: #{data}\n\n" \
|
|
167
|
+
"IMPORTANT: Express confidence as a decimal between 0.0 and 1.0 " \
|
|
168
|
+
"(e.g., 0.85 for 85% confidence, not 85)."
|
|
169
|
+
|
|
160
170
|
result = @client.generate(
|
|
161
|
-
prompt:
|
|
171
|
+
prompt: prompt,
|
|
162
172
|
schema: @analysis_schema
|
|
163
173
|
)
|
|
164
174
|
|
data/examples/dhan_console.rb
CHANGED
|
@@ -42,8 +42,8 @@ MAX_HISTORY = 200
|
|
|
42
42
|
COLOR_RESET = "\e[0m"
|
|
43
43
|
COLOR_USER = "\e[32m"
|
|
44
44
|
COLOR_LLM = "\e[36m"
|
|
45
|
-
USER_PROMPT = "#{COLOR_USER}you>#{COLOR_RESET} "
|
|
46
|
-
LLM_PROMPT = "#{COLOR_LLM}llm>#{COLOR_RESET} "
|
|
45
|
+
USER_PROMPT = "#{COLOR_USER}you>#{COLOR_RESET} ".freeze
|
|
46
|
+
LLM_PROMPT = "#{COLOR_LLM}llm>#{COLOR_RESET} ".freeze
|
|
47
47
|
|
|
48
48
|
def build_reader
|
|
49
49
|
TTY::Reader.new
|
|
@@ -497,7 +497,7 @@ def build_tools
|
|
|
497
497
|
"get_historical_data" => lambda do |exchange_segment:, from_date:, to_date:, symbol: nil, security_id: nil,
|
|
498
498
|
interval: nil, expiry_code: nil, calculate_indicators: false|
|
|
499
499
|
# Convert security_id to integer if provided (LLM may pass it as string)
|
|
500
|
-
normalized_security_id = security_id
|
|
500
|
+
normalized_security_id = security_id&.to_i
|
|
501
501
|
DhanHQDataTools.get_historical_data(**compact_kwargs(exchange_segment: exchange_segment,
|
|
502
502
|
symbol: symbol,
|
|
503
503
|
security_id: normalized_security_id,
|
|
@@ -509,14 +509,14 @@ def build_tools
|
|
|
509
509
|
end,
|
|
510
510
|
"get_expiry_list" => lambda do |exchange_segment:, symbol: nil, security_id: nil|
|
|
511
511
|
# Convert security_id to integer if provided (LLM may pass it as string)
|
|
512
|
-
normalized_security_id = security_id
|
|
512
|
+
normalized_security_id = security_id&.to_i
|
|
513
513
|
DhanHQDataTools.get_expiry_list(**compact_kwargs(exchange_segment: exchange_segment,
|
|
514
514
|
symbol: symbol,
|
|
515
515
|
security_id: normalized_security_id))
|
|
516
516
|
end,
|
|
517
517
|
"get_option_chain" => lambda do |exchange_segment:, symbol: nil, security_id: nil, expiry: nil, strikes_count: 5|
|
|
518
518
|
# Convert security_id to integer if provided (LLM may pass it as string)
|
|
519
|
-
normalized_security_id = security_id
|
|
519
|
+
normalized_security_id = security_id&.to_i
|
|
520
520
|
# Default to 5 strikes (2 ITM, ATM, 2 OTM) - good balance for analysis
|
|
521
521
|
normalized_strikes_count = strikes_count.to_i
|
|
522
522
|
normalized_strikes_count = 5 if normalized_strikes_count < 1 # Minimum 1 (ATM)
|
|
@@ -555,16 +555,18 @@ def tool_messages(messages)
|
|
|
555
555
|
end
|
|
556
556
|
|
|
557
557
|
def print_tool_results(messages)
|
|
558
|
-
puts "Tool Results:"
|
|
559
558
|
tool_messages(messages).each do |message|
|
|
560
559
|
print_tool_message(message)
|
|
561
560
|
end
|
|
561
|
+
puts # blank line after tool results
|
|
562
562
|
end
|
|
563
563
|
|
|
564
564
|
def print_tool_message(message)
|
|
565
565
|
tool_name = message[:name] || "unknown_tool"
|
|
566
|
-
|
|
567
|
-
|
|
566
|
+
content = parse_tool_content(message[:content])
|
|
567
|
+
|
|
568
|
+
puts "\n#{COLOR_LLM}🔧 Tool Called:#{COLOR_RESET} #{tool_name}"
|
|
569
|
+
print_formatted_result(tool_name, content)
|
|
568
570
|
end
|
|
569
571
|
|
|
570
572
|
def format_tool_content(content)
|
|
@@ -582,6 +584,99 @@ rescue JSON::ParserError
|
|
|
582
584
|
content
|
|
583
585
|
end
|
|
584
586
|
|
|
587
|
+
def print_formatted_result(tool_name, content)
|
|
588
|
+
return puts content if content.is_a?(String)
|
|
589
|
+
|
|
590
|
+
result = content["result"] || content
|
|
591
|
+
|
|
592
|
+
case tool_name
|
|
593
|
+
when "get_live_ltp"
|
|
594
|
+
print_ltp_result(result)
|
|
595
|
+
when "get_market_quote"
|
|
596
|
+
print_quote_result(result)
|
|
597
|
+
when "get_historical_data"
|
|
598
|
+
print_historical_result(result, content)
|
|
599
|
+
when "get_option_chain"
|
|
600
|
+
print_option_chain_result(result)
|
|
601
|
+
when "get_expiry_list"
|
|
602
|
+
print_expiry_list_result(result)
|
|
603
|
+
when "find_instrument"
|
|
604
|
+
print_instrument_result(result)
|
|
605
|
+
else
|
|
606
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} #{JSON.pretty_generate(content)}"
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
def print_ltp_result(result)
|
|
611
|
+
symbol = result["symbol"] || "Unknown"
|
|
612
|
+
ltp = result["ltp"] || result.dig("ltp_data", "last_price")
|
|
613
|
+
exchange = result["exchange_segment"]
|
|
614
|
+
|
|
615
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} #{symbol} (#{exchange})"
|
|
616
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Last Price: ₹#{ltp}"
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def print_quote_result(result)
|
|
620
|
+
symbol = result["symbol"] || "Unknown"
|
|
621
|
+
quote = result["quote"] || {}
|
|
622
|
+
ohlc = quote["ohlc"] || {}
|
|
623
|
+
|
|
624
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} #{symbol}"
|
|
625
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} LTP: ₹#{quote['last_price']}"
|
|
626
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} OHLC: O:#{ohlc['open']} H:#{ohlc['high']} L:#{ohlc['low']} C:#{ohlc['close']}"
|
|
627
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Volume: #{quote['volume']}" if quote["volume"]&.positive?
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
def print_historical_result(result, content)
|
|
631
|
+
if result.is_a?(Hash) && result.key?("indicators")
|
|
632
|
+
print_indicator_result(result)
|
|
633
|
+
elsif result.is_a?(Hash)
|
|
634
|
+
data_points = result["data"]&.size || 0
|
|
635
|
+
interval = content.dig("params", "interval")
|
|
636
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Historical data: #{data_points} records"
|
|
637
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Interval: #{interval}" if interval
|
|
638
|
+
elsif result.is_a?(Array)
|
|
639
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} #{result.size} data points"
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def print_indicator_result(result)
|
|
644
|
+
indicators = result["indicators"]
|
|
645
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Technical Indicators:"
|
|
646
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Current Price: ₹#{indicators['current_price']}"
|
|
647
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} RSI(14): #{indicators['rsi']&.round(2)}"
|
|
648
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} MACD: #{indicators.dig('macd', 'macd')&.round(2)}"
|
|
649
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} SMA(20): ₹#{indicators['sma_20']&.round(2)}"
|
|
650
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} SMA(50): ₹#{indicators['sma_50']&.round(2)}"
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
def print_option_chain_result(result)
|
|
654
|
+
oc = result.dig("data", "oc") || {}
|
|
655
|
+
last_price = result.dig("data", "last_price")
|
|
656
|
+
strikes = oc.keys.size
|
|
657
|
+
|
|
658
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Spot: ₹#{last_price}"
|
|
659
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Strikes: #{strikes}"
|
|
660
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} (Filtered: ATM/OTM/ITM with both CE & PE)"
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
def print_expiry_list_result(result)
|
|
664
|
+
expiries = result["expiries"] || []
|
|
665
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Available expiries: #{expiries.size}"
|
|
666
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Next expiry: #{expiries.first}" if expiries.any?
|
|
667
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Expiries: #{expiries[0..4].join(', ')}#{'...' if expiries.size > 5}"
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
def print_instrument_result(result)
|
|
671
|
+
symbol = result["symbol"]
|
|
672
|
+
security_id = result["security_id"]
|
|
673
|
+
exchange = result["exchange_segment"]
|
|
674
|
+
|
|
675
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Found: #{symbol}"
|
|
676
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Security ID: #{security_id}"
|
|
677
|
+
puts " #{COLOR_LLM}→#{COLOR_RESET} Exchange: #{exchange}"
|
|
678
|
+
end
|
|
679
|
+
|
|
585
680
|
def show_llm_summary?
|
|
586
681
|
ENV["SHOW_LLM_SUMMARY"] == "true"
|
|
587
682
|
end
|
|
@@ -236,7 +236,12 @@ module DhanHQ
|
|
|
236
236
|
"stop_loss" => { "type" => "number" },
|
|
237
237
|
"target_price" => { "type" => "number" },
|
|
238
238
|
"risk_reward_ratio" => { "type" => "number" },
|
|
239
|
-
"confidence" => {
|
|
239
|
+
"confidence" => {
|
|
240
|
+
"type" => "number",
|
|
241
|
+
"minimum" => 0,
|
|
242
|
+
"maximum" => 1,
|
|
243
|
+
"description" => "Confidence (0.0 to 1.0)"
|
|
244
|
+
},
|
|
240
245
|
"reasoning" => { "type" => "string" },
|
|
241
246
|
"timeframe" => { "type" => "string" }
|
|
242
247
|
}
|
|
@@ -21,7 +21,7 @@ module DhanHQ
|
|
|
21
21
|
"type" => "number",
|
|
22
22
|
"minimum" => 0,
|
|
23
23
|
"maximum" => 1,
|
|
24
|
-
"description" => "Confidence in this decision"
|
|
24
|
+
"description" => "Confidence in this decision (0.0 to 1.0, where 1.0 is 100% confident)"
|
|
25
25
|
},
|
|
26
26
|
"parameters" => {
|
|
27
27
|
"type" => "object",
|
|
@@ -47,7 +47,7 @@ module DhanHQ
|
|
|
47
47
|
"type" => "number",
|
|
48
48
|
"minimum" => 0,
|
|
49
49
|
"maximum" => 1,
|
|
50
|
-
"description" => "Confidence in this decision"
|
|
50
|
+
"description" => "Confidence in this decision (0.0 to 1.0, where 1.0 is 100% confident)"
|
|
51
51
|
},
|
|
52
52
|
"parameters" => {
|
|
53
53
|
"type" => "object",
|