ollama-client 0.2.1 → 0.2.3

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +220 -12
  4. data/docs/CLOUD.md +29 -0
  5. data/docs/CONSOLE_IMPROVEMENTS.md +256 -0
  6. data/docs/FEATURES_ADDED.md +145 -0
  7. data/docs/HANDLERS_ANALYSIS.md +190 -0
  8. data/docs/README.md +37 -0
  9. data/docs/SCHEMA_FIXES.md +147 -0
  10. data/docs/TEST_UPDATES.md +107 -0
  11. data/examples/README.md +92 -0
  12. data/examples/advanced_complex_schemas.rb +6 -3
  13. data/examples/advanced_multi_step_agent.rb +13 -7
  14. data/examples/chat_console.rb +143 -0
  15. data/examples/complete_workflow.rb +14 -4
  16. data/examples/dhan_console.rb +843 -0
  17. data/examples/dhanhq/agents/base_agent.rb +0 -2
  18. data/examples/dhanhq/agents/orchestrator_agent.rb +1 -2
  19. data/examples/dhanhq/agents/technical_analysis_agent.rb +67 -49
  20. data/examples/dhanhq/analysis/market_structure.rb +44 -28
  21. data/examples/dhanhq/analysis/pattern_recognizer.rb +64 -47
  22. data/examples/dhanhq/analysis/trend_analyzer.rb +6 -8
  23. data/examples/dhanhq/dhanhq_agent.rb +296 -99
  24. data/examples/dhanhq/indicators/technical_indicators.rb +3 -5
  25. data/examples/dhanhq/scanners/intraday_options_scanner.rb +360 -255
  26. data/examples/dhanhq/scanners/swing_scanner.rb +118 -84
  27. data/examples/dhanhq/schemas/agent_schemas.rb +2 -2
  28. data/examples/dhanhq/services/data_service.rb +5 -7
  29. data/examples/dhanhq/services/trading_service.rb +0 -3
  30. data/examples/dhanhq/technical_analysis_agentic_runner.rb +217 -84
  31. data/examples/dhanhq/technical_analysis_runner.rb +216 -162
  32. data/examples/dhanhq/test_tool_calling.rb +538 -0
  33. data/examples/dhanhq/test_tool_calling_verbose.rb +251 -0
  34. data/examples/dhanhq/utils/trading_parameter_normalizer.rb +12 -17
  35. data/examples/dhanhq_agent.rb +159 -116
  36. data/examples/dhanhq_tools.rb +1158 -251
  37. data/examples/multi_step_agent_with_external_data.rb +368 -0
  38. data/examples/structured_tools.rb +89 -0
  39. data/examples/test_dhanhq_tool_calling.rb +375 -0
  40. data/examples/test_tool_calling.rb +160 -0
  41. data/examples/tool_calling_direct.rb +124 -0
  42. data/examples/tool_dto_example.rb +94 -0
  43. data/exe/dhan_console +4 -0
  44. data/exe/ollama-client +1 -1
  45. data/lib/ollama/agent/executor.rb +116 -15
  46. data/lib/ollama/client.rb +118 -55
  47. data/lib/ollama/config.rb +36 -0
  48. data/lib/ollama/dto.rb +187 -0
  49. data/lib/ollama/embeddings.rb +77 -0
  50. data/lib/ollama/options.rb +104 -0
  51. data/lib/ollama/response.rb +121 -0
  52. data/lib/ollama/tool/function/parameters/property.rb +72 -0
  53. data/lib/ollama/tool/function/parameters.rb +101 -0
  54. data/lib/ollama/tool/function.rb +78 -0
  55. data/lib/ollama/tool.rb +60 -0
  56. data/lib/ollama/version.rb +1 -1
  57. data/lib/ollama_client.rb +3 -0
  58. metadata +31 -3
  59. /data/{PRODUCTION_FIXES.md → docs/PRODUCTION_FIXES.md} +0 -0
  60. /data/{TESTING.md → docs/TESTING.md} +0 -0
