ollama_chat 0.0.58 → 0.0.59
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/CHANGES.md +44 -0
- data/README.md +1 -0
- data/Rakefile +1 -0
- data/lib/ollama_chat/chat.rb +27 -9
- data/lib/ollama_chat/follow_chat.rb +23 -1
- data/lib/ollama_chat/information.rb +1 -0
- data/lib/ollama_chat/ollama_chat_config/default_config.yml +7 -0
- data/lib/ollama_chat/tool_calling.rb +104 -0
- data/lib/ollama_chat/tools/cve.rb +92 -0
- data/lib/ollama_chat/tools/endoflife.rb +98 -0
- data/lib/ollama_chat/tools/weather/dwd_sensor.rb +203 -0
- data/lib/ollama_chat/tools/weather.rb +105 -0
- data/lib/ollama_chat/tools.rb +32 -0
- data/lib/ollama_chat/version.rb +1 -1
- data/lib/ollama_chat.rb +2 -0
- data/ollama_chat.gemspec +6 -5
- data/spec/ollama_chat/chat_spec.rb +17 -0
- data/spec/ollama_chat/tools/cve_spec.rb +71 -0
- data/spec/ollama_chat/tools/endoflife_spec.rb +72 -0
- data/spec/ollama_chat/tools/weather_spec.rb +59 -0
- metadata +33 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 98fa6fed72170e4aa21b04c8a66ba443942ed66cb9b2eb380708029a2fd592f5
|
|
4
|
+
data.tar.gz: 40ed71dcd2a5770289dc1d1e5e29f44679b436584b8ce24ed6fa4756c8c694f6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b64ad829de42272883cfdfad370cc2a34a97d4b68efe957019a05f07e8b6b8ff0aa1a3d80711427da117c58bfbb4d39265921bcfe228bbac7805a7c4fa1a8725
|
|
7
|
+
data.tar.gz: 3d91f987a19b7b387ca2f21edb7e0b3643c79e7f5c983c160d441a08415b47c5267e74f86f1bd30f3881c5001b4916fd9555d461f733b3f9d0278fb0e0b9a895
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-02-05 v0.0.59
|
|
4
|
+
|
|
5
|
+
### New Features
|
|
6
|
+
|
|
7
|
+
- **Dynamic Tool Management**: Added `/tools` command with subcommands `/tools
|
|
8
|
+
enable` and `/tools disable` for listing, enabling, or disabling tools
|
|
9
|
+
dynamically
|
|
10
|
+
|
|
11
|
+
- **Three Built-in Tools**:
|
|
12
|
+
- `get_current_weather` - Fetches real-time temperature from German Weather
|
|
13
|
+
Service (DWD)
|
|
14
|
+
- `get_cve` - Retrieves CVE details from MITRE API
|
|
15
|
+
- `get_endoflife` - Queries endoflife.date API for software lifecycle
|
|
16
|
+
information
|
|
17
|
+
|
|
18
|
+
### Technical Improvements
|
|
19
|
+
|
|
20
|
+
- **Tool Calling System**: Implemented comprehensive tool management using
|
|
21
|
+
`@enabled_tools` array
|
|
22
|
+
- **Configuration Support**: Added tool configurations in `default_config.yml`
|
|
23
|
+
- **Integration**: Enhanced `Ollama::Chat` with proper tool calling integration
|
|
24
|
+
and `Ollama::Tool` support
|
|
25
|
+
- **Error Handling**: Robust error handling for external API calls with proper
|
|
26
|
+
fallbacks
|
|
27
|
+
|
|
28
|
+
### Documentation & Testing
|
|
29
|
+
|
|
30
|
+
- Updated README with new tool management commands
|
|
31
|
+
- Added comprehensive RSpec tests for all three new tools
|
|
32
|
+
- Enhanced gemspec with updated file listings and dependencies
|
|
33
|
+
- Implemented caching for HTTP responses using
|
|
34
|
+
`OllamaChat::Utils::CacheFetcher`
|
|
35
|
+
|
|
36
|
+
### Code Structure
|
|
37
|
+
|
|
38
|
+
- **New Modules**: `OllamaChat::ToolCalling`, `OllamaChat::Tools::CVE`,
|
|
39
|
+
`OllamaChat::Tools::EndOfLife`, `OllamaChat::Tools::Weather`
|
|
40
|
+
- **Enhanced Classes**: `OllamaChat::Chat` with tool management and `DWDSensor`
|
|
41
|
+
for weather data retrieval
|
|
42
|
+
- **Dependency**: Added `rubyzip` for DWD data processing
|
|
43
|
+
|
|
44
|
+
This release transforms OllamaChat from a chat interface into a smart assistant
|
|
45
|
+
capable of executing external tools and accessing real-world data.
|
|
46
|
+
|
|
3
47
|
## 2026-02-02 v0.0.58
|
|
4
48
|
|
|
5
49
|
- Updated Redis image to version to valkey **9.0.1** in docker-compose.yml
|
data/README.md
CHANGED
|
@@ -187,6 +187,7 @@ The following commands can be given inside the chat, if prefixed by a `/`:
|
|
|
187
187
|
/revise_last edit the last response in an external editor
|
|
188
188
|
/output filename save last response to filename
|
|
189
189
|
/pipe command write last response to command's stdin
|
|
190
|
+
/tools [enable|disable] list enabled, enable, or disable tools
|
|
190
191
|
/vim insert the last message into a vim server
|
|
191
192
|
/quit to quit
|
|
192
193
|
/help to view this help
|
data/Rakefile
CHANGED
|
@@ -59,6 +59,7 @@ GemHadar do
|
|
|
59
59
|
dependency 'csv', '~> 3.0'
|
|
60
60
|
dependency 'const_conf', '~> 0.3'
|
|
61
61
|
dependency 'context_spook', '~> 1.5'
|
|
62
|
+
dependency 'rubyzip', '~> 3.0'
|
|
62
63
|
development_dependency 'all_images', '~> 0.6'
|
|
63
64
|
development_dependency 'rspec', '~> 3.2'
|
|
64
65
|
development_dependency 'kramdown', '~> 2.0'
|
data/lib/ollama_chat/chat.rb
CHANGED
|
@@ -53,6 +53,7 @@ class OllamaChat::Chat
|
|
|
53
53
|
include OllamaChat::Conversation
|
|
54
54
|
include OllamaChat::InputContent
|
|
55
55
|
include OllamaChat::MessageEditing
|
|
56
|
+
include OllamaChat::ToolCalling
|
|
56
57
|
|
|
57
58
|
# Initializes a new OllamaChat::Chat instance with the given command-line
|
|
58
59
|
# arguments.
|
|
@@ -112,6 +113,8 @@ class OllamaChat::Chat
|
|
|
112
113
|
@kramdown_ansi_styles = configure_kramdown_ansi_styles
|
|
113
114
|
init_chat_history
|
|
114
115
|
@opts[?S] and init_server_socket
|
|
116
|
+
@enabled_tools = []
|
|
117
|
+
@tool_call_results = {}
|
|
115
118
|
rescue ComplexConfig::AttributeMissing, ComplexConfig::ConfigurationSyntaxError => e
|
|
116
119
|
fix_config(e)
|
|
117
120
|
end
|
|
@@ -372,6 +375,16 @@ class OllamaChat::Chat
|
|
|
372
375
|
STDERR.puts "Warning: No message found to insert into Vim"
|
|
373
376
|
end
|
|
374
377
|
:next
|
|
378
|
+
when %r(^/tools(?:\s+(enable|disable))?$)
|
|
379
|
+
case $1
|
|
380
|
+
when nil
|
|
381
|
+
list_tools
|
|
382
|
+
when 'enable'
|
|
383
|
+
enable_tool
|
|
384
|
+
when 'disable'
|
|
385
|
+
disable_tool
|
|
386
|
+
end
|
|
387
|
+
:next
|
|
375
388
|
when %r(^/config$)
|
|
376
389
|
display_config
|
|
377
390
|
:next
|
|
@@ -563,16 +576,20 @@ class OllamaChat::Chat
|
|
|
563
576
|
type = :terminal_input
|
|
564
577
|
input_prompt = bold { color(172) { message_type(@images) + " user" } } + bold { "> " }
|
|
565
578
|
begin
|
|
566
|
-
content =
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
579
|
+
if content = handle_tool_call_results?
|
|
580
|
+
@parse_content = false
|
|
581
|
+
else
|
|
582
|
+
content = enable_command_completion do
|
|
583
|
+
if prefill_prompt = @prefill_prompt.full?
|
|
584
|
+
Reline.pre_input_hook = -> {
|
|
585
|
+
Reline.insert_text prefill_prompt.gsub(/\n*\z/, '')
|
|
586
|
+
@prefill_prompt = nil
|
|
587
|
+
}
|
|
588
|
+
else
|
|
589
|
+
Reline.pre_input_hook = nil
|
|
590
|
+
end
|
|
591
|
+
Reline.readline(input_prompt, true)&.chomp
|
|
574
592
|
end
|
|
575
|
-
Reline.readline(input_prompt, true)&.chomp
|
|
576
593
|
end
|
|
577
594
|
rescue Interrupt
|
|
578
595
|
if message = server_socket_message
|
|
@@ -635,6 +652,7 @@ class OllamaChat::Chat
|
|
|
635
652
|
options: @model_options,
|
|
636
653
|
stream: stream.on?,
|
|
637
654
|
think: ,
|
|
655
|
+
tools: ,
|
|
638
656
|
&handler
|
|
639
657
|
)
|
|
640
658
|
rescue Ollama::Errors::BadRequestError
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
#
|
|
10
10
|
# @example Processing a chat response
|
|
11
11
|
# follow_chat = OllamaChat::FollowChat.new(chat: chat_instance, messages: message_list)
|
|
12
|
-
# follow_chat.
|
|
12
|
+
# follow_chat.tool_call(response)
|
|
13
13
|
class OllamaChat::FollowChat
|
|
14
14
|
include Ollama
|
|
15
15
|
include Ollama::Handlers::Concern
|
|
@@ -80,11 +80,33 @@ class OllamaChat::FollowChat
|
|
|
80
80
|
|
|
81
81
|
output_eval_stats(response)
|
|
82
82
|
|
|
83
|
+
handle_tool_calls(response)
|
|
84
|
+
|
|
83
85
|
self
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
private
|
|
87
89
|
|
|
90
|
+
# The handle_tool_calls method processes tool calls from a response and
|
|
91
|
+
# executes them.
|
|
92
|
+
#
|
|
93
|
+
# This method checks if the response contains tool calls, and if so, iterates
|
|
94
|
+
# through each tool call to execute the corresponding tool from the
|
|
95
|
+
# registered tools. The results of the tool execution are stored in the
|
|
96
|
+
# chat's tool_call_results hash using the tool name as the key.
|
|
97
|
+
#
|
|
98
|
+
# @param response [Object] the response object containing tool calls to
|
|
99
|
+
# process
|
|
100
|
+
def handle_tool_calls(response)
|
|
101
|
+
return unless response.message.ask_and_send(:tool_calls)
|
|
102
|
+
|
|
103
|
+
response.message.tool_calls.each do |tool_call|
|
|
104
|
+
name = tool_call.function.name
|
|
105
|
+
@chat.tool_call_results[name] = OllamaChat::Tools.registered[name].
|
|
106
|
+
execute(tool_call, config: @chat.config)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
88
110
|
# The truncate_for_terminal method processes text to fit within a specified
|
|
89
111
|
# number of lines.
|
|
90
112
|
#
|
|
@@ -152,6 +152,7 @@ module OllamaChat::Information
|
|
|
152
152
|
/revise_last edit the last response in an external editor
|
|
153
153
|
/output filename save last response to filename
|
|
154
154
|
/pipe command write last response to command's stdin
|
|
155
|
+
/tools [enable|disable] list enabled, enable, or disable tools
|
|
155
156
|
/vim insert the last message into a vim server
|
|
156
157
|
/quit to quit
|
|
157
158
|
/help to view this help
|
|
@@ -84,3 +84,10 @@ web_search:
|
|
|
84
84
|
url: <%= OllamaChat::EnvConfig::OLLAMA::SEARXNG_URL %>
|
|
85
85
|
vim:
|
|
86
86
|
clientserver: socket
|
|
87
|
+
tools:
|
|
88
|
+
get_current_weather:
|
|
89
|
+
station_id: '00433'
|
|
90
|
+
get_cve:
|
|
91
|
+
url: 'https://cveawg.mitre.org/api/cve/%{cve_id}'
|
|
92
|
+
get_endoflife:
|
|
93
|
+
url: "https://endoflife.date/api/v1/products/%{product}"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# A module that provides tool calling functionality for OllamaChat.
|
|
2
|
+
#
|
|
3
|
+
# The ToolCalling module encapsulates methods for managing and processing tool
|
|
4
|
+
# calls within the chat application. It handles the registration and execution
|
|
5
|
+
# of tools that can be invoked during conversations, allowing the chat to
|
|
6
|
+
# interact with external systems or perform specialized tasks beyond simple
|
|
7
|
+
# text generation.
|
|
8
|
+
module OllamaChat::ToolCalling
|
|
9
|
+
# The tools reader returns the registered tools for the chat session.
|
|
10
|
+
#
|
|
11
|
+
# @return [ Hash ] a hash containing the registered tools
|
|
12
|
+
def tools
|
|
13
|
+
@enabled_tools.map { OllamaChat::Tools.registered[it]&.to_hash }.compact
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The configured_tools method returns an array of tool names configured for
|
|
17
|
+
# the chat session.
|
|
18
|
+
#
|
|
19
|
+
# This method retrieves the list of available tools from the configuration
|
|
20
|
+
# and returns them as a sorted array of strings. It handles cases where the
|
|
21
|
+
# tools configuration might be nil or empty by returning an empty array.
|
|
22
|
+
#
|
|
23
|
+
# @return [Array<String>] a sorted array of tool names configured for the
|
|
24
|
+
# chat session
|
|
25
|
+
def configured_tools
|
|
26
|
+
Array(config.tools&.attribute_names&.map(&:to_s)).sort
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The tool_call_results reader returns the tools' results for the
|
|
30
|
+
# chat session if any.
|
|
31
|
+
#
|
|
32
|
+
# @return [ Hash ] a hash containing the registered tool results
|
|
33
|
+
attr_reader :tool_call_results
|
|
34
|
+
|
|
35
|
+
# The list_tools method displays the sorted list of enabled tools.
|
|
36
|
+
#
|
|
37
|
+
# This method outputs to standard output the alphabetically sorted list of
|
|
38
|
+
# tool names that are currently enabled in the chat session.
|
|
39
|
+
def list_tools
|
|
40
|
+
puts @enabled_tools.sort
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# The enable_tool method allows the user to select and enable a tool from a
|
|
44
|
+
# list of available tools.
|
|
45
|
+
#
|
|
46
|
+
# This method presents a menu of tools that can be enabled, excluding those
|
|
47
|
+
# that are already enabled. It uses the chooser to display the available
|
|
48
|
+
# tools and handles the user's selection by adding the chosen tool to the
|
|
49
|
+
# list of enabled tools and sorting the list.
|
|
50
|
+
def enable_tool
|
|
51
|
+
select_tools = configured_tools - @enabled_tools
|
|
52
|
+
select_tools += [ '[EXIT]' ]
|
|
53
|
+
case chosen = OllamaChat::Utils::Chooser.choose(select_tools)
|
|
54
|
+
when '[EXIT]', nil
|
|
55
|
+
STDOUT.puts "Exiting chooser."
|
|
56
|
+
return
|
|
57
|
+
when *select_tools
|
|
58
|
+
@enabled_tools << chosen
|
|
59
|
+
@enabled_tools.sort!
|
|
60
|
+
puts "Enabled tool %s" % bold(chosen)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# The disable_tool method allows the user to select and disable a tool from a
|
|
65
|
+
# list of enabled tools.
|
|
66
|
+
#
|
|
67
|
+
# This method presents a menu of currently enabled tools to the user,
|
|
68
|
+
# allowing them to choose which tool to disable. It uses the chooser to
|
|
69
|
+
# display the available tools and handles the user's selection by removing
|
|
70
|
+
# the chosen tool from the list of enabled tools and sorting the list
|
|
71
|
+
# afterwards.
|
|
72
|
+
def disable_tool
|
|
73
|
+
select_tools = @enabled_tools
|
|
74
|
+
select_tools += [ '[EXIT]' ]
|
|
75
|
+
case chosen = OllamaChat::Utils::Chooser.choose(select_tools)
|
|
76
|
+
when '[EXIT]', nil
|
|
77
|
+
STDOUT.puts "Exiting chooser."
|
|
78
|
+
return
|
|
79
|
+
when *select_tools
|
|
80
|
+
@enabled_tools.delete chosen
|
|
81
|
+
puts "Disabled tool %s" % bold(chosen)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
# The handle_tool_call_results? method processes and returns results from
|
|
88
|
+
# tool calls.
|
|
89
|
+
#
|
|
90
|
+
# This method checks if there are any pending tool call results and formats
|
|
91
|
+
# them into a string message. It clears the tool call results after
|
|
92
|
+
# processing.
|
|
93
|
+
#
|
|
94
|
+
# @return [ String, nil ] a formatted string containing tool call results or
|
|
95
|
+
# nil if no results exist
|
|
96
|
+
def handle_tool_call_results?
|
|
97
|
+
@tool_call_results.present? or return
|
|
98
|
+
content = @tool_call_results.map do |name, result|
|
|
99
|
+
"Tool %s returned %s" % [ name, result ]
|
|
100
|
+
end.join(?\n)
|
|
101
|
+
@tool_call_results.clear
|
|
102
|
+
content
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# A tool for fetching CVE (Common Vulnerabilities and Exposures) information.
|
|
2
|
+
#
|
|
3
|
+
# This tool allows the chat client to retrieve CVE details by ID from a configured
|
|
4
|
+
# API endpoint. It integrates with the Ollama tool calling system to provide
|
|
5
|
+
# security-related information to the language model.
|
|
6
|
+
#
|
|
7
|
+
# @example Using the CVE tool
|
|
8
|
+
# # The tool can be invoked with a CVE ID
|
|
9
|
+
# { "name": "get_cve", "arguments": { "cve_id": "CVE-2023-12345" } }
|
|
10
|
+
#
|
|
11
|
+
# @see OllamaChat::Tools
|
|
12
|
+
class OllamaChat::Tools::CVE
|
|
13
|
+
include Ollama
|
|
14
|
+
|
|
15
|
+
# Initializes a new CVE tool instance.
|
|
16
|
+
#
|
|
17
|
+
# @return [OllamaChat::Tools::CVE] a new CVE tool instance
|
|
18
|
+
def initialize
|
|
19
|
+
@name = 'get_cve'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns the name of the tool.
|
|
23
|
+
#
|
|
24
|
+
# @return [String] the name of the tool ('get_cve')
|
|
25
|
+
attr_reader :name
|
|
26
|
+
|
|
27
|
+
# Creates and returns a tool definition for getting CVE information.
|
|
28
|
+
#
|
|
29
|
+
# This method constructs the function signature that describes what the tool
|
|
30
|
+
# does, its parameters, and required fields. The tool expects a CVE-ID
|
|
31
|
+
# parameter to be provided.
|
|
32
|
+
#
|
|
33
|
+
# @return [Ollama::Tool] a tool definition for retrieving CVE information
|
|
34
|
+
def tool
|
|
35
|
+
Tool.new(
|
|
36
|
+
type: 'function',
|
|
37
|
+
function: Tool::Function.new(
|
|
38
|
+
name:,
|
|
39
|
+
description: 'Get the CVE for id as JSON',
|
|
40
|
+
parameters: Tool::Function::Parameters.new(
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
cve_id: Tool::Function::Parameters::Property.new(
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'The CVE-ID to get'
|
|
46
|
+
),
|
|
47
|
+
},
|
|
48
|
+
required: %w[cve_id]
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Executes the CVE lookup operation.
|
|
55
|
+
#
|
|
56
|
+
# This method fetches CVE data from the configured API endpoint using the
|
|
57
|
+
# provided CVE ID. It handles the HTTP request, parses the JSON response,
|
|
58
|
+
# and returns the structured data.
|
|
59
|
+
#
|
|
60
|
+
# @param tool_call [Ollama::Tool::Call] the tool call object containing function details
|
|
61
|
+
# @param opts [Hash] additional options
|
|
62
|
+
# @option opts [ComplexConfig::Settings] :config the configuration object
|
|
63
|
+
# @return [Hash, String] the parsed CVE data as a hash or an error message
|
|
64
|
+
# @raise [StandardError] if there's an issue with the HTTP request or JSON parsing
|
|
65
|
+
def execute(tool_call, **opts)
|
|
66
|
+
config = opts[:config]
|
|
67
|
+
cve_id = tool_call.function.arguments.cve_id
|
|
68
|
+
url = config.tools.get_cve.url % { cve_id: }
|
|
69
|
+
OllamaChat::Utils::Fetcher.get(
|
|
70
|
+
url,
|
|
71
|
+
headers: {
|
|
72
|
+
'Accept' => 'application/json',
|
|
73
|
+
},
|
|
74
|
+
debug: OllamaChat::EnvConfig::OLLAMA::CHAT::DEBUG
|
|
75
|
+
) do |tmp|
|
|
76
|
+
data = JSON.parse(tmp.read, object_class: JSON::GenericObject)
|
|
77
|
+
return data
|
|
78
|
+
end
|
|
79
|
+
rescue StandardError => e
|
|
80
|
+
"Failed to fetch CVE for #{cve_id} #{e.class}: #{e.message}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Converts the tool to a hash representation.
|
|
84
|
+
#
|
|
85
|
+
# This method provides a standardized way to serialize the tool definition
|
|
86
|
+
# for use in tool calling systems.
|
|
87
|
+
#
|
|
88
|
+
# @return [Hash] a hash representation of the tool
|
|
89
|
+
def to_hash
|
|
90
|
+
tool.to_hash
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# A tool for fetching endoflife.date product information.
|
|
2
|
+
#
|
|
3
|
+
# This tool allows the chat client to retrieve endoflife.date information
|
|
4
|
+
# for software products by ID. It integrates with the Ollama tool calling
|
|
5
|
+
# system to provide lifecycle and support information to the language model.
|
|
6
|
+
#
|
|
7
|
+
# @example Using the endoflife tool
|
|
8
|
+
# # The tool can be invoked with a product name
|
|
9
|
+
# { "name": "get_endoflife", "arguments": { "product": "mysql" } }
|
|
10
|
+
#
|
|
11
|
+
# @see OllamaChat::Tools
|
|
12
|
+
class OllamaChat::Tools::EndOfLife
|
|
13
|
+
include Ollama
|
|
14
|
+
|
|
15
|
+
# Initializes a new endoflife tool instance.
|
|
16
|
+
#
|
|
17
|
+
# @return [OllamaChat::Tools::EndOfLife] a new endoflife tool instance
|
|
18
|
+
def initialize
|
|
19
|
+
@name = 'get_endoflife'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns the name of the tool.
|
|
23
|
+
#
|
|
24
|
+
# @return [String] the name of the tool ('get_endoflife')
|
|
25
|
+
attr_reader :name
|
|
26
|
+
|
|
27
|
+
# Creates and returns a tool definition for getting endoflife information.
|
|
28
|
+
#
|
|
29
|
+
# This method constructs the function signature that describes what the tool
|
|
30
|
+
# does, its parameters, and required fields. The tool expects a product name
|
|
31
|
+
# parameter to be provided.
|
|
32
|
+
#
|
|
33
|
+
# @return [Ollama::Tool] a tool definition for retrieving endoflife information
|
|
34
|
+
def tool
|
|
35
|
+
Tool.new(
|
|
36
|
+
type: 'function',
|
|
37
|
+
function: Tool::Function.new(
|
|
38
|
+
name:,
|
|
39
|
+
description: 'Get the endoflife information for a product as JSON',
|
|
40
|
+
parameters: Tool::Function::Parameters.new(
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
product: Tool::Function::Parameters::Property.new(
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'The product name to get endoflife information for'
|
|
46
|
+
),
|
|
47
|
+
},
|
|
48
|
+
required: %w[product]
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Executes the endoflife lookup operation.
|
|
55
|
+
#
|
|
56
|
+
# This method fetches endoflife data from the endoflife.date API using the
|
|
57
|
+
# provided product name. It handles the HTTP request, parses the JSON response,
|
|
58
|
+
# and returns the structured data.
|
|
59
|
+
#
|
|
60
|
+
# @param tool_call [Ollama::Tool::Call] the tool call object containing function details
|
|
61
|
+
# @param opts [Hash] additional options
|
|
62
|
+
# @option opts [ComplexConfig::Settings] :config the configuration object
|
|
63
|
+
# @return [Hash, String] the parsed endoflife data as a hash or an error message
|
|
64
|
+
# @raise [StandardError] if there's an issue with the HTTP request or JSON parsing
|
|
65
|
+
def execute(tool_call, **opts)
|
|
66
|
+
config = opts[:config]
|
|
67
|
+
product = tool_call.function.arguments.product
|
|
68
|
+
|
|
69
|
+
# Construct the URL for the endoflife API
|
|
70
|
+
url = config.tools.get_endoflife.url % { product: }
|
|
71
|
+
|
|
72
|
+
# Fetch the data from endoflife.date API
|
|
73
|
+
OllamaChat::Utils::Fetcher.get(
|
|
74
|
+
url,
|
|
75
|
+
headers: {
|
|
76
|
+
'Accept' => 'application/json',
|
|
77
|
+
'User-Agent' => OllamaChat::Chat.user_agent
|
|
78
|
+
},
|
|
79
|
+
debug: OllamaChat::EnvConfig::OLLAMA::CHAT::DEBUG
|
|
80
|
+
) do |tmp|
|
|
81
|
+
# Parse the JSON response
|
|
82
|
+
data = JSON.parse(tmp.read, object_class: JSON::GenericObject)
|
|
83
|
+
return data
|
|
84
|
+
end
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
"Failed to fetch endoflife data for #{product}: #{e.class}: #{e.message}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Converts the tool to a hash representation.
|
|
90
|
+
#
|
|
91
|
+
# This method provides a standardized way to serialize the tool definition
|
|
92
|
+
# for use in tool calling systems.
|
|
93
|
+
#
|
|
94
|
+
# @return [Hash] a hash representation of the tool
|
|
95
|
+
def to_hash
|
|
96
|
+
tool.to_hash
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'uri'
|
|
3
|
+
require 'zip'
|
|
4
|
+
|
|
5
|
+
# A sensor implementation that fetches real-time temperature data from the
|
|
6
|
+
# German Weather Service (DWD). This sensor connects to DWD's open data API to
|
|
7
|
+
# retrieve the latest air temperature measurements for a specified weather
|
|
8
|
+
# station.
|
|
9
|
+
#
|
|
10
|
+
# The sensor expects DWD data files in ZIP format containing CSV data with the
|
|
11
|
+
# following structure:
|
|
12
|
+
# - Files are named like: 10minutenwerte_TU_00433_now.zip
|
|
13
|
+
# - Data is stored in CSV format with semicolon separators
|
|
14
|
+
# - Temperature values are in the TT_10 column (in Celsius)
|
|
15
|
+
# - Timestamps are in MESS_DATUM column (format: YYYYMMDDHHMM)
|
|
16
|
+
#
|
|
17
|
+
# Example usage:
|
|
18
|
+
# sensor = DWDSensor.new(sensor_id: 'station_1', station_id: '00433')
|
|
19
|
+
# temperature = sensor.measure
|
|
20
|
+
class DWDSensor
|
|
21
|
+
DEFAULT_URL_TEMPLATE = "https://opendata.dwd.de/climate_environment/CDC/observations_germany/climate/10_minutes/air_temperature/now/10minutenwerte_TU_%{station_id}_now.zip" # Template for the download URL
|
|
22
|
+
|
|
23
|
+
# Initializes a new sensor reader instance with the specified parameters.
|
|
24
|
+
#
|
|
25
|
+
# @param sensor_id [ String ] the unique identifier for the sensor
|
|
26
|
+
# @param station_id [ String ] the unique identifier for the weather station
|
|
27
|
+
# @param url_template [ String ] the URL template for fetching sensor data
|
|
28
|
+
# @param logger [ Logger ] the logger instance to use for logging messages
|
|
29
|
+
def initialize(sensor_id:, station_id:, url_template: DEFAULT_URL_TEMPLATE, logger: Logger.new($stderr))
|
|
30
|
+
@sensor_id = sensor_id
|
|
31
|
+
@station_id = station_id
|
|
32
|
+
@url_template = url_template
|
|
33
|
+
@last_modified = nil
|
|
34
|
+
@logger = logger
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# The sensor_id method provides read-only access to the identifier of the
|
|
38
|
+
# sensor.
|
|
39
|
+
#
|
|
40
|
+
# @attr_reader [ String, Integer ] the unique identifier assigned to the sensor
|
|
41
|
+
# instance
|
|
42
|
+
attr_reader :sensor_id
|
|
43
|
+
|
|
44
|
+
# The measure method reads temperature data from a sensor and returns
|
|
45
|
+
# timestamped measurements.
|
|
46
|
+
#
|
|
47
|
+
# @return [ Array<Time, Float>, nil ] an array containing the timestamp and temperature reading,
|
|
48
|
+
# or nil if no valid temperature could be read
|
|
49
|
+
# @return [ nil ] if no temperature reading was available
|
|
50
|
+
def measure
|
|
51
|
+
@logger.info "Starting to read temperature from #{self.class} #{@sensor_id}…"
|
|
52
|
+
time, temp = read_temperature
|
|
53
|
+
if time && temp
|
|
54
|
+
@logger.info "Read temperature from #{self.class} #{@sensor_id} at #{time.iso8601}: #{temp}℃ "
|
|
55
|
+
[ time, temp ]
|
|
56
|
+
else
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# Reads the temperature data for the station, skipping the fetch if data is
|
|
64
|
+
# still current.
|
|
65
|
+
#
|
|
66
|
+
# This method first checks if the data fetching should be skipped based on
|
|
67
|
+
# the last modified timestamp comparison. If skipping is not appropriate, it
|
|
68
|
+
# proceeds to fetch the latest temperature data from DWD API.
|
|
69
|
+
#
|
|
70
|
+
# @return [nil] if the data fetching was skipped or failed
|
|
71
|
+
# @return [Array<Time, Float>] the result of the temperature fetching operation
|
|
72
|
+
# if successful
|
|
73
|
+
def read_temperature
|
|
74
|
+
if skip_fetching?
|
|
75
|
+
@logger.info "Data for station #{@station_id} still current from #{@last_modified.iso8601} => Skip fetching."
|
|
76
|
+
return
|
|
77
|
+
end
|
|
78
|
+
fetch_latest_temperature_from_dwd
|
|
79
|
+
rescue => e
|
|
80
|
+
@logger.error "Failed to fetch DWD data for station #{@station_id} => #{e.class}: #{e}"
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns a URI object constructed from the URL template and station ID.
|
|
85
|
+
#
|
|
86
|
+
# @return [URI] a URI object created by interpolating the station ID into the URL template
|
|
87
|
+
def uri
|
|
88
|
+
URI(@url_template % { station_id: @station_id })
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Determines whether fetching should be skipped based on modification time
|
|
92
|
+
# comparisons.
|
|
93
|
+
#
|
|
94
|
+
# @return [ Boolean ] true if the resource has not been modified since last
|
|
95
|
+
# fetch, false otherwise
|
|
96
|
+
# @return [ Boolean ] false if no previous modification time is available
|
|
97
|
+
def skip_fetching?
|
|
98
|
+
@last_modified or return false
|
|
99
|
+
response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
100
|
+
http.head(uri.path)
|
|
101
|
+
end
|
|
102
|
+
last_modified = Time.parse(response['Last-Modified'])
|
|
103
|
+
if last_modified > @last_modified
|
|
104
|
+
@last_modified = nil
|
|
105
|
+
false
|
|
106
|
+
else
|
|
107
|
+
true
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# Fetches the latest temperature data from DWD API for the configured
|
|
113
|
+
# station.
|
|
114
|
+
#
|
|
115
|
+
# This method performs an HTTP GET request to the DWD open data API endpoint,
|
|
116
|
+
# retrieves the ZIP file containing weather data, and processes it to extract
|
|
117
|
+
# the most recent temperature reading. It handles various HTTP status codes
|
|
118
|
+
# and network errors gracefully.
|
|
119
|
+
#
|
|
120
|
+
# @return [Array<Time, Float>, nil] An array containing the timestamp and
|
|
121
|
+
# temperature value of the measurement, or nil if the fetch or processing
|
|
122
|
+
# fails
|
|
123
|
+
def fetch_latest_temperature_from_dwd
|
|
124
|
+
@logger.debug "Fetching DWD data from: #{uri}"
|
|
125
|
+
response = Net::HTTP.get_response(uri)
|
|
126
|
+
if response.code == '200'
|
|
127
|
+
@logger.info "Successfully fetched data for station #{@station_id}"
|
|
128
|
+
|
|
129
|
+
result = extract_from_zip(response.body)
|
|
130
|
+
@last_modified = Time.parse(response['Last-Modified'])
|
|
131
|
+
result
|
|
132
|
+
elsif response.code == '404'
|
|
133
|
+
@logger.error "File not found for station #{@station_id}: #{uri}"
|
|
134
|
+
nil
|
|
135
|
+
else
|
|
136
|
+
@logger.error "HTTP Error #{response.code}: #{response.message}"
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
139
|
+
rescue => e
|
|
140
|
+
@logger.error "Network error fetching DWD data => #{e.class}: #{e}"
|
|
141
|
+
nil
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Extracts temperature data from a ZIP file containing DWD CSV data.
|
|
145
|
+
#
|
|
146
|
+
# This method takes raw ZIP file data, extracts the first entry (expected to
|
|
147
|
+
# be the CSV weather data), and processes it to return the latest temperature
|
|
148
|
+
# reading. The ZIP file is expected to contain CSV data with semicolon
|
|
149
|
+
# separators.
|
|
150
|
+
#
|
|
151
|
+
# @param zip_body [String] The raw binary content of the ZIP file
|
|
152
|
+
# @return [Array<Time, Float>, nil] An array containing the timestamp and temperature value,
|
|
153
|
+
# or nil if extraction or parsing fails
|
|
154
|
+
def extract_from_zip(zip_body)
|
|
155
|
+
# Create StringIO from response body
|
|
156
|
+
zip_data = StringIO.new(zip_body)
|
|
157
|
+
|
|
158
|
+
# Extract the first entry from ZIP file (should be the CSV data)
|
|
159
|
+
Zip::File.open_buffer(zip_data) do |zip_file|
|
|
160
|
+
entry = zip_file.first
|
|
161
|
+
if entry
|
|
162
|
+
content = entry.get_input_stream.read
|
|
163
|
+
@logger.debug "Extracted #{content.length} bytes from ZIP"
|
|
164
|
+
return parse_dwd_data(content)
|
|
165
|
+
else
|
|
166
|
+
@logger.error "No entries found in ZIP file for station #{@station_id}"
|
|
167
|
+
nil
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
rescue => e
|
|
171
|
+
@logger.error "Error extracting from ZIP: #{e}"
|
|
172
|
+
nil
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Parses DWD CSV data to extract the latest temperature reading for a weather
|
|
176
|
+
# station.
|
|
177
|
+
#
|
|
178
|
+
# @param data [String] The raw CSV data string from DWD containing weather measurements
|
|
179
|
+
# @return [Array<Time, Float>, nil] An array containing the timestamp and temperature value,
|
|
180
|
+
# or nil if no valid temperature data is found or if there are no data entries
|
|
181
|
+
def parse_dwd_data(data)
|
|
182
|
+
# Parse CSV data from DWD
|
|
183
|
+
csv_data = CSV.parse(data, headers: true, col_sep: ?;)
|
|
184
|
+
|
|
185
|
+
# Find the latest entry (most recent timestamp)
|
|
186
|
+
latest_entry, time = csv_data.
|
|
187
|
+
map { |row| [ row, Time.strptime(row['MESS_DATUM'], '%Y%m%d%H%M') ] }.
|
|
188
|
+
max_by(&:last)
|
|
189
|
+
|
|
190
|
+
if latest_entry
|
|
191
|
+
temp = latest_entry['TT_10']
|
|
192
|
+
if temp && temp != '9999' # 9999 indicates missing data
|
|
193
|
+
[ time, temp.to_f ]
|
|
194
|
+
else
|
|
195
|
+
@logger.warn "No valid temperature data found for station #{@station_id}"
|
|
196
|
+
nil
|
|
197
|
+
end
|
|
198
|
+
else
|
|
199
|
+
@logger.warn "No data entries found for station #{@station_id}"
|
|
200
|
+
nil
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require_relative 'weather/dwd_sensor'
|
|
2
|
+
|
|
3
|
+
# A module that provides tool registration and management for OllamaChat.
|
|
4
|
+
#
|
|
5
|
+
# The Tools module serves as a registry for available tools that can be
|
|
6
|
+
# invoked during chat conversations. It maintains a collection of
|
|
7
|
+
# registered tools and provides methods for registering new tools and
|
|
8
|
+
# accessing the complete set of available tools for use in chat
|
|
9
|
+
# interactions.
|
|
10
|
+
class OllamaChat::Tools::Weather
|
|
11
|
+
include Ollama
|
|
12
|
+
|
|
13
|
+
# The initialize method sets up the weather tool with its name.
|
|
14
|
+
def initialize
|
|
15
|
+
@name = 'get_current_weather'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The name reader returns the name of the tool.
|
|
19
|
+
#
|
|
20
|
+
# @return [ String ] the name of the tool
|
|
21
|
+
attr_reader :name
|
|
22
|
+
|
|
23
|
+
# The tool method creates and returns a tool definition for getting
|
|
24
|
+
# current weather information.
|
|
25
|
+
#
|
|
26
|
+
# This method constructs a tool specification that can be used to invoke
|
|
27
|
+
# a weather information service. The tool definition includes the
|
|
28
|
+
# function name, description, and parameter specifications for location
|
|
29
|
+
# and temperature unit.
|
|
30
|
+
#
|
|
31
|
+
# @return [Ollama::Tool] a tool definition for retrieving current weather
|
|
32
|
+
# information
|
|
33
|
+
def tool
|
|
34
|
+
Tool.new(
|
|
35
|
+
type: 'function',
|
|
36
|
+
function: Tool::Function.new(
|
|
37
|
+
name:,
|
|
38
|
+
description: 'Get the current weather for a location',
|
|
39
|
+
parameters: Tool::Function::Parameters.new(
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
location: Tool::Function::Parameters::Property.new(
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'The location to get the weather for, e.g. San Francisco, CA'
|
|
45
|
+
),
|
|
46
|
+
temperature_unit: Tool::Function::Parameters::Property.new(
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: "The unit to return the temperature in, either 'celsius' or 'fahrenheit'",
|
|
49
|
+
enum: %w[celsius fahrenheit]
|
|
50
|
+
)
|
|
51
|
+
},
|
|
52
|
+
required: %w[location temperature_unit]
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Executes a tool call to get current weather information.
|
|
59
|
+
#
|
|
60
|
+
# This method retrieves temperature data from a weather sensor using the
|
|
61
|
+
# DWD (German Weather Service) API and formats the result into a
|
|
62
|
+
# human-readable string including the temperature value, unit, and
|
|
63
|
+
# timestamp.
|
|
64
|
+
#
|
|
65
|
+
# @param tool_call [Object] the tool call object containing function
|
|
66
|
+
# details
|
|
67
|
+
# @param opts [Hash] additional options
|
|
68
|
+
# @option opts [ComplexConfig::Settings] :config the configuration object
|
|
69
|
+
#
|
|
70
|
+
# @return [String] a formatted weather report or error message
|
|
71
|
+
# @return [String] an error message if the weather data could not be
|
|
72
|
+
# retrieved
|
|
73
|
+
def execute(tool_call, **opts)
|
|
74
|
+
station_id = opts[:config].tools.get_current_weather.station_id
|
|
75
|
+
sensor = DWDSensor.new(
|
|
76
|
+
sensor_id: "dwd_#{station_id}",
|
|
77
|
+
station_id: ,
|
|
78
|
+
logger: Logger.new(STDOUT)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
time, temp = sensor.measure
|
|
82
|
+
|
|
83
|
+
unless time && temp
|
|
84
|
+
return "Could not retrieve temperature for station #{station_id}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
unit = ?℃
|
|
88
|
+
|
|
89
|
+
if tool_call.function.arguments.temperature_unit == 'fahrenheit'
|
|
90
|
+
unit = ?℉
|
|
91
|
+
temp = temp * 9.0 / 5 + 32
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
"The temperature was %s %s at the time of %s" % [ temp, unit, time ]
|
|
95
|
+
rescue StandardError => e
|
|
96
|
+
"Failed to fetch weather for station #{station_id} #{e.class}: #{e.message}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The to_hash method converts the tool to a hash representation.
|
|
100
|
+
#
|
|
101
|
+
# @return [ Hash ] a hash representation of the tool
|
|
102
|
+
def to_hash
|
|
103
|
+
tool.to_hash
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# A module that provides tool registration and management for OllamaChat.
|
|
2
|
+
#
|
|
3
|
+
# The Tools module serves as a registry for available tools that can be invoked
|
|
4
|
+
# during chat conversations. It maintains a collection of registered tools and
|
|
5
|
+
# provides methods for registering new tools and accessing the complete set of
|
|
6
|
+
# available tools for use in chat interactions.
|
|
7
|
+
module OllamaChat::Tools
|
|
8
|
+
class << self
|
|
9
|
+
# The registered attribute reader
|
|
10
|
+
#
|
|
11
|
+
# @return [ Hash ] the registered tools hash containing all available tools
|
|
12
|
+
attr_accessor :registered
|
|
13
|
+
|
|
14
|
+
# The register method adds a tool to the registry.
|
|
15
|
+
#
|
|
16
|
+
# @param tool [Object] the tool to be registered
|
|
17
|
+
#
|
|
18
|
+
# @return [Class] the class itself
|
|
19
|
+
def register(tool)
|
|
20
|
+
registered[tool.name] = tool
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
self.registered = {}
|
|
26
|
+
end
|
|
27
|
+
require 'ollama_chat/tools/weather'
|
|
28
|
+
OllamaChat::Tools.register OllamaChat::Tools::Weather.new
|
|
29
|
+
require 'ollama_chat/tools/cve'
|
|
30
|
+
OllamaChat::Tools.register OllamaChat::Tools::CVE.new
|
|
31
|
+
require 'ollama_chat/tools/endoflife'
|
|
32
|
+
OllamaChat::Tools.register OllamaChat::Tools::EndOfLife.new
|
data/lib/ollama_chat/version.rb
CHANGED
data/lib/ollama_chat.rb
CHANGED
data/ollama_chat.gemspec
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: ollama_chat 0.0.
|
|
2
|
+
# stub: ollama_chat 0.0.59 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "ollama_chat".freeze
|
|
6
|
-
s.version = "0.0.
|
|
6
|
+
s.version = "0.0.59".freeze
|
|
7
7
|
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
|
9
9
|
s.require_paths = ["lib".freeze]
|
|
@@ -12,15 +12,15 @@ Gem::Specification.new do |s|
|
|
|
12
12
|
s.description = "The app provides a command-line interface (CLI) to an Ollama AI model,\nallowing users to engage in text-based conversations and generate\nhuman-like responses. Users can import data from local files or web pages,\nwhich are then processed through three different modes: fully importing the\ncontent into the conversation context, summarizing the information for\nconcise reference, or storing it in an embedding vector database for later\nretrieval based on the conversation.\n".freeze
|
|
13
13
|
s.email = "flori@ping.de".freeze
|
|
14
14
|
s.executables = ["ollama_chat".freeze, "ollama_chat_send".freeze]
|
|
15
|
-
s.extra_rdoc_files = ["README.md".freeze, "lib/ollama_chat.rb".freeze, "lib/ollama_chat/chat.rb".freeze, "lib/ollama_chat/clipboard.rb".freeze, "lib/ollama_chat/conversation.rb".freeze, "lib/ollama_chat/dialog.rb".freeze, "lib/ollama_chat/document_cache.rb".freeze, "lib/ollama_chat/env_config.rb".freeze, "lib/ollama_chat/follow_chat.rb".freeze, "lib/ollama_chat/history.rb".freeze, "lib/ollama_chat/information.rb".freeze, "lib/ollama_chat/input_content.rb".freeze, "lib/ollama_chat/kramdown_ansi.rb".freeze, "lib/ollama_chat/message_editing.rb".freeze, "lib/ollama_chat/message_format.rb".freeze, "lib/ollama_chat/message_list.rb".freeze, "lib/ollama_chat/message_output.rb".freeze, "lib/ollama_chat/model_handling.rb".freeze, "lib/ollama_chat/ollama_chat_config.rb".freeze, "lib/ollama_chat/parsing.rb".freeze, "lib/ollama_chat/redis_cache.rb".freeze, "lib/ollama_chat/server_socket.rb".freeze, "lib/ollama_chat/source_fetching.rb".freeze, "lib/ollama_chat/state_selectors.rb".freeze, "lib/ollama_chat/switches.rb".freeze, "lib/ollama_chat/think_control.rb".freeze, "lib/ollama_chat/utils.rb".freeze, "lib/ollama_chat/utils/cache_fetcher.rb".freeze, "lib/ollama_chat/utils/chooser.rb".freeze, "lib/ollama_chat/utils/fetcher.rb".freeze, "lib/ollama_chat/utils/file_argument.rb".freeze, "lib/ollama_chat/version.rb".freeze, "lib/ollama_chat/vim.rb".freeze, "lib/ollama_chat/web_searching.rb".freeze]
|
|
16
|
-
s.files = [".utilsrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_chat_send".freeze, "config/searxng/settings.yml".freeze, "docker-compose.yml".freeze, "lib/ollama_chat.rb".freeze, "lib/ollama_chat/chat.rb".freeze, "lib/ollama_chat/clipboard.rb".freeze, "lib/ollama_chat/conversation.rb".freeze, "lib/ollama_chat/dialog.rb".freeze, "lib/ollama_chat/document_cache.rb".freeze, "lib/ollama_chat/env_config.rb".freeze, "lib/ollama_chat/follow_chat.rb".freeze, "lib/ollama_chat/history.rb".freeze, "lib/ollama_chat/information.rb".freeze, "lib/ollama_chat/input_content.rb".freeze, "lib/ollama_chat/kramdown_ansi.rb".freeze, "lib/ollama_chat/message_editing.rb".freeze, "lib/ollama_chat/message_format.rb".freeze, "lib/ollama_chat/message_list.rb".freeze, "lib/ollama_chat/message_output.rb".freeze, "lib/ollama_chat/model_handling.rb".freeze, "lib/ollama_chat/ollama_chat_config.rb".freeze, "lib/ollama_chat/ollama_chat_config/default_config.yml".freeze, "lib/ollama_chat/parsing.rb".freeze, "lib/ollama_chat/redis_cache.rb".freeze, "lib/ollama_chat/server_socket.rb".freeze, "lib/ollama_chat/source_fetching.rb".freeze, "lib/ollama_chat/state_selectors.rb".freeze, "lib/ollama_chat/switches.rb".freeze, "lib/ollama_chat/think_control.rb".freeze, "lib/ollama_chat/utils.rb".freeze, "lib/ollama_chat/utils/cache_fetcher.rb".freeze, "lib/ollama_chat/utils/chooser.rb".freeze, "lib/ollama_chat/utils/fetcher.rb".freeze, "lib/ollama_chat/utils/file_argument.rb".freeze, "lib/ollama_chat/version.rb".freeze, "lib/ollama_chat/vim.rb".freeze, "lib/ollama_chat/web_searching.rb".freeze, "ollama_chat.gemspec".freeze, "redis/redis.conf".freeze, "spec/assets/api_show.json".freeze, "spec/assets/api_tags.json".freeze, "spec/assets/api_version.json".freeze, "spec/assets/conversation.json".freeze, "spec/assets/duckduckgo.html".freeze, "spec/assets/example.atom".freeze, "spec/assets/example.csv".freeze, "spec/assets/example.html".freeze, "spec/assets/example.pdf".freeze, "spec/assets/example.ps".freeze, "spec/assets/example.rb".freeze, "spec/assets/example.rss".freeze, "spec/assets/example.xml".freeze, "spec/assets/example_with_quote.html".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/prompt.txt".freeze, "spec/assets/searxng.json".freeze, "spec/ollama_chat/chat_spec.rb".freeze, "spec/ollama_chat/clipboard_spec.rb".freeze, "spec/ollama_chat/follow_chat_spec.rb".freeze, "spec/ollama_chat/information_spec.rb".freeze, "spec/ollama_chat/input_content_spec.rb".freeze, "spec/ollama_chat/kramdown_ansi_spec.rb".freeze, "spec/ollama_chat/message_editing_spec.rb".freeze, "spec/ollama_chat/message_list_spec.rb".freeze, "spec/ollama_chat/message_output_spec.rb".freeze, "spec/ollama_chat/model_handling_spec.rb".freeze, "spec/ollama_chat/parsing_spec.rb".freeze, "spec/ollama_chat/redis_cache_spec.rb".freeze, "spec/ollama_chat/server_socket_spec.rb".freeze, "spec/ollama_chat/source_fetching_spec.rb".freeze, "spec/ollama_chat/state_selectors_spec.rb".freeze, "spec/ollama_chat/switches_spec.rb".freeze, "spec/ollama_chat/think_control_spec.rb".freeze, "spec/ollama_chat/utils/cache_fetcher_spec.rb".freeze, "spec/ollama_chat/utils/fetcher_spec.rb".freeze, "spec/ollama_chat/utils/file_argument_spec.rb".freeze, "spec/ollama_chat/vim_spec.rb".freeze, "spec/ollama_chat/web_searching_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
|
|
15
|
+
s.extra_rdoc_files = ["README.md".freeze, "lib/ollama_chat.rb".freeze, "lib/ollama_chat/chat.rb".freeze, "lib/ollama_chat/clipboard.rb".freeze, "lib/ollama_chat/conversation.rb".freeze, "lib/ollama_chat/dialog.rb".freeze, "lib/ollama_chat/document_cache.rb".freeze, "lib/ollama_chat/env_config.rb".freeze, "lib/ollama_chat/follow_chat.rb".freeze, "lib/ollama_chat/history.rb".freeze, "lib/ollama_chat/information.rb".freeze, "lib/ollama_chat/input_content.rb".freeze, "lib/ollama_chat/kramdown_ansi.rb".freeze, "lib/ollama_chat/message_editing.rb".freeze, "lib/ollama_chat/message_format.rb".freeze, "lib/ollama_chat/message_list.rb".freeze, "lib/ollama_chat/message_output.rb".freeze, "lib/ollama_chat/model_handling.rb".freeze, "lib/ollama_chat/ollama_chat_config.rb".freeze, "lib/ollama_chat/parsing.rb".freeze, "lib/ollama_chat/redis_cache.rb".freeze, "lib/ollama_chat/server_socket.rb".freeze, "lib/ollama_chat/source_fetching.rb".freeze, "lib/ollama_chat/state_selectors.rb".freeze, "lib/ollama_chat/switches.rb".freeze, "lib/ollama_chat/think_control.rb".freeze, "lib/ollama_chat/tool_calling.rb".freeze, "lib/ollama_chat/tools.rb".freeze, "lib/ollama_chat/tools/cve.rb".freeze, "lib/ollama_chat/tools/endoflife.rb".freeze, "lib/ollama_chat/tools/weather.rb".freeze, "lib/ollama_chat/tools/weather/dwd_sensor.rb".freeze, "lib/ollama_chat/utils.rb".freeze, "lib/ollama_chat/utils/cache_fetcher.rb".freeze, "lib/ollama_chat/utils/chooser.rb".freeze, "lib/ollama_chat/utils/fetcher.rb".freeze, "lib/ollama_chat/utils/file_argument.rb".freeze, "lib/ollama_chat/version.rb".freeze, "lib/ollama_chat/vim.rb".freeze, "lib/ollama_chat/web_searching.rb".freeze]
|
|
16
|
+
s.files = [".utilsrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_chat_send".freeze, "config/searxng/settings.yml".freeze, "docker-compose.yml".freeze, "lib/ollama_chat.rb".freeze, "lib/ollama_chat/chat.rb".freeze, "lib/ollama_chat/clipboard.rb".freeze, "lib/ollama_chat/conversation.rb".freeze, "lib/ollama_chat/dialog.rb".freeze, "lib/ollama_chat/document_cache.rb".freeze, "lib/ollama_chat/env_config.rb".freeze, "lib/ollama_chat/follow_chat.rb".freeze, "lib/ollama_chat/history.rb".freeze, "lib/ollama_chat/information.rb".freeze, "lib/ollama_chat/input_content.rb".freeze, "lib/ollama_chat/kramdown_ansi.rb".freeze, "lib/ollama_chat/message_editing.rb".freeze, "lib/ollama_chat/message_format.rb".freeze, "lib/ollama_chat/message_list.rb".freeze, "lib/ollama_chat/message_output.rb".freeze, "lib/ollama_chat/model_handling.rb".freeze, "lib/ollama_chat/ollama_chat_config.rb".freeze, "lib/ollama_chat/ollama_chat_config/default_config.yml".freeze, "lib/ollama_chat/parsing.rb".freeze, "lib/ollama_chat/redis_cache.rb".freeze, "lib/ollama_chat/server_socket.rb".freeze, "lib/ollama_chat/source_fetching.rb".freeze, "lib/ollama_chat/state_selectors.rb".freeze, "lib/ollama_chat/switches.rb".freeze, "lib/ollama_chat/think_control.rb".freeze, "lib/ollama_chat/tool_calling.rb".freeze, "lib/ollama_chat/tools.rb".freeze, "lib/ollama_chat/tools/cve.rb".freeze, "lib/ollama_chat/tools/endoflife.rb".freeze, "lib/ollama_chat/tools/weather.rb".freeze, "lib/ollama_chat/tools/weather/dwd_sensor.rb".freeze, "lib/ollama_chat/utils.rb".freeze, "lib/ollama_chat/utils/cache_fetcher.rb".freeze, "lib/ollama_chat/utils/chooser.rb".freeze, "lib/ollama_chat/utils/fetcher.rb".freeze, "lib/ollama_chat/utils/file_argument.rb".freeze, "lib/ollama_chat/version.rb".freeze, "lib/ollama_chat/vim.rb".freeze, "lib/ollama_chat/web_searching.rb".freeze, "ollama_chat.gemspec".freeze, "redis/redis.conf".freeze, "spec/assets/api_show.json".freeze, "spec/assets/api_tags.json".freeze, "spec/assets/api_version.json".freeze, "spec/assets/conversation.json".freeze, "spec/assets/duckduckgo.html".freeze, "spec/assets/example.atom".freeze, "spec/assets/example.csv".freeze, "spec/assets/example.html".freeze, "spec/assets/example.pdf".freeze, "spec/assets/example.ps".freeze, "spec/assets/example.rb".freeze, "spec/assets/example.rss".freeze, "spec/assets/example.xml".freeze, "spec/assets/example_with_quote.html".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/prompt.txt".freeze, "spec/assets/searxng.json".freeze, "spec/ollama_chat/chat_spec.rb".freeze, "spec/ollama_chat/clipboard_spec.rb".freeze, "spec/ollama_chat/follow_chat_spec.rb".freeze, "spec/ollama_chat/information_spec.rb".freeze, "spec/ollama_chat/input_content_spec.rb".freeze, "spec/ollama_chat/kramdown_ansi_spec.rb".freeze, "spec/ollama_chat/message_editing_spec.rb".freeze, "spec/ollama_chat/message_list_spec.rb".freeze, "spec/ollama_chat/message_output_spec.rb".freeze, "spec/ollama_chat/model_handling_spec.rb".freeze, "spec/ollama_chat/parsing_spec.rb".freeze, "spec/ollama_chat/redis_cache_spec.rb".freeze, "spec/ollama_chat/server_socket_spec.rb".freeze, "spec/ollama_chat/source_fetching_spec.rb".freeze, "spec/ollama_chat/state_selectors_spec.rb".freeze, "spec/ollama_chat/switches_spec.rb".freeze, "spec/ollama_chat/think_control_spec.rb".freeze, "spec/ollama_chat/tools/cve_spec.rb".freeze, "spec/ollama_chat/tools/endoflife_spec.rb".freeze, "spec/ollama_chat/tools/weather_spec.rb".freeze, "spec/ollama_chat/utils/cache_fetcher_spec.rb".freeze, "spec/ollama_chat/utils/fetcher_spec.rb".freeze, "spec/ollama_chat/utils/file_argument_spec.rb".freeze, "spec/ollama_chat/vim_spec.rb".freeze, "spec/ollama_chat/web_searching_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
|
|
17
17
|
s.homepage = "https://github.com/flori/ollama_chat".freeze
|
|
18
18
|
s.licenses = ["MIT".freeze]
|
|
19
19
|
s.rdoc_options = ["--title".freeze, "OllamaChat - A command-line interface (CLI) for interacting with an Ollama AI model.".freeze, "--main".freeze, "README.md".freeze]
|
|
20
20
|
s.required_ruby_version = Gem::Requirement.new(">= 3.2".freeze)
|
|
21
21
|
s.rubygems_version = "4.0.3".freeze
|
|
22
22
|
s.summary = "A command-line interface (CLI) for interacting with an Ollama AI model.".freeze
|
|
23
|
-
s.test_files = ["spec/assets/example.rb".freeze, "spec/ollama_chat/chat_spec.rb".freeze, "spec/ollama_chat/clipboard_spec.rb".freeze, "spec/ollama_chat/follow_chat_spec.rb".freeze, "spec/ollama_chat/information_spec.rb".freeze, "spec/ollama_chat/input_content_spec.rb".freeze, "spec/ollama_chat/kramdown_ansi_spec.rb".freeze, "spec/ollama_chat/message_editing_spec.rb".freeze, "spec/ollama_chat/message_list_spec.rb".freeze, "spec/ollama_chat/message_output_spec.rb".freeze, "spec/ollama_chat/model_handling_spec.rb".freeze, "spec/ollama_chat/parsing_spec.rb".freeze, "spec/ollama_chat/redis_cache_spec.rb".freeze, "spec/ollama_chat/server_socket_spec.rb".freeze, "spec/ollama_chat/source_fetching_spec.rb".freeze, "spec/ollama_chat/state_selectors_spec.rb".freeze, "spec/ollama_chat/switches_spec.rb".freeze, "spec/ollama_chat/think_control_spec.rb".freeze, "spec/ollama_chat/utils/cache_fetcher_spec.rb".freeze, "spec/ollama_chat/utils/fetcher_spec.rb".freeze, "spec/ollama_chat/utils/file_argument_spec.rb".freeze, "spec/ollama_chat/vim_spec.rb".freeze, "spec/ollama_chat/web_searching_spec.rb".freeze, "spec/spec_helper.rb".freeze]
|
|
23
|
+
s.test_files = ["spec/assets/example.rb".freeze, "spec/ollama_chat/chat_spec.rb".freeze, "spec/ollama_chat/clipboard_spec.rb".freeze, "spec/ollama_chat/follow_chat_spec.rb".freeze, "spec/ollama_chat/information_spec.rb".freeze, "spec/ollama_chat/input_content_spec.rb".freeze, "spec/ollama_chat/kramdown_ansi_spec.rb".freeze, "spec/ollama_chat/message_editing_spec.rb".freeze, "spec/ollama_chat/message_list_spec.rb".freeze, "spec/ollama_chat/message_output_spec.rb".freeze, "spec/ollama_chat/model_handling_spec.rb".freeze, "spec/ollama_chat/parsing_spec.rb".freeze, "spec/ollama_chat/redis_cache_spec.rb".freeze, "spec/ollama_chat/server_socket_spec.rb".freeze, "spec/ollama_chat/source_fetching_spec.rb".freeze, "spec/ollama_chat/state_selectors_spec.rb".freeze, "spec/ollama_chat/switches_spec.rb".freeze, "spec/ollama_chat/think_control_spec.rb".freeze, "spec/ollama_chat/tools/cve_spec.rb".freeze, "spec/ollama_chat/tools/endoflife_spec.rb".freeze, "spec/ollama_chat/tools/weather_spec.rb".freeze, "spec/ollama_chat/utils/cache_fetcher_spec.rb".freeze, "spec/ollama_chat/utils/fetcher_spec.rb".freeze, "spec/ollama_chat/utils/file_argument_spec.rb".freeze, "spec/ollama_chat/vim_spec.rb".freeze, "spec/ollama_chat/web_searching_spec.rb".freeze, "spec/spec_helper.rb".freeze]
|
|
24
24
|
|
|
25
25
|
s.specification_version = 4
|
|
26
26
|
|
|
@@ -52,4 +52,5 @@ Gem::Specification.new do |s|
|
|
|
52
52
|
s.add_runtime_dependency(%q<csv>.freeze, ["~> 3.0".freeze])
|
|
53
53
|
s.add_runtime_dependency(%q<const_conf>.freeze, ["~> 0.3".freeze])
|
|
54
54
|
s.add_runtime_dependency(%q<context_spook>.freeze, ["~> 1.5".freeze])
|
|
55
|
+
s.add_runtime_dependency(%q<rubyzip>.freeze, ["~> 3.0".freeze])
|
|
55
56
|
end
|
|
@@ -177,6 +177,23 @@ describe OllamaChat::Chat, protect_env: true do
|
|
|
177
177
|
expect(chat.handle_input("/load ./some_file")).to eq :next
|
|
178
178
|
end
|
|
179
179
|
|
|
180
|
+
describe 'tools' do
|
|
181
|
+
it 'returns :next when input is "/tools"' do
|
|
182
|
+
expect(chat).to receive(:list_tools)
|
|
183
|
+
expect(chat.handle_input("/tools")).to eq :next
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it 'returns :next when input is "/tools enable"' do
|
|
187
|
+
expect(chat).to receive(:enable_tool)
|
|
188
|
+
expect(chat.handle_input("/tools enable")).to eq :next
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it 'returns :next when input is "/tools disable"' do
|
|
192
|
+
expect(chat).to receive(:disable_tool)
|
|
193
|
+
expect(chat.handle_input("/tools disable")).to eq :next
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
180
197
|
it 'returns :next when input is "/config"' do
|
|
181
198
|
expect(chat).to receive(:display_config)
|
|
182
199
|
expect(chat.handle_input("/config")).to eq :next
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe OllamaChat::Tools::CVE do
|
|
4
|
+
let :chat do
|
|
5
|
+
OllamaChat::Chat.new argv: chat_default_config
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
connect_to_ollama_server
|
|
9
|
+
|
|
10
|
+
it 'can have name' do
|
|
11
|
+
expect(described_class.new.name).to eq 'get_cve'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'can have tool' do
|
|
15
|
+
expect(described_class.new.tool).to be_a Ollama::Tool
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'can be executed successfully' do
|
|
19
|
+
# Mock the fetcher to return a valid CVE response
|
|
20
|
+
cve_id = 'CVE-2023-12345'
|
|
21
|
+
tool_call = double(
|
|
22
|
+
'ToolCall',
|
|
23
|
+
function: double(
|
|
24
|
+
name: 'get_cve',
|
|
25
|
+
arguments: double(
|
|
26
|
+
cve_id: cve_id
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
url = chat.config.tools.get_cve.url
|
|
32
|
+
|
|
33
|
+
# Stub the HTTP request
|
|
34
|
+
stub_request(:get, url % { cve_id: cve_id })
|
|
35
|
+
.to_return(
|
|
36
|
+
status: 200,
|
|
37
|
+
body: '{"id": "CVE-2023-12345", "description": "Test vulnerability description"}',
|
|
38
|
+
headers: { 'Content-Type' => 'application/json' }
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
result = described_class.new.execute(tool_call, config: chat.config)
|
|
42
|
+
expect(result.id).to eq 'CVE-2023-12345'
|
|
43
|
+
expect(result.description).to include('Test vulnerability description')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'can handle execution errors gracefully' do
|
|
47
|
+
cve_id = 'CVE-2023-99999'
|
|
48
|
+
tool_call = double(
|
|
49
|
+
'ToolCall',
|
|
50
|
+
function: double(
|
|
51
|
+
name: 'get_cve',
|
|
52
|
+
arguments: double(
|
|
53
|
+
cve_id: cve_id
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
url = chat.config.tools.get_cve.url
|
|
59
|
+
|
|
60
|
+
stub_request(:get, url % { cve_id: })
|
|
61
|
+
.to_return(status: 404, body: 'Not Found')
|
|
62
|
+
|
|
63
|
+
result = described_class.new.execute(tool_call, config: chat.config)
|
|
64
|
+
expect(result).to include('Failed to fetch CVE')
|
|
65
|
+
expect(result).to include(cve_id)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'can be converted to hash' do
|
|
69
|
+
expect(described_class.new.to_hash).to be_a Hash
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe OllamaChat::Tools::EndOfLife do
|
|
4
|
+
let :chat do
|
|
5
|
+
OllamaChat::Chat.new argv: chat_default_config
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
connect_to_ollama_server
|
|
9
|
+
|
|
10
|
+
it 'can have name' do
|
|
11
|
+
expect(described_class.new.name).to eq 'get_endoflife'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'can have tool' do
|
|
15
|
+
expect(described_class.new.tool).to be_a Ollama::Tool
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'can be executed successfully' do
|
|
19
|
+
# Mock the fetcher to return a valid endoflife response
|
|
20
|
+
product = 'ruby'
|
|
21
|
+
tool_call = double(
|
|
22
|
+
'ToolCall',
|
|
23
|
+
function: double(
|
|
24
|
+
name: 'get_endoflife',
|
|
25
|
+
arguments: double(
|
|
26
|
+
product: product
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
url = chat.config.tools.get_endoflife.url
|
|
32
|
+
|
|
33
|
+
# Stub the HTTP request
|
|
34
|
+
stub_request(:get, url % { product: })
|
|
35
|
+
.to_return(
|
|
36
|
+
status: 200,
|
|
37
|
+
body: '{ "cycle": "3.1", "releaseDate": "2023-05-01", "eol": "2026-05-01" }',
|
|
38
|
+
headers: { 'Content-Type' => 'application/json' }
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
result = described_class.new.execute(tool_call, config: chat.config)
|
|
42
|
+
expect(result.cycle).to eq '3.1'
|
|
43
|
+
expect(result.releaseDate).to eq '2023-05-01'
|
|
44
|
+
expect(result.eol).to eq '2026-05-01'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'can handle execution errors gracefully' do
|
|
48
|
+
product = 'nonexistent-product'
|
|
49
|
+
tool_call = double(
|
|
50
|
+
'ToolCall',
|
|
51
|
+
function: double(
|
|
52
|
+
name: 'get_endoflife',
|
|
53
|
+
arguments: double(
|
|
54
|
+
product: product
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
url = chat.config.tools.get_endoflife.url
|
|
60
|
+
|
|
61
|
+
stub_request(:get, url % { product: product })
|
|
62
|
+
.to_return(status: 404, body: 'Not Found')
|
|
63
|
+
|
|
64
|
+
result = described_class.new.execute(tool_call, config: chat.config)
|
|
65
|
+
expect(result).to include('Failed to fetch endoflife data')
|
|
66
|
+
expect(result).to include(product)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'can be converted to hash' do
|
|
70
|
+
expect(described_class.new.to_hash).to be_a Hash
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe OllamaChat::Tools::Weather do
|
|
4
|
+
let :chat do
|
|
5
|
+
OllamaChat::Chat.new argv: chat_default_config
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
connect_to_ollama_server
|
|
9
|
+
|
|
10
|
+
it 'can have name' do
|
|
11
|
+
expect(described_class.new.name).to eq 'get_current_weather'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'can have tool' do
|
|
15
|
+
expect(described_class.new.tool).to be_a Ollama::Tool
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'can be executed for celsius' do
|
|
19
|
+
expect(DWDSensor).to receive(:new).and_return(
|
|
20
|
+
double(measure: [ Time.now, 23.0 ])
|
|
21
|
+
)
|
|
22
|
+
tool_call = double(
|
|
23
|
+
'ToolCall',
|
|
24
|
+
function: double(
|
|
25
|
+
name: 'get_current_weather',
|
|
26
|
+
arguments: double(
|
|
27
|
+
location: 'Berlin',
|
|
28
|
+
temperature_unit: 'celsius'
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
expect(
|
|
33
|
+
described_class.new.execute(tool_call, config: chat.config)
|
|
34
|
+
).to match(/The temperature was 23.0 ℃ at the time of /)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'can be executed for fahrenheit' do
|
|
38
|
+
expect(DWDSensor).to receive(:new).and_return(
|
|
39
|
+
double(measure: [ Time.now, 23.0 ])
|
|
40
|
+
)
|
|
41
|
+
tool_call = double(
|
|
42
|
+
'ToolCall',
|
|
43
|
+
function: double(
|
|
44
|
+
name: 'get_current_weather',
|
|
45
|
+
arguments: double(
|
|
46
|
+
location: 'Berlin',
|
|
47
|
+
temperature_unit: 'fahrenheit'
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
expect(
|
|
52
|
+
described_class.new.execute(tool_call, config: chat.config)
|
|
53
|
+
).to match(/The temperature was 73.4 ℉ at the time of /)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'can be converted to hash' do
|
|
57
|
+
expect(described_class.new.to_hash).to be_a Hash
|
|
58
|
+
end
|
|
59
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ollama_chat
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.59
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Florian Frank
|
|
@@ -407,6 +407,20 @@ dependencies:
|
|
|
407
407
|
- - "~>"
|
|
408
408
|
- !ruby/object:Gem::Version
|
|
409
409
|
version: '1.5'
|
|
410
|
+
- !ruby/object:Gem::Dependency
|
|
411
|
+
name: rubyzip
|
|
412
|
+
requirement: !ruby/object:Gem::Requirement
|
|
413
|
+
requirements:
|
|
414
|
+
- - "~>"
|
|
415
|
+
- !ruby/object:Gem::Version
|
|
416
|
+
version: '3.0'
|
|
417
|
+
type: :runtime
|
|
418
|
+
prerelease: false
|
|
419
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
420
|
+
requirements:
|
|
421
|
+
- - "~>"
|
|
422
|
+
- !ruby/object:Gem::Version
|
|
423
|
+
version: '3.0'
|
|
410
424
|
description: |
|
|
411
425
|
The app provides a command-line interface (CLI) to an Ollama AI model,
|
|
412
426
|
allowing users to engage in text-based conversations and generate
|
|
@@ -447,6 +461,12 @@ extra_rdoc_files:
|
|
|
447
461
|
- lib/ollama_chat/state_selectors.rb
|
|
448
462
|
- lib/ollama_chat/switches.rb
|
|
449
463
|
- lib/ollama_chat/think_control.rb
|
|
464
|
+
- lib/ollama_chat/tool_calling.rb
|
|
465
|
+
- lib/ollama_chat/tools.rb
|
|
466
|
+
- lib/ollama_chat/tools/cve.rb
|
|
467
|
+
- lib/ollama_chat/tools/endoflife.rb
|
|
468
|
+
- lib/ollama_chat/tools/weather.rb
|
|
469
|
+
- lib/ollama_chat/tools/weather/dwd_sensor.rb
|
|
450
470
|
- lib/ollama_chat/utils.rb
|
|
451
471
|
- lib/ollama_chat/utils/cache_fetcher.rb
|
|
452
472
|
- lib/ollama_chat/utils/chooser.rb
|
|
@@ -491,6 +511,12 @@ files:
|
|
|
491
511
|
- lib/ollama_chat/state_selectors.rb
|
|
492
512
|
- lib/ollama_chat/switches.rb
|
|
493
513
|
- lib/ollama_chat/think_control.rb
|
|
514
|
+
- lib/ollama_chat/tool_calling.rb
|
|
515
|
+
- lib/ollama_chat/tools.rb
|
|
516
|
+
- lib/ollama_chat/tools/cve.rb
|
|
517
|
+
- lib/ollama_chat/tools/endoflife.rb
|
|
518
|
+
- lib/ollama_chat/tools/weather.rb
|
|
519
|
+
- lib/ollama_chat/tools/weather/dwd_sensor.rb
|
|
494
520
|
- lib/ollama_chat/utils.rb
|
|
495
521
|
- lib/ollama_chat/utils/cache_fetcher.rb
|
|
496
522
|
- lib/ollama_chat/utils/chooser.rb
|
|
@@ -535,6 +561,9 @@ files:
|
|
|
535
561
|
- spec/ollama_chat/state_selectors_spec.rb
|
|
536
562
|
- spec/ollama_chat/switches_spec.rb
|
|
537
563
|
- spec/ollama_chat/think_control_spec.rb
|
|
564
|
+
- spec/ollama_chat/tools/cve_spec.rb
|
|
565
|
+
- spec/ollama_chat/tools/endoflife_spec.rb
|
|
566
|
+
- spec/ollama_chat/tools/weather_spec.rb
|
|
538
567
|
- spec/ollama_chat/utils/cache_fetcher_spec.rb
|
|
539
568
|
- spec/ollama_chat/utils/fetcher_spec.rb
|
|
540
569
|
- spec/ollama_chat/utils/file_argument_spec.rb
|
|
@@ -586,6 +615,9 @@ test_files:
|
|
|
586
615
|
- spec/ollama_chat/state_selectors_spec.rb
|
|
587
616
|
- spec/ollama_chat/switches_spec.rb
|
|
588
617
|
- spec/ollama_chat/think_control_spec.rb
|
|
618
|
+
- spec/ollama_chat/tools/cve_spec.rb
|
|
619
|
+
- spec/ollama_chat/tools/endoflife_spec.rb
|
|
620
|
+
- spec/ollama_chat/tools/weather_spec.rb
|
|
589
621
|
- spec/ollama_chat/utils/cache_fetcher_spec.rb
|
|
590
622
|
- spec/ollama_chat/utils/fetcher_spec.rb
|
|
591
623
|
- spec/ollama_chat/utils/file_argument_spec.rb
|