ollama_chat 0.0.61 → 0.0.63
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 +78 -0
- data/Rakefile +1 -0
- data/lib/ollama_chat/chat.rb +46 -16
- data/lib/ollama_chat/env_config.rb +18 -0
- data/lib/ollama_chat/follow_chat.rb +41 -2
- data/lib/ollama_chat/message_list.rb +3 -1
- data/lib/ollama_chat/ollama_chat_config/default_config.yml +38 -1
- data/lib/ollama_chat/parsing.rb +4 -0
- data/lib/ollama_chat/tool_calling.rb +26 -1
- data/lib/ollama_chat/tools/browse.rb +86 -0
- data/lib/ollama_chat/tools/concern.rb +59 -0
- data/lib/ollama_chat/tools/directory_structure.rb +14 -78
- data/lib/ollama_chat/tools/endoflife.rb +0 -98
- data/lib/ollama_chat/tools/{grep.rb → execute_grep.rb} +28 -30
- data/lib/ollama_chat/tools/file_context.rb +38 -41
- data/lib/ollama_chat/tools/gem_path_lookup.rb +96 -0
- data/lib/ollama_chat/tools/{weather.rb → get_current_weather.rb} +13 -21
- data/lib/ollama_chat/tools/{cve.rb → get_cve.rb} +11 -38
- data/lib/ollama_chat/tools/get_endoflife.rb +74 -0
- data/lib/ollama_chat/tools/{location.rb → get_location.rb} +9 -21
- data/lib/ollama_chat/tools/import_url.rb +81 -0
- data/lib/ollama_chat/tools/read_file.rb +43 -0
- data/lib/ollama_chat/tools/run_tests.rb +84 -0
- data/lib/ollama_chat/tools/search_web.rb +75 -0
- data/lib/ollama_chat/tools/vim_open_file.rb +93 -0
- data/lib/ollama_chat/tools/write_file.rb +91 -0
- data/lib/ollama_chat/tools.rb +30 -15
- data/lib/ollama_chat/utils/analyze_directory.rb +64 -0
- data/lib/ollama_chat/utils/path_validator.rb +55 -0
- data/lib/ollama_chat/utils.rb +2 -0
- data/lib/ollama_chat/version.rb +1 -1
- data/lib/ollama_chat/vim.rb +84 -10
- data/lib/ollama_chat.rb +20 -2
- data/ollama_chat.gemspec +6 -5
- data/spec/ollama_chat/message_list_spec.rb +1 -0
- data/spec/ollama_chat/tools/browse_spec.rb +131 -0
- data/spec/ollama_chat/tools/directory_structure_spec.rb +12 -7
- data/spec/ollama_chat/tools/execute_grep_spec.rb +211 -0
- data/spec/ollama_chat/tools/{file_content_spec.rb → file_context_spec.rb} +4 -7
- data/spec/ollama_chat/tools/gem_path_lookup_spec.rb +82 -0
- data/spec/ollama_chat/tools/{weather_spec.rb → get_current_weather_spec.rb} +32 -3
- data/spec/ollama_chat/tools/{cve_spec.rb → get_cve_spec.rb} +11 -9
- data/spec/ollama_chat/tools/{endoflife_spec.rb → get_endoflife_spec.rb} +12 -10
- data/spec/ollama_chat/tools/{location_spec.rb → get_location_spec.rb} +6 -6
- data/spec/ollama_chat/tools/import_url_spec.rb +97 -0
- data/spec/ollama_chat/tools/read_file_spec.rb +85 -0
- data/spec/ollama_chat/tools/run_tests_spec.rb +113 -0
- data/spec/ollama_chat/tools/search_web_spec.rb +104 -0
- data/spec/ollama_chat/tools/vim_open_file_spec.rb +86 -0
- data/spec/ollama_chat/tools/write_file_spec.rb +136 -0
- data/spec/ollama_chat/utils/analyze_directory_spec.rb +91 -0
- data/spec/spec_helper.rb +44 -7
- metadata +77 -21
- data/spec/ollama_chat/tools/grep_spec.rb +0 -136
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5d207c29d7838f0cb8bb885dfc93038d2c246eb63d829d0af2e0cdef4c404d3f
|
|
4
|
+
data.tar.gz: 3b70df18f2d302506614a56556ec0f98d2e814a3558a7bc141b73cc0716c5158
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b2647b7371490991e879f6f48682a7e7e2a2c3d7ab0fafd41fc7275cd204f9bb288b0ca0e8b11d5be9b0c23b54c93d141e418f0495ce4782de2b0165df1a703
|
|
7
|
+
data.tar.gz: 83fa7ff6da22352d5d5af335e98975d423641ab3969e51f311068fd0912f4fb3494c22058c1a66e892cf1d11574cc705d9dc3c714ef50123ad555ca67011311b
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-02-09 v0.0.63
|
|
4
|
+
|
|
5
|
+
- Added `OllamaChat::Utils::PathValidator` module with `assert_valid_path`
|
|
6
|
+
helper and `OllamaChat::InvalidPathError` exception
|
|
7
|
+
- Refactored `FileContext`, `ReadFile`, and `WriteFile` tools to use new path
|
|
8
|
+
validation logic
|
|
9
|
+
- Simplified `OllamaChat::Tools::FileContext` to use glob pattern only,
|
|
10
|
+
removing the `path` parameter
|
|
11
|
+
- Added `ignore_case` flag to `execute_grep` tool with dynamic command
|
|
12
|
+
construction using `eval_template`
|
|
13
|
+
- Renamed tool classes and files to more descriptive names:
|
|
14
|
+
- `browser.rb` → `browse.rb` (class `Browser` → `Browse`)
|
|
15
|
+
- `grep.rb` → `execute_grep.rb` (class `Grep` → `ExecuteGrep`)
|
|
16
|
+
- `weather.rb` → `get_current_weather.rb` (class `Weather` → `GetCurrentWeather`)
|
|
17
|
+
- `cve.rb` → `get_cve.rb` (class `CVE` → `GetCVE`)
|
|
18
|
+
- `endoflife.rb` → `get_endoflife.rb` (class `EndOfLife` → `GetEndoflife`)
|
|
19
|
+
- `location.rb` → `get_location.rb` (class `Location` → `GetLocation`)
|
|
20
|
+
- `file_reader.rb` → `read_file.rb` (class `FileReader` → `ReadFile`)
|
|
21
|
+
- `file_writer.rb` → `write_file.rb` (class `FileWriter` → `WriteFile`)
|
|
22
|
+
- Updated `follow_chat.rb` to use `require_confirmation?` instead of `confirm?`
|
|
23
|
+
when checking tool confirmation
|
|
24
|
+
- Added scheme whitelist to `ImportURL` tool with `schemes: [http, https]`
|
|
25
|
+
configuration
|
|
26
|
+
- Introduced `read_file` tool with path validation and error handling
|
|
27
|
+
- Added `run_tests` tool for executing RSpec or Test-Unit test suites with
|
|
28
|
+
configurable runner and coverage reporting
|
|
29
|
+
- Added `vim_open_file` tool for remote Vim file opening with line range
|
|
30
|
+
support
|
|
31
|
+
- Enhanced `OllamaChat::Vim` class with file opening and line/range selection
|
|
32
|
+
capabilities
|
|
33
|
+
- Added `GemPathLookup` tool to find gem installation paths using Bundler
|
|
34
|
+
- Added `valid_json?` method to `OllamaChat::Tools::Concern` for consistent
|
|
35
|
+
JSON validation
|
|
36
|
+
- Implemented `ImportURL` tool for fetching web content
|
|
37
|
+
|
|
38
|
+
## 2026-02-07 v0.0.62
|
|
39
|
+
|
|
40
|
+
**Tool Execution**
|
|
41
|
+
- All tools now return structured JSON errors (`error` + `message`).
|
|
42
|
+
- Confirmation prompts (`confirm?`) added to `OllamaChat::FollowChat`.
|
|
43
|
+
- `infobar` displays a busy indicator and status messages during tool runs.
|
|
44
|
+
- Tool methods accept `config:` and `chat:` keyword arguments.
|
|
45
|
+
|
|
46
|
+
**Tool Registration**
|
|
47
|
+
- Centralized logic via `OllamaChat::Tools::Concern` to prevent duplicate
|
|
48
|
+
registrations.
|
|
49
|
+
|
|
50
|
+
**File Context Tool (`file_context`)**
|
|
51
|
+
- Supports an exact `path:` argument in addition to `directory:` + `pattern:`.
|
|
52
|
+
- Uses `blank?` for argument validation.
|
|
53
|
+
- YARD documentation added.
|
|
54
|
+
|
|
55
|
+
**Directory Structure Tool (`directory_structure`)**
|
|
56
|
+
- Delegates to `OllamaChat::Utils::AnalyzeDirectory.generate_structure`.
|
|
57
|
+
- Excludes hidden files, symlinks, and the `pkg` directory by default.
|
|
58
|
+
- `exclude` option configurable in `default_config.yml`.
|
|
59
|
+
|
|
60
|
+
**Utility Module**
|
|
61
|
+
- New `OllamaChat::Utils::AnalyzeDirectory` containing the `generate_structure`
|
|
62
|
+
method.
|
|
63
|
+
|
|
64
|
+
**Error Handling**
|
|
65
|
+
- `CVE`, `EndOfLife`, `Grep`, and `Weather` tools now catch all exceptions and return structured JSON errors.
|
|
66
|
+
|
|
67
|
+
**Testing**
|
|
68
|
+
- Added comprehensive specs for `AnalyzeDirectory` (traversal, exclusions,
|
|
69
|
+
error handling).
|
|
70
|
+
- Tests for exact `path` usage in `file_context` with conflict detection.
|
|
71
|
+
- Updated `test_files` list in the gemspec.
|
|
72
|
+
|
|
73
|
+
**Configuration**
|
|
74
|
+
- `directory_structure` accepts an `exclude` option via `default_config.yml`.
|
|
75
|
+
- Tool signatures updated to accept `config:` and `chat:`.
|
|
76
|
+
|
|
77
|
+
**Gem Specification**
|
|
78
|
+
- Updated `test_files`, `extra_rdoc_files`, and `files` arrays to include new
|
|
79
|
+
utilities, tests, and documentation.
|
|
80
|
+
|
|
3
81
|
## 2026-02-06 v0.0.61
|
|
4
82
|
|
|
5
83
|
### New Features
|
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 'infobar', '>= 0.13.1'
|
|
62
63
|
dependency 'rubyzip', '~> 3.0'
|
|
63
64
|
development_dependency 'all_images', '~> 0.6'
|
|
64
65
|
development_dependency 'rspec', '~> 3.2'
|
data/lib/ollama_chat/chat.rb
CHANGED
|
@@ -95,27 +95,21 @@ class OllamaChat::Chat
|
|
|
95
95
|
connect_ollama
|
|
96
96
|
@model = choose_model(@opts[?m], config.model.name)
|
|
97
97
|
@model_options = Ollama::Options[config.model.options]
|
|
98
|
-
model_system
|
|
99
|
-
embedding_enabled.set(config.embedding.enabled && !@opts[?E])
|
|
98
|
+
@model_system = pull_model_unless_present(@model, @model_options)
|
|
100
99
|
if @opts[?c]
|
|
101
100
|
messages.load_conversation(@opts[?c])
|
|
102
101
|
else
|
|
103
|
-
|
|
104
|
-
if @opts[?s] =~ /\A\?/
|
|
105
|
-
change_system_prompt(default, system: @opts[?s])
|
|
106
|
-
else
|
|
107
|
-
system = OllamaChat::Utils::FileArgument.get_file_argument(@opts[?s], default:)
|
|
108
|
-
system.present? and messages.set_system_prompt(system)
|
|
109
|
-
end
|
|
102
|
+
setup_system_prompt
|
|
110
103
|
end
|
|
104
|
+
embedding_enabled.set(config.embedding.enabled && !@opts[?E])
|
|
111
105
|
@documents = setup_documents
|
|
112
106
|
@cache = setup_cache
|
|
113
107
|
@images = []
|
|
114
108
|
@kramdown_ansi_styles = configure_kramdown_ansi_styles
|
|
109
|
+
@enabled_tools = default_enabled_tools
|
|
110
|
+
@tool_call_results = {}
|
|
115
111
|
init_chat_history
|
|
116
112
|
@opts[?S] and init_server_socket
|
|
117
|
-
@enabled_tools = config.tools.to_h.map { |n, v| n.to_s if v[:default] }.compact
|
|
118
|
-
@tool_call_results = {}
|
|
119
113
|
rescue ComplexConfig::AttributeMissing, ComplexConfig::ConfigurationSyntaxError => e
|
|
120
114
|
fix_config(e)
|
|
121
115
|
end
|
|
@@ -208,6 +202,25 @@ class OllamaChat::Chat
|
|
|
208
202
|
interact_with_user
|
|
209
203
|
end
|
|
210
204
|
|
|
205
|
+
# The vim method creates and returns a new Vim instance for interacting with
|
|
206
|
+
# a Vim server.
|
|
207
|
+
#
|
|
208
|
+
# This method initializes a Vim client that can be used to insert text into
|
|
209
|
+
# Vim buffers or open files in a running Vim server. It derives the server
|
|
210
|
+
# name from the provided argument or uses a default server name based on the
|
|
211
|
+
# current working directory.
|
|
212
|
+
#
|
|
213
|
+
# @param server_name [ String, nil ] the name of the Vim server to connect to
|
|
214
|
+
# If nil or empty, a default server name is derived from the current
|
|
215
|
+
# working directory
|
|
216
|
+
#
|
|
217
|
+
# @return [ OllamaChat::Vim ] a new Vim instance configured with the
|
|
218
|
+
# specified server name
|
|
219
|
+
def vim(server_name = nil)
|
|
220
|
+
clientserver = config.vim?&.clientserver
|
|
221
|
+
OllamaChat::Vim.new(server_name, clientserver:)
|
|
222
|
+
end
|
|
223
|
+
|
|
211
224
|
private
|
|
212
225
|
|
|
213
226
|
# Handles user input commands and processes chat interactions.
|
|
@@ -248,7 +261,7 @@ class OllamaChat::Chat
|
|
|
248
261
|
messages.list_conversation(n)
|
|
249
262
|
:next
|
|
250
263
|
when %r(^/last(?:\s+(\d*))?$)
|
|
251
|
-
n = $1.to_i
|
|
264
|
+
n = $1.to_i.clamp(1..)
|
|
252
265
|
messages.show_last(n)
|
|
253
266
|
:next
|
|
254
267
|
when %r(^/clear(?:\s+(messages|links|history|tags|all))?$)
|
|
@@ -370,8 +383,7 @@ class OllamaChat::Chat
|
|
|
370
383
|
:next
|
|
371
384
|
when %r(^/vim(?:\s+(.+))?$)
|
|
372
385
|
if message = messages.last
|
|
373
|
-
|
|
374
|
-
OllamaChat::Vim.new($1, clientserver:).insert message.content
|
|
386
|
+
vim($1).insert message.content
|
|
375
387
|
else
|
|
376
388
|
STDERR.puts "Warning: No message found to insert into Vim"
|
|
377
389
|
end
|
|
@@ -702,8 +714,6 @@ class OllamaChat::Chat
|
|
|
702
714
|
save_history
|
|
703
715
|
end
|
|
704
716
|
|
|
705
|
-
private
|
|
706
|
-
|
|
707
717
|
# The base_url method returns the Ollama server URL from command-line options
|
|
708
718
|
# or environment configuration.
|
|
709
719
|
#
|
|
@@ -738,6 +748,26 @@ class OllamaChat::Chat
|
|
|
738
748
|
@ollama
|
|
739
749
|
end
|
|
740
750
|
|
|
751
|
+
# Sets up the system prompt for the chat session.
|
|
752
|
+
#
|
|
753
|
+
# This method determines whether to use a default system prompt or a custom
|
|
754
|
+
# one specified via command-line options. If a custom system prompt is
|
|
755
|
+
# provided with a regex selector (starting with ?), it invokes the
|
|
756
|
+
# change_system_prompt method to handle the selection. Otherwise, it
|
|
757
|
+
# retrieves the system prompt from a file or uses the default value, then
|
|
758
|
+
# sets it in the message history.
|
|
759
|
+
#
|
|
760
|
+
# @return [ void ] this method returns nil after setting up the system prompt
|
|
761
|
+
def setup_system_prompt
|
|
762
|
+
default = config.system_prompts.default? || @model_system
|
|
763
|
+
if @opts[?s] =~ /\A\?/
|
|
764
|
+
change_system_prompt(default, system: @opts[?s])
|
|
765
|
+
else
|
|
766
|
+
system = OllamaChat::Utils::FileArgument.get_file_argument(@opts[?s], default:)
|
|
767
|
+
system.present? and messages.set_system_prompt(system)
|
|
768
|
+
end
|
|
769
|
+
end
|
|
770
|
+
|
|
741
771
|
# The setup_documents method initializes the document processing pipeline by
|
|
742
772
|
# configuring the embedding model and database connection.
|
|
743
773
|
# It then loads specified documents into the system and returns the
|
|
@@ -51,6 +51,14 @@ module OllamaChat
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
BROWSER = set do
|
|
55
|
+
description 'Browser to use'
|
|
56
|
+
|
|
57
|
+
default do
|
|
58
|
+
%w[ open xdg-open ].find { `which #{_1}` }.full?(:chomp)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
54
62
|
DIFF_TOOL = set do
|
|
55
63
|
description 'Diff tool to apply changes with'
|
|
56
64
|
|
|
@@ -128,6 +136,16 @@ module OllamaChat
|
|
|
128
136
|
description 'File to save the chat history in'
|
|
129
137
|
default XDG_CACHE_HOME + 'history.json'
|
|
130
138
|
end
|
|
139
|
+
|
|
140
|
+
module TOOLS
|
|
141
|
+
description 'Tool specific configuration settings'
|
|
142
|
+
|
|
143
|
+
RUN_TESTS_TEST_RUNNER = set do
|
|
144
|
+
description 'Configured test runner for run_tests tool function'
|
|
145
|
+
default 'rspec'
|
|
146
|
+
required true
|
|
147
|
+
end
|
|
148
|
+
end
|
|
131
149
|
end
|
|
132
150
|
end
|
|
133
151
|
end
|
|
@@ -102,8 +102,47 @@ class OllamaChat::FollowChat
|
|
|
102
102
|
|
|
103
103
|
response.message.tool_calls.each do |tool_call|
|
|
104
104
|
name = tool_call.function.name
|
|
105
|
-
@chat.
|
|
106
|
-
|
|
105
|
+
unless @chat.config.tools.attribute_set?(name)
|
|
106
|
+
STDERR.printf("Unconfigured tool named %s ignored => Skip.\n", name)
|
|
107
|
+
next
|
|
108
|
+
end
|
|
109
|
+
unless OllamaChat::Tools.registered?(name)
|
|
110
|
+
STDERR.printf("Unregistered tool named %s ignored => Skip.\n", name)
|
|
111
|
+
next
|
|
112
|
+
end
|
|
113
|
+
STDOUT.puts
|
|
114
|
+
confirmed = true
|
|
115
|
+
args = JSON.pretty_generate(tool_call.function.arguments)
|
|
116
|
+
if @chat.config.tools[name].require_confirmation?
|
|
117
|
+
prompt = "I want to execute tool %s\n%s\nConfirm? (y/n) " % [
|
|
118
|
+
bold { name },
|
|
119
|
+
italic { args },
|
|
120
|
+
]
|
|
121
|
+
confirmed = @chat.ask?(prompt:) =~ /\Ay/i
|
|
122
|
+
else
|
|
123
|
+
STDOUT.puts "Executing tool %s\n%s" % [
|
|
124
|
+
bold { name },
|
|
125
|
+
italic { args },
|
|
126
|
+
]
|
|
127
|
+
end
|
|
128
|
+
result = nil
|
|
129
|
+
if confirmed
|
|
130
|
+
STDOUT.printf(
|
|
131
|
+
"\n%s Execution of tool %s confirmed.\n\n", ?✅, bold { name }
|
|
132
|
+
)
|
|
133
|
+
result = OllamaChat::Tools.registered[name].
|
|
134
|
+
execute(tool_call, chat: @chat, config: @chat.config)
|
|
135
|
+
else
|
|
136
|
+
result = JSON(
|
|
137
|
+
message: 'User denied confirmation!',
|
|
138
|
+
resolve: 'You **MUST** ask the user for instructions on how to proceed!!!',
|
|
139
|
+
)
|
|
140
|
+
STDOUT.printf(
|
|
141
|
+
"\n%s Execution of tool %s denied by user.\n", ?🚫, bold { name }
|
|
142
|
+
)
|
|
143
|
+
sleep 1
|
|
144
|
+
end
|
|
145
|
+
@chat.tool_call_results[name] = result
|
|
107
146
|
end
|
|
108
147
|
end
|
|
109
148
|
|
|
@@ -113,7 +113,9 @@ class OllamaChat::MessageList
|
|
|
113
113
|
end
|
|
114
114
|
@messages =
|
|
115
115
|
File.open(filename, 'r') do |output|
|
|
116
|
-
JSON(output.read).map {
|
|
116
|
+
JSON(output.read).map {
|
|
117
|
+
Ollama::Message.from_hash(_1 | { 'content' => nil })
|
|
118
|
+
}
|
|
117
119
|
end
|
|
118
120
|
self
|
|
119
121
|
end
|
|
@@ -98,8 +98,45 @@ tools:
|
|
|
98
98
|
default: true
|
|
99
99
|
file_context:
|
|
100
100
|
default: false
|
|
101
|
+
require_confirmation: true
|
|
102
|
+
allowed:
|
|
103
|
+
- ./spec
|
|
101
104
|
directory_structure:
|
|
102
105
|
default: false
|
|
106
|
+
require_confirmation: true
|
|
107
|
+
exclude:
|
|
108
|
+
- corpus
|
|
109
|
+
- pkg
|
|
103
110
|
execute_grep:
|
|
104
111
|
default: true
|
|
105
|
-
cmd:
|
|
112
|
+
cmd: |
|
|
113
|
+
grep #{'-i' if ignore_case} -m #{max_results} -r #{pattern} #{path}
|
|
114
|
+
browse:
|
|
115
|
+
default: false
|
|
116
|
+
write_file:
|
|
117
|
+
default: false
|
|
118
|
+
require_confirmation: true
|
|
119
|
+
allowed:
|
|
120
|
+
- ./tmp
|
|
121
|
+
read_file:
|
|
122
|
+
default: false
|
|
123
|
+
require_confirmation: false
|
|
124
|
+
allowed:
|
|
125
|
+
- ./tmp
|
|
126
|
+
- ./lib
|
|
127
|
+
- ./spec
|
|
128
|
+
search_web:
|
|
129
|
+
default: true
|
|
130
|
+
import_url:
|
|
131
|
+
default: true
|
|
132
|
+
require_confirmation: true
|
|
133
|
+
schemes:
|
|
134
|
+
- http
|
|
135
|
+
- https
|
|
136
|
+
gem_path_lookup:
|
|
137
|
+
default: true
|
|
138
|
+
vim_open_file:
|
|
139
|
+
default: true
|
|
140
|
+
run_tests:
|
|
141
|
+
require_confirmation: true
|
|
142
|
+
default: true
|
data/lib/ollama_chat/parsing.rb
CHANGED
|
@@ -219,6 +219,10 @@ module OllamaChat::Parsing
|
|
|
219
219
|
tags = Documentrix::Utils::Tags.new valid_tag: /\A#*([\w\]\[]+)/
|
|
220
220
|
contents = [ content ]
|
|
221
221
|
content.scan(CONTENT_REGEXP).each { |url, tag, file_url, quoted_file, file|
|
|
222
|
+
if file && File.directory?(file)
|
|
223
|
+
contents << OllamaChat::Utils::AnalyzeDirectory.generate_structure(file)
|
|
224
|
+
next
|
|
225
|
+
end
|
|
222
226
|
check_exist = false
|
|
223
227
|
case
|
|
224
228
|
when tag
|
|
@@ -13,6 +13,27 @@ module OllamaChat::ToolCalling
|
|
|
13
13
|
@enabled_tools.map { OllamaChat::Tools.registered[it]&.to_hash }.compact
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
# The default_enabled_tools method returns an array of tool names that are
|
|
17
|
+
# configured as default tools.
|
|
18
|
+
#
|
|
19
|
+
# This method iterates through the configured tools and collects those that
|
|
20
|
+
# are registered and have the default flag set to true. Unregistered tools
|
|
21
|
+
# are skipped with a warning message.
|
|
22
|
+
#
|
|
23
|
+
# @return [Array<String>] a sorted array of tool names that are configured as
|
|
24
|
+
# default tools
|
|
25
|
+
def default_enabled_tools
|
|
26
|
+
result = []
|
|
27
|
+
config.tools.each { |n, v|
|
|
28
|
+
if OllamaChat::Tools.registered?(n)
|
|
29
|
+
result << n.to_s if v.default
|
|
30
|
+
else
|
|
31
|
+
STDERR.puts "Skipping configuration for unregistered tool %s" % bold { n }
|
|
32
|
+
end
|
|
33
|
+
}
|
|
34
|
+
result
|
|
35
|
+
end
|
|
36
|
+
|
|
16
37
|
# The configured_tools method returns an array of tool names configured for
|
|
17
38
|
# the chat session.
|
|
18
39
|
#
|
|
@@ -39,7 +60,11 @@ module OllamaChat::ToolCalling
|
|
|
39
60
|
def list_tools
|
|
40
61
|
configured_tools.each do |tool|
|
|
41
62
|
enabled = @enabled_tools.member?(tool) ? ?✓ : ?☐
|
|
42
|
-
|
|
63
|
+
require_confirmation = config.tools[tool].require_confirmation? ? ?? : ?☐
|
|
64
|
+
printf(
|
|
65
|
+
"%s %s %s\n",
|
|
66
|
+
enabled, require_confirmation, (enabled ? bold { tool } : tool)
|
|
67
|
+
)
|
|
43
68
|
end
|
|
44
69
|
end
|
|
45
70
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
require "shellwords"
|
|
2
|
+
|
|
3
|
+
# A tool for opening URLs/files in the user's default browser application.
|
|
4
|
+
#
|
|
5
|
+
# This tool enables the chat client to open web URLs or local files in the
|
|
6
|
+
# user's default browser, allowing users to view content directly in their
|
|
7
|
+
# browser environment. It integrates with the Ollama tool calling system to
|
|
8
|
+
# provide
|
|
9
|
+
# seamless web browsing capabilities during chat interactions.
|
|
10
|
+
class OllamaChat::Tools::Browse
|
|
11
|
+
include OllamaChat::Tools::Concern
|
|
12
|
+
|
|
13
|
+
def self.register_name = 'browse'
|
|
14
|
+
|
|
15
|
+
# Creates and returns a tool definition for opening URLs/files in the browser.
|
|
16
|
+
#
|
|
17
|
+
# This method constructs the function signature that describes what the tool
|
|
18
|
+
# does, its parameters, and required fields. The tool expects a URL parameter
|
|
19
|
+
# to be provided.
|
|
20
|
+
#
|
|
21
|
+
# @return [Ollama::Tool] a tool definition for opening URLs/files in the browser
|
|
22
|
+
def tool
|
|
23
|
+
Tool.new(
|
|
24
|
+
type: 'function',
|
|
25
|
+
function: Tool::Function.new(
|
|
26
|
+
name:,
|
|
27
|
+
description: <<~EOT,
|
|
28
|
+
Open a URL or file in the user\'s default browser application so they
|
|
29
|
+
can view the content directly
|
|
30
|
+
EOT
|
|
31
|
+
parameters: Tool::Function::Parameters.new(
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
url: Tool::Function::Parameters::Property.new(
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: <<~EOT,
|
|
37
|
+
The URL or file to open in the user\'s browser for them to view
|
|
38
|
+
directly
|
|
39
|
+
EOT
|
|
40
|
+
),
|
|
41
|
+
},
|
|
42
|
+
required: %w[url]
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Executes the browser opening operation.
|
|
49
|
+
#
|
|
50
|
+
# This method opens the specified URL or file in the user's default browser
|
|
51
|
+
# application. It handles the system call and returns the result status.
|
|
52
|
+
#
|
|
53
|
+
# @param tool_call [Ollama::Tool::Call] the tool call object containing function details
|
|
54
|
+
# @param opts [Hash] additional options
|
|
55
|
+
# @return [String] the execution result as JSON string
|
|
56
|
+
# @raise [StandardError] if there's an issue with opening the URL/file
|
|
57
|
+
def execute(tool_call, **opts)
|
|
58
|
+
url = tool_call.function.arguments.url
|
|
59
|
+
result = browse_url(url)
|
|
60
|
+
{
|
|
61
|
+
success: result.success?,
|
|
62
|
+
exitstatus: result.exitstatus,
|
|
63
|
+
message: 'opening URL/file',
|
|
64
|
+
url: ,
|
|
65
|
+
}.to_json
|
|
66
|
+
rescue => e
|
|
67
|
+
{ error: e.class, message: e.message }.to_json
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
# Opens a URL or file in the system browser.
|
|
73
|
+
#
|
|
74
|
+
# This method uses the system's default browser to open the provided URL or file.
|
|
75
|
+
# It respects the BROWSER environment variable if set, otherwise defaults to "open".
|
|
76
|
+
#
|
|
77
|
+
# @param url [String] the URL or file path to open
|
|
78
|
+
# @return [Process::Status] the process status of the system call
|
|
79
|
+
def browse_url(url)
|
|
80
|
+
browser = OllamaChat::EnvConfig::BROWSER? || "open"
|
|
81
|
+
system %{#{browser} #{Shellwords.escape(url)}}
|
|
82
|
+
$?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
self
|
|
86
|
+
end.register
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# A module that provides common functionality for OllamaChat tools.
|
|
2
|
+
#
|
|
3
|
+
# This module serves as a base class for all tool implementations in the
|
|
4
|
+
# OllamaChat application, providing shared behavior and methods that tools can
|
|
5
|
+
# inherit from. It includes delegation to the tool name and registration
|
|
6
|
+
# functionality to integrate tools into the chat system's tool registry.
|
|
7
|
+
module OllamaChat::Tools::Concern
|
|
8
|
+
extend Tins::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
include Ollama
|
|
12
|
+
|
|
13
|
+
implement :tool, :submodule
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class_methods do
|
|
17
|
+
# The register method registers the tool with the tools registry.
|
|
18
|
+
# @return [ OllamaChat::Tools ] the current instance after registration
|
|
19
|
+
def register
|
|
20
|
+
OllamaChat::Tools.register(self)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The register_name attribute accessor provides read and write access to
|
|
24
|
+
# the register name of the tool.
|
|
25
|
+
#
|
|
26
|
+
# @return [ String ] the register name of the tool
|
|
27
|
+
attr_accessor :register_name
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The name method returns the registered name of the tool.
|
|
31
|
+
#
|
|
32
|
+
# @return [String] the registered name of the tool instance
|
|
33
|
+
def name
|
|
34
|
+
self.class.register_name
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# The valid_json? method returns a proc that validates JSON data from a
|
|
38
|
+
# temporary file.
|
|
39
|
+
#
|
|
40
|
+
# @return [Proc] a proc that takes a temporary file and returns its JSON
|
|
41
|
+
# content or raises an error
|
|
42
|
+
def valid_json?
|
|
43
|
+
-> tmp {
|
|
44
|
+
if data = tmp.read.full?
|
|
45
|
+
JSON.parse(data)
|
|
46
|
+
return data
|
|
47
|
+
else
|
|
48
|
+
raise JSON::ParserError, 'require JSON data'
|
|
49
|
+
end
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The to_hash method converts the tool to a hash representation.
|
|
54
|
+
#
|
|
55
|
+
# @return [ Hash ] a hash representation of the tool
|
|
56
|
+
def to_hash
|
|
57
|
+
tool.to_hash
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -7,19 +7,10 @@
|
|
|
7
7
|
# The tool supports traversing directories and returns a structured
|
|
8
8
|
# representation of the file system hierarchy.
|
|
9
9
|
class OllamaChat::Tools::DirectoryStructure
|
|
10
|
-
include
|
|
10
|
+
include OllamaChat::Tools::Concern
|
|
11
|
+
include OllamaChat::Utils::AnalyzeDirectory
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
# @return [OllamaChat::Tools::DirectoryStructure] a new directory_structure tool instance
|
|
15
|
-
def initialize
|
|
16
|
-
@name = 'directory_structure'
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Returns the name of the tool.
|
|
20
|
-
#
|
|
21
|
-
# @return [String] the name of the tool ('directory_structure')
|
|
22
|
-
attr_reader :name
|
|
13
|
+
def self.register_name = 'directory_structure'
|
|
23
14
|
|
|
24
15
|
# Creates and returns a tool definition for retrieving directory structure.
|
|
25
16
|
#
|
|
@@ -51,77 +42,22 @@ class OllamaChat::Tools::DirectoryStructure
|
|
|
51
42
|
# Executes the directory structure retrieval operation.
|
|
52
43
|
#
|
|
53
44
|
# This method traverses the directory structure starting from the specified
|
|
54
|
-
# path and returns a structured representation of
|
|
55
|
-
#
|
|
45
|
+
# path and returns a structured representation of the file system hierarchy.
|
|
46
|
+
#
|
|
47
|
+
# @param tool_call [Ollama::Tool::Call] the tool call object containing
|
|
48
|
+
# function details
|
|
56
49
|
#
|
|
57
|
-
# @param tool_call [Ollama::Tool::Call] the tool call object containing function details
|
|
58
50
|
# @param opts [Hash] additional options
|
|
59
51
|
# @return [String] the directory structure as a JSON string
|
|
60
|
-
# @raise [StandardError] if there's an issue with directory traversal or JSON
|
|
52
|
+
# @raise [StandardError] if there's an issue with directory traversal or JSON
|
|
53
|
+
# serialization
|
|
61
54
|
def execute(tool_call, **opts)
|
|
62
|
-
|
|
55
|
+
config = opts[:config]
|
|
56
|
+
path = Pathname.new(tool_call.function.arguments.path || '.')
|
|
63
57
|
|
|
64
|
-
structure = generate_structure(path)
|
|
58
|
+
structure = generate_structure(path, exclude: config.tools.directory_structure.exclude?)
|
|
65
59
|
structure.to_json
|
|
66
60
|
end
|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# This method provides a standardized way to serialize the tool definition
|
|
71
|
-
# for use in tool calling systems.
|
|
72
|
-
#
|
|
73
|
-
# @return [Hash] a hash representation of the tool
|
|
74
|
-
def to_hash
|
|
75
|
-
tool.to_hash
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
|
|
80
|
-
# Generates a directory structure representation with files and
|
|
81
|
-
# subdirectories.
|
|
82
|
-
#
|
|
83
|
-
# @param path [String] the path to start generating the structure from,
|
|
84
|
-
# defaults to current directory
|
|
85
|
-
#
|
|
86
|
-
# @return [Array<Hash>, Hash] an array of hashes representing files and
|
|
87
|
-
# directories, or a hash with error information if an exception occurs
|
|
88
|
-
# @return [Array] an empty array if the path is invalid or has no children
|
|
89
|
-
# @return [Hash] a hash with error details if an exception is raised during processing
|
|
90
|
-
#
|
|
91
|
-
# @example Generate structure for current directory
|
|
92
|
-
# generate_structure
|
|
93
|
-
#
|
|
94
|
-
# @example Generate structure for a specific path
|
|
95
|
-
# generate_structure('/path/to/directory')
|
|
96
|
-
#
|
|
97
|
-
# @note Hidden files and directories (starting with '.') are skipped
|
|
98
|
-
# @note Symbolic links are skipped
|
|
99
|
-
# @note The method uses recursive calls to traverse subdirectories
|
|
100
|
-
# @note If an error occurs during traversal, it returns a hash with error details
|
|
101
|
-
def generate_structure(path = ?.)
|
|
102
|
-
path = Pathname.new(path).expand_path
|
|
103
|
-
entries = []
|
|
104
|
-
path.children.sort.each do |child|
|
|
105
|
-
# Skip hidden files/directories
|
|
106
|
-
next if child.basename.to_s.start_with?('.')
|
|
107
|
-
# Skip symlinks
|
|
108
|
-
next if child.symlink?
|
|
109
|
-
|
|
110
|
-
if child.directory?
|
|
111
|
-
entries << {
|
|
112
|
-
type: 'directory',
|
|
113
|
-
name: child.basename.to_s,
|
|
114
|
-
children: generate_structure(child)
|
|
115
|
-
}
|
|
116
|
-
elsif child.file?
|
|
117
|
-
entries << {
|
|
118
|
-
type: 'file',
|
|
119
|
-
name: child.basename.to_s
|
|
120
|
-
}
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
entries
|
|
124
|
-
rescue => e
|
|
125
|
-
{ error: e.class, message: e.message }
|
|
126
|
-
end
|
|
127
|
-
end
|
|
62
|
+
self
|
|
63
|
+
end.register
|