@@ -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
  }
@@ -46,7 +46,8 @@ class MultiStepAgent
46
46
  "confidence" => {
47
47
  "type" => "number",
48
48
  "minimum" => 0,
49
- "maximum" => 1
49
+ "maximum" => 1,
50
+ "description" => "Confidence level (0.0 to 1.0, where 1.0 is 100% confident)"
50
51
  },
51
52
  "next_steps" => {
52
53
  "type" => "array",
@@ -123,7 +124,8 @@ class MultiStepAgent
123
124
  if recent_actions.length == 3 && recent_actions.uniq.length == 1
124
125
  puts "⚠️ Detected repetitive actions - forcing workflow progression"
125
126
  # Force next phase
126
- if recent_actions.first == "collect"
127
+ case recent_actions.first
128
+ when "collect"
127
129
  puts " → Moving to analysis phase"
128
130
  decision["action"]["type"] = "analyze"
129
131
  decision["action"]["parameters"] = { "target" => "collected_data" }
@@ -133,7 +135,7 @@ class MultiStepAgent
133
135
  action: "analyze",
134
136
  result: result
135
137
  }
136
- elsif recent_actions.first == "analyze"
138
+ when "analyze"
137
139
  puts " → Moving to validation phase"
138
140
  decision["action"]["type"] = "validate"
139
141
  decision["action"]["parameters"] = { "type" => "results" }
@@ -143,7 +145,7 @@ class MultiStepAgent
143
145
  action: "validate",
144
146
  result: result
145
147
  }
146
- elsif recent_actions.first == "validate"
148
+ when "validate"
147
149
  puts " → Completing workflow"
148
150
  decision["action"]["type"] = "complete"
149
151
  result = execute_action(decision)
@@ -269,9 +271,7 @@ class MultiStepAgent
269
271
  when "collect"
270
272
  data_key = params["data_type"] || params["key"] || "user_data"
271
273
  # Prevent collecting the same generic data repeatedly
272
- if @state[:data_collected].key?(data_key) && data_key.match?(/^(missing|unknown|data)$/i) && !@state[:data_collected].key?("user_data")
273
- data_key = "user_data"
274
- end
274
+ data_key = "user_data" if should_collect_user_data?(data_key)
275
275
  puts " 📥 Collecting: #{data_key}"
276
276
  @state[:data_collected][data_key] = "collected_at_#{Time.now.to_i}"
277
277
  { status: "collected", key: data_key }
@@ -304,6 +304,12 @@ class MultiStepAgent
304
304
  end
305
305
  end
306
306
 
307
+ def should_collect_user_data?(data_key)
308
+ @state[:data_collected].key?(data_key) &&
309
+ data_key.match?(/^(missing|unknown|data)$/i) &&
310
+ !@state[:data_collected].key?("user_data")
311
+ end
312
+
307
313
  def display_summary
308
314
  puts "\n" + "=" * 60
309
315
  puts "Workflow Summary"
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/ollama_client"
5
+ require "tty-reader"
6
+ require "tty-screen"
7
+ require "tty-cursor"
8
+
9
+ def build_config
10
+ config = Ollama::Config.new
11
+ config.base_url = ENV["OLLAMA_BASE_URL"] if ENV["OLLAMA_BASE_URL"]
12
+ config.model = ENV["OLLAMA_MODEL"] if ENV["OLLAMA_MODEL"]
13
+ config.temperature = ENV["OLLAMA_TEMPERATURE"].to_f if ENV["OLLAMA_TEMPERATURE"]
14
+ config
15
+ end
16
+
17
+ def exit_command?(text)
18
+ %w[/exit /quit exit quit].include?(text.downcase)
19
+ end
20
+
21
+ def add_system_message(messages)
22
+ system_prompt = ENV.fetch("OLLAMA_SYSTEM", nil)
23
+ return unless system_prompt && !system_prompt.strip.empty?
24
+
25
+ messages << { role: "system", content: system_prompt }
26
+ end
27
+
28
+ def print_banner(config)
29
+ puts "Ollama chat console"
30
+ puts "Model: #{config.model}"
31
+ puts "Base URL: #{config.base_url}"
32
+ puts "Type /exit to quit."
33
+ puts "Screen: #{TTY::Screen.width}x#{TTY::Screen.height}"
34
+ puts
35
+ end
36
+
37
+ HISTORY_PATH = ".ollama_chat_history"
38
+ MAX_HISTORY = 200
39
+ COLOR_RESET = "\e[0m"
40
+ COLOR_USER = "\e[32m"
41
+ COLOR_LLM = "\e[36m"
42
+ USER_PROMPT = "#{COLOR_USER}you>#{COLOR_RESET} ".freeze
43
+ LLM_PROMPT = "#{COLOR_LLM}llm>#{COLOR_RESET} ".freeze
44
+
45
+ def build_reader
46
+ TTY::Reader.new
47
+ end
48
+
49
+ def read_input(reader)
50
+ reader.read_line(USER_PROMPT)
51
+ end
52
+
53
+ def load_history(reader, path)
54
+ history = load_history_list(path)
55
+ history.reverse_each { |line| reader.add_to_history(line) }
56
+ end
57
+
58
+ def load_history_list(path)
59
+ return [] unless File.exist?(path)
60
+
61
+ unique_history(normalize_history(File.readlines(path, chomp: true)))
62
+ end
63
+
64
+ def normalize_history(lines)
65
+ lines.map(&:strip).reject(&:empty?)
66
+ end
67
+
68
+ def unique_history(lines)
69
+ seen = {}
70
+ lines.each_with_object([]) do |line, unique|
71
+ next if seen[line]
72
+
73
+ unique << line
74
+ seen[line] = true
75
+ end
76
+ end
77
+
78
+ def update_history(path, text)
79
+ history = load_history_list(path)
80
+ history.delete(text)
81
+ history.unshift(text)
82
+ history = history.first(MAX_HISTORY)
83
+
84
+ File.write(path, history.join("\n") + (history.empty? ? "" : "\n"))
85
+ end
86
+
87
+ def chat_response(client, messages, config)
88
+ content = +""
89
+ prompt_printed = false
90
+
91
+ print "#{COLOR_LLM}...#{COLOR_RESET}"
92
+ $stdout.flush
93
+
94
+ client.chat_raw(
95
+ messages: messages,
96
+ allow_chat: true,
97
+ options: { temperature: config.temperature },
98
+ stream: true
99
+ ) do |chunk|
100
+ token = chunk.dig("message", "content").to_s
101
+ next if token.empty?
102
+
103
+ unless prompt_printed
104
+ print "\r#{LLM_PROMPT}"
105
+ prompt_printed = true
106
+ end
107
+
108
+ content << token
109
+ print token
110
+ $stdout.flush
111
+ end
112
+
113
+ puts
114
+ content
115
+ end
116
+
117
+ def run_console(client, config)
118
+ messages = []
119
+ add_system_message(messages)
120
+ print_banner(config)
121
+ reader = build_reader
122
+ load_history(reader, HISTORY_PATH)
123
+
124
+ loop do
125
+ input = read_input(reader)
126
+ break unless input
127
+
128
+ text = input.strip
129
+ next if text.empty?
130
+ break if exit_command?(text)
131
+
132
+ update_history(HISTORY_PATH, text)
133
+ messages << { role: "user", content: text }
134
+ content = chat_response(client, messages, config)
135
+ messages << { role: "assistant", content: content }
136
+ end
137
+ rescue Interrupt
138
+ puts "\nExiting..."
139
+ end
140
+
141
+ config = build_config
142
+ client = Ollama::Client.new(config: config)
143
+ run_console(client, config)
@@ -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: "Given this context: #{context}\n\nDecide the next action to take.",
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: "Analyze this data and provide insights: #{data}",
171
+ prompt: prompt,
162
172
  schema: @analysis_schema
163
173
  )
164
174