ollama_chat 0.0.123 → 0.0.125
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 +155 -0
- data/Rakefile +2 -2
- data/bin/ollama_chat_log +1 -1
- data/lib/ollama_chat/asr.rb +3 -3
- data/lib/ollama_chat/chat.rb +21 -27
- data/lib/ollama_chat/clipboard.rb +4 -4
- data/lib/ollama_chat/collection_search.rb +162 -0
- data/lib/ollama_chat/commands.rb +85 -55
- data/lib/ollama_chat/compaction.rb +36 -15
- data/lib/ollama_chat/config_handling.rb +13 -7
- data/lib/ollama_chat/conversation.rb +8 -5
- data/lib/ollama_chat/database/migrations/011_add_trigger_to_sessions.rb +7 -0
- data/lib/ollama_chat/database/models/app_state.rb +7 -5
- data/lib/ollama_chat/database/models/session.rb +15 -2
- data/lib/ollama_chat/database/session_locking.rb +3 -2
- data/lib/ollama_chat/dialog.rb +8 -8
- data/lib/ollama_chat/favourites_management.rb +4 -4
- data/lib/ollama_chat/feedback.rb +41 -0
- data/lib/ollama_chat/file_editing.rb +5 -4
- data/lib/ollama_chat/follow_chat.rb +33 -28
- data/lib/ollama_chat/information.rb +66 -15
- data/lib/ollama_chat/input_content.rb +5 -5
- data/lib/ollama_chat/message_editing.rb +3 -3
- data/lib/ollama_chat/message_list.rb +69 -13
- data/lib/ollama_chat/message_output.rb +7 -7
- data/lib/ollama_chat/model_handling.rb +71 -45
- data/lib/ollama_chat/oc.rb +3 -1
- data/lib/ollama_chat/ollama_chat_config/default_config.yml +48 -8
- data/lib/ollama_chat/parsing.rb +7 -7
- data/lib/ollama_chat/personae_management.rb +45 -27
- data/lib/ollama_chat/prompt_management.rb +54 -50
- data/lib/ollama_chat/rag_handling.rb +87 -25
- data/lib/ollama_chat/server_socket.rb +1 -1
- data/lib/ollama_chat/session_management.rb +146 -55
- data/lib/ollama_chat/source_fetching.rb +9 -7
- data/lib/ollama_chat/speaker.rb +15 -1
- data/lib/ollama_chat/state_selectors.rb +3 -2
- data/lib/ollama_chat/switches.rb +1 -3
- data/lib/ollama_chat/system_prompt_management.rb +14 -2
- data/lib/ollama_chat/tool_calling.rb +5 -5
- data/lib/ollama_chat/tools/concern.rb +1 -0
- data/lib/ollama_chat/tools/directory_structure.rb +6 -6
- data/lib/ollama_chat/tools/execute_shell.rb +1 -1
- data/lib/ollama_chat/tools/forget.rb +113 -0
- data/lib/ollama_chat/tools/memorize.rb +116 -0
- data/lib/ollama_chat/tools/roll_dice.rb +1 -1
- data/lib/ollama_chat/tools/run_tests.rb +1 -1
- data/lib/ollama_chat/tools/search_knowledge.rb +12 -60
- data/lib/ollama_chat/tools.rb +2 -0
- data/lib/ollama_chat/utils/audio_player.rb +1 -0
- data/lib/ollama_chat/utils/tag_resolver.rb +1 -1
- data/lib/ollama_chat/version.rb +1 -1
- data/lib/ollama_chat/vim.rb +4 -2
- data/lib/ollama_chat/web_searching.rb +8 -8
- data/lib/ollama_chat.rb +2 -0
- data/ollama_chat.gemspec +7 -7
- data/spec/ollama_chat/asr_spec.rb +5 -5
- data/spec/ollama_chat/chat_spec.rb +3 -3
- data/spec/ollama_chat/clipboard_spec.rb +3 -3
- data/spec/ollama_chat/collection_search_spec.rb +134 -0
- data/spec/ollama_chat/command_concern_spec.rb +174 -0
- data/spec/ollama_chat/commands_spec.rb +354 -6
- data/spec/ollama_chat/compaction_spec.rb +21 -15
- data/spec/ollama_chat/config_handling_spec.rb +9 -12
- data/spec/ollama_chat/conversation_spec.rb +17 -18
- data/spec/ollama_chat/favourites_management_spec.rb +6 -6
- data/spec/ollama_chat/file_editing_spec.rb +3 -3
- data/spec/ollama_chat/follow_chat_spec.rb +3 -1
- data/spec/ollama_chat/http_handling_spec.rb +10 -10
- data/spec/ollama_chat/information_spec.rb +4 -4
- data/spec/ollama_chat/input_content_spec.rb +1 -1
- data/spec/ollama_chat/message_editing_spec.rb +5 -5
- data/spec/ollama_chat/message_list_spec.rb +76 -2
- data/spec/ollama_chat/message_output_spec.rb +7 -7
- data/spec/ollama_chat/parsing_spec.rb +5 -5
- data/spec/ollama_chat/prompt_management_spec.rb +12 -14
- data/spec/ollama_chat/rag_handling_spec.rb +31 -32
- data/spec/ollama_chat/session_management_spec.rb +82 -11
- data/spec/ollama_chat/source_fetching_spec.rb +4 -4
- data/spec/ollama_chat/speaker_spec.rb +92 -0
- data/spec/ollama_chat/state_selectors_spec.rb +1 -1
- data/spec/ollama_chat/system_prompt_management_spec.rb +4 -4
- data/spec/ollama_chat/think_control_spec.rb +2 -2
- data/spec/ollama_chat/tool_calling_spec.rb +4 -4
- data/spec/ollama_chat/tools/execute_shell_spec.rb +7 -7
- data/spec/ollama_chat/tools/forget_spec.rb +144 -0
- data/spec/ollama_chat/tools/memorize_spec.rb +204 -0
- data/spec/ollama_chat/tools/resolve_tag_spec.rb +5 -9
- data/spec/ollama_chat/tools/search_knowledge_spec.rb +24 -19
- data/spec/ollama_chat/tts_spec.rb +13 -19
- data/spec/ollama_chat/utils/audio_player_spec.rb +5 -3
- data/spec/ollama_chat/web_searching_spec.rb +2 -2
- metadata +25 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3de94c682281a4667e89feee589883bd33acf082165f37ce443072117a773167
|
|
4
|
+
data.tar.gz: 94cccbb92af48198a70879cf1028a0aebfe0cda49e25ccd82352cb35d9054bf7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d3bad30e34ad309fe6c2f2263802af2778eded9459affc6c6713c6383f58706fe54411e7a4b6f087c487718d0a6ea8e07e90dfed22ca1a64b563d207ce61287e
|
|
7
|
+
data.tar.gz: 8f9befaa4a6a519b8f7b4424f0d16380663b863e039f08142fc667b01ecb83aa989b9e635ea5373b22191c7ba8f568e97dd210d732d1d6d5099dd3e5cffb74ee
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,160 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-10-03 v0.0.125
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
* Session-scoped trigger injection mechanism:
|
|
8
|
+
* New migration `011_add_trigger_to_sessions` adding a `:text` JSON column to
|
|
9
|
+
`sessions`.
|
|
10
|
+
* `Session#trigger` attribute with `plugin :serialization, :json` and `nil`
|
|
11
|
+
default.
|
|
12
|
+
* `CollectionSearch#trigger_inject` dispatch logic including embedding
|
|
13
|
+
guards, collection switching, document finding, and reranking.
|
|
14
|
+
* New `/session trigger edit` command registered in `commands.rb`.
|
|
15
|
+
* `edit_session_trigger` method in `session_management.rb` supporting dual
|
|
16
|
+
chooser and prefill.
|
|
17
|
+
* Trigger configuration display (⚡ block) added to `info_rag` in
|
|
18
|
+
`information.rb`.
|
|
19
|
+
* `runtime_information_inject` extracted from `chat.rb` into
|
|
20
|
+
`information.rb`.
|
|
21
|
+
* New prompt contexts: `prompts.rerank` (`default`, `memory_rerank`,
|
|
22
|
+
`lorebook_rerank`), `prompts.prompt.snippets_retrieval`, and
|
|
23
|
+
`prompts.prompt.snippets_trigger`.
|
|
24
|
+
* `persona_name` line added to `prompts.system.persona`.
|
|
25
|
+
* `TagResult` changed from `Data.define` to `Struct.new` to allow mutable
|
|
26
|
+
fields.
|
|
27
|
+
* `∅` fallback for empty patterns in `collection_stats`.
|
|
28
|
+
* Info-level logging for embedding requests/responses and rerank survivors in
|
|
29
|
+
`collection_search.rb`.
|
|
30
|
+
* New spec file `collection_search_spec.rb` with 7 examples for
|
|
31
|
+
`#trigger_inject`.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
* `SessionLocking#lock?` now accepts a required `chat:` keyword argument to
|
|
36
|
+
decouple feedback delegation from the host object.
|
|
37
|
+
* `search_knowledge.rb` updated to use `snippets_retrieval` DB prompt,
|
|
38
|
+
`collection` block, and `database_collection?` guard.
|
|
39
|
+
* `@embedding` instance variable swept to `embedding` accessor in
|
|
40
|
+
`information.rb`, `source_fetching.rb`, and `web_searching.rb`.
|
|
41
|
+
* `Tins::Duration#format` used for compaction voice timing in
|
|
42
|
+
`report_compaction`, replacing `%.1f seconds`.
|
|
43
|
+
* `tins` dependency bumped from `~> **1.58**` to `~> **1.60**` to support
|
|
44
|
+
unpadded `%H`, `%M`, and `%u` directives.
|
|
45
|
+
* `resolve_tag_spec.rb` updated to replace an unreachable rescue-path test with
|
|
46
|
+
a "tagged file does not exist" test.
|
|
47
|
+
* `search_knowledge_spec.rb` updated for new prompt names, `collection` block,
|
|
48
|
+
and `tags_set` doubles.
|
|
49
|
+
* `compaction_spec.rb` regex updated to match the new duration format.
|
|
50
|
+
* `Tins::GO.go` flag string reordered in `bin/ollama_chat_log`.
|
|
51
|
+
|
|
52
|
+
### Fixed
|
|
53
|
+
|
|
54
|
+
* Spec expectations in `session_management_spec.rb`, `parsing_spec.rb`, and
|
|
55
|
+
`source_fetching_spec.rb` updated to use Ruby 3.1+ hash shorthand `chat:`.
|
|
56
|
+
|
|
57
|
+
### Tests
|
|
58
|
+
|
|
59
|
+
* Added specs for `CommandConcern` class methods:
|
|
60
|
+
* `help_message` via `OllamaChat::Commands`: table structure, category
|
|
61
|
+
headers, pattern filtering, subcommand expansion, and optional/mandatory
|
|
62
|
+
asterisks.
|
|
63
|
+
* `command_completions`: array shape, bare names, subcommand variants, and
|
|
64
|
+
sort order.
|
|
65
|
+
* `Command` class: `execute_if_match?` (match, captures, nil-regexp path,
|
|
66
|
+
no-context error), `completions` (product, optional bare, strict),
|
|
67
|
+
`command_names`, and `optional?`.
|
|
68
|
+
* Duplicate registration guard against the real `:copy` entry in
|
|
69
|
+
`OllamaChat::Commands`.
|
|
70
|
+
* Added specs for remaining command dispatch gaps:
|
|
71
|
+
* `/paste -i` stdin path via `paste_from_stdin`.
|
|
72
|
+
* `/last -v` voice output via `speak_last`.
|
|
73
|
+
* `/regenerate` success path (drop + return content).
|
|
74
|
+
* `/prompt delete` routes to `choose_and_delete_prompt`.
|
|
75
|
+
* `/web` without explicit count passes `nil`.
|
|
76
|
+
* `/character` JSON success paths: `info` (pager), `load` (raw content),
|
|
77
|
+
`import` (delegation).
|
|
78
|
+
* `/character` PNG `load` with `PNGMetadataExtractor`.
|
|
79
|
+
* Added specs for `SessionManagement#edit_session_trigger`:
|
|
80
|
+
* Setting trigger from `nil` to a configured hash with `enabled`,
|
|
81
|
+
`prompt_name`, and `text_count`.
|
|
82
|
+
* Updating config for an already-configured collection.
|
|
83
|
+
* Switching the trigger to a different collection name.
|
|
84
|
+
* Added specs for session model options and trigger edit:
|
|
85
|
+
* `/session model options` delegates to `edit_session_model_options` and
|
|
86
|
+
returns `:next`.
|
|
87
|
+
* `/session trigger edit` delegates to `edit_session_trigger` and returns
|
|
88
|
+
`:next`.
|
|
89
|
+
|
|
90
|
+
## 2026-10-02 v0.0.124
|
|
91
|
+
|
|
92
|
+
### Added
|
|
93
|
+
|
|
94
|
+
* `OllamaChat::Feedback` module to replace raw `STDOUT.puts` and
|
|
95
|
+
`STDERR.puts` calls with typed, testable system notifications.
|
|
96
|
+
* Supports types: `:success`, `:cancel`, `:denied`, `:warn`, `:info`,
|
|
97
|
+
`:alarm`.
|
|
98
|
+
* Integrates with `use_pager` and handles newline formatting via
|
|
99
|
+
`output.print`.
|
|
100
|
+
* `memorize` tool to embed timestamped entries into per-persona
|
|
101
|
+
`memory-<persona_name>` Documentrix collections.
|
|
102
|
+
* `forget` tool to clear a single memory record by its ISO-8601 timestamp
|
|
103
|
+
tag.
|
|
104
|
+
* `/collection query` subcommand to debug collections, supporting optional
|
|
105
|
+
`-e` (edit) and `-r` (rerank) flags.
|
|
106
|
+
* `/config env` subcommand to display the Ollama Chat environment variable
|
|
107
|
+
tree using `OC.view`.
|
|
108
|
+
* `/last -v` flag to voice previous assistant messages via
|
|
109
|
+
`MessageList#speak_last`.
|
|
110
|
+
* `OllamaChat::CollectionSearch` module extracted from `SearchKnowledge`
|
|
111
|
+
tool, providing `find_document_records` and `rerank_records` methods mixed
|
|
112
|
+
into `Chat`.
|
|
113
|
+
* `refresh_system_prompt` method in `SystemPromptManagement` to
|
|
114
|
+
re-interpolate the active system prompt with fresh `persona` and
|
|
115
|
+
`runtime_info` values after collection mutations.
|
|
116
|
+
* Voice announcement in the compaction report, including
|
|
117
|
+
`context_filled_before`, `context_filled_after`, and `duration` in
|
|
118
|
+
`Compaction::Result`.
|
|
119
|
+
* `Speaker#wait_for_speaker` method to join the speaker thread and block
|
|
120
|
+
until completion.
|
|
121
|
+
* `spec/ollama_chat/speaker_spec.rb` with tests for `OllamaChat::Speaker`.
|
|
122
|
+
|
|
123
|
+
### Changed
|
|
124
|
+
|
|
125
|
+
* Replaced `str % {…}` with `named_placeholders_interpolate({…})` for
|
|
126
|
+
DB-prompt call sites to handle unknown placeholders gracefully.
|
|
127
|
+
* Updated `directory_structure` tool description from "Tree viewer" to "File
|
|
128
|
+
locator" and added guidance to prefer it over shell `find` or `ls -R`.
|
|
129
|
+
* Routed prompt change feedback through the `chat` instance in `app_state.rb`
|
|
130
|
+
to ensure messages target the active chat session.
|
|
131
|
+
* Switched spec stubs from `allow` to `expect` in `http_handling_spec.rb`,
|
|
132
|
+
`message_list_spec.rb`, `execute_shell_spec.rb`, `tts_spec.rb`,
|
|
133
|
+
`compaction_spec.rb`, `audio_player_spec.rb`, and `asr_spec.rb` to enforce
|
|
134
|
+
required calls.
|
|
135
|
+
* Bumped `const_conf` dependency from `~> **0.3**` to `~> **0.9**`.
|
|
136
|
+
* Updated `rerank_records` to accept an optional `prompt_name:` keyword
|
|
137
|
+
argument.
|
|
138
|
+
* Improved new session name generation by extracting
|
|
139
|
+
`new_random_session_name` into `SessionManagement` and handling `nil`
|
|
140
|
+
cancellation in `determine_valid_new_name_for_session`.
|
|
141
|
+
* Allowed naming new sessions via `choose_session` by threading an optional
|
|
142
|
+
`name:` keyword through `new_session` and `Session.with_defaults`.
|
|
143
|
+
* Logged stored memory text in the `memorize` tool by adding `text:` to the
|
|
144
|
+
`chat.log` data hash.
|
|
145
|
+
* Extracted context usage helpers `context_usage_colored` and
|
|
146
|
+
`context_percentage` to eliminate duplicated format logic.
|
|
147
|
+
* Updated `output_eval_stats` in `follow_chat.rb` to print colored context
|
|
148
|
+
usage.
|
|
149
|
+
|
|
150
|
+
### Fixed
|
|
151
|
+
|
|
152
|
+
* Fixed indentation bug in `model_handling.rb` import/export by changing
|
|
153
|
+
`sub` to `gsub`.
|
|
154
|
+
* Guarded source link rendering in `search_knowledge` against `nil` or empty
|
|
155
|
+
`source` values.
|
|
156
|
+
* Suppressed `report_on_exception` on `AudioPlayer` background thread.
|
|
157
|
+
|
|
3
158
|
## 2026-09-25 v0.0.123
|
|
4
159
|
|
|
5
160
|
### Added
|
data/Rakefile
CHANGED
|
@@ -49,13 +49,13 @@ GemHadar do
|
|
|
49
49
|
dependency 'reverse_markdown', '~> 3.0'
|
|
50
50
|
dependency 'kramdown-ansi', '~> 0.5'
|
|
51
51
|
dependency 'complex_config', '~> 0.22', '>= 0.22.2'
|
|
52
|
-
dependency 'tins', '~> 1.
|
|
52
|
+
dependency 'tins', '~> 1.60'
|
|
53
53
|
dependency 'search_ui', '>= 0.2'
|
|
54
54
|
dependency 'amatch', '~> 0.4'
|
|
55
55
|
dependency 'pdf-reader', '~> 2.0'
|
|
56
56
|
dependency 'bigdecimal', '~> 3.1'
|
|
57
57
|
dependency 'csv', '~> 3.0'
|
|
58
|
-
dependency 'const_conf', '~> 0.
|
|
58
|
+
dependency 'const_conf', '~> 0.9'
|
|
59
59
|
dependency 'context_spook', '~> 1.6'
|
|
60
60
|
dependency 'infobar', '>= 0.13.1'
|
|
61
61
|
dependency 'rubyzip', '~> 3.0'
|
data/bin/ollama_chat_log
CHANGED
data/lib/ollama_chat/asr.rb
CHANGED
|
@@ -38,11 +38,11 @@ module OllamaChat
|
|
|
38
38
|
executable = Shellwords.split(cmd).first
|
|
39
39
|
result = system(cmd, out: File::NULL, err: File::NULL)
|
|
40
40
|
if result.nil?
|
|
41
|
-
|
|
41
|
+
chat.feedback("ASR: #{executable} not found in PATH.", type: :warn)
|
|
42
42
|
return
|
|
43
43
|
end
|
|
44
44
|
unless File.size(wav.path) > 0
|
|
45
|
-
|
|
45
|
+
chat.feedback("ASR: #{executable} produced no output.", type: :warn)
|
|
46
46
|
return
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -61,7 +61,7 @@ module OllamaChat
|
|
|
61
61
|
result = JSON.parse(response.body) rescue nil
|
|
62
62
|
end
|
|
63
63
|
chat.log(:error, e, data: { status:, result: })
|
|
64
|
-
|
|
64
|
+
chat.feedback("ASR transcription failed: #{e.message}", type: :warn)
|
|
65
65
|
nil
|
|
66
66
|
end
|
|
67
67
|
|
data/lib/ollama_chat/chat.rb
CHANGED
|
@@ -34,6 +34,7 @@ require 'zlib'
|
|
|
34
34
|
class OllamaChat::Chat
|
|
35
35
|
include Tins::GO
|
|
36
36
|
include Term::ANSIColor
|
|
37
|
+
include OllamaChat::Feedback
|
|
37
38
|
include OllamaChat::TokenEstimator
|
|
38
39
|
include OllamaChat::HTTPHandling
|
|
39
40
|
include OllamaChat::Commands
|
|
@@ -64,6 +65,7 @@ class OllamaChat::Chat
|
|
|
64
65
|
include OllamaChat::ConfigHandling
|
|
65
66
|
include OllamaChat::PersonaeManagement
|
|
66
67
|
include OllamaChat::SessionManagement
|
|
68
|
+
include OllamaChat::CollectionSearch
|
|
67
69
|
include OllamaChat::RAGHandling
|
|
68
70
|
include OllamaChat::FavouritesManagement
|
|
69
71
|
include OllamaChat::PromptHandling
|
|
@@ -195,7 +197,7 @@ class OllamaChat::Chat
|
|
|
195
197
|
abort "Failed to use to model: #{e}"
|
|
196
198
|
end
|
|
197
199
|
|
|
198
|
-
|
|
200
|
+
feedback ?\n
|
|
199
201
|
|
|
200
202
|
setup_persona_from_session
|
|
201
203
|
setup_system_prompt
|
|
@@ -203,7 +205,7 @@ class OllamaChat::Chat
|
|
|
203
205
|
print_welcome
|
|
204
206
|
info_session
|
|
205
207
|
|
|
206
|
-
|
|
208
|
+
feedback("\nType /help to display the chat help.")
|
|
207
209
|
|
|
208
210
|
interact_with_user
|
|
209
211
|
end
|
|
@@ -312,10 +314,11 @@ class OllamaChat::Chat
|
|
|
312
314
|
# instance when a background thread was spawned (so callers can call
|
|
313
315
|
# `cancel_speaking`), or `nil` for inline playback or when voice is off
|
|
314
316
|
def speak(text, background: true)
|
|
317
|
+
session or return
|
|
315
318
|
voice.on? or return
|
|
316
|
-
|
|
319
|
+
current_voice = session.current_voice.full? or return
|
|
317
320
|
voice_handler.method_defined?(:speak) or return
|
|
318
|
-
voice_handler.new(chat: self, voice:).speak(text, background:)
|
|
321
|
+
voice_handler.new(chat: self, voice: current_voice).speak(text, background:)
|
|
319
322
|
end
|
|
320
323
|
|
|
321
324
|
# Returns a human-readable string representation of the Chat object,
|
|
@@ -434,19 +437,19 @@ class OllamaChat::Chat
|
|
|
434
437
|
case what
|
|
435
438
|
when 'messages', nil
|
|
436
439
|
messages.clear
|
|
437
|
-
|
|
440
|
+
feedback("Cleared messages.", type: :info)
|
|
438
441
|
when 'links'
|
|
439
442
|
links.clear
|
|
440
|
-
|
|
443
|
+
feedback("Cleared links.", type: :info)
|
|
441
444
|
when 'history'
|
|
442
445
|
clear_history
|
|
443
|
-
|
|
446
|
+
feedback("Cleared history.", type: :info)
|
|
444
447
|
when 'tags'
|
|
445
448
|
@documents.clear
|
|
446
|
-
|
|
449
|
+
feedback("Cleared all tags.", type: :info)
|
|
447
450
|
when 'images'
|
|
448
451
|
messages.clear_images
|
|
449
|
-
|
|
452
|
+
feedback("Cleared all images.", type: :info)
|
|
450
453
|
when 'all'
|
|
451
454
|
if confirm?(
|
|
452
455
|
prompt: '🔔 Are you sure to clear messages and collection? (y/n) ',
|
|
@@ -457,9 +460,9 @@ class OllamaChat::Chat
|
|
|
457
460
|
@documents.clear
|
|
458
461
|
links.clear
|
|
459
462
|
clear_history
|
|
460
|
-
|
|
463
|
+
feedback("Cleared messages and collection #{bold{collection}}.", type: :info)
|
|
461
464
|
else
|
|
462
|
-
|
|
465
|
+
feedback("Denied.", type: :denied)
|
|
463
466
|
end
|
|
464
467
|
end
|
|
465
468
|
persona_profile
|
|
@@ -517,7 +520,7 @@ class OllamaChat::Chat
|
|
|
517
520
|
type = message.type.full?(:to_sym) || :socket_input
|
|
518
521
|
content = message.content
|
|
519
522
|
@parse_content = message.parse
|
|
520
|
-
|
|
523
|
+
feedback color(112) { "Received a server socket message. Processing now…" }, type: :info
|
|
521
524
|
else
|
|
522
525
|
raise
|
|
523
526
|
end
|
|
@@ -551,17 +554,8 @@ class OllamaChat::Chat
|
|
|
551
554
|
group_uuid:
|
|
552
555
|
)
|
|
553
556
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
messages << OllamaChat::Message.new(
|
|
557
|
-
role: 'user',
|
|
558
|
-
tool_name: ,
|
|
559
|
-
sender_name: tool_name,
|
|
560
|
-
content: dynamic_runtime_information,
|
|
561
|
-
images: @images.dup,
|
|
562
|
-
group_uuid:
|
|
563
|
-
)
|
|
564
|
-
end
|
|
557
|
+
runtime_information_inject(group_uuid:)
|
|
558
|
+
trigger_inject(content, group_uuid:)
|
|
565
559
|
end
|
|
566
560
|
@images.clear
|
|
567
561
|
handler = OllamaChat::FollowChat.new(
|
|
@@ -619,13 +613,13 @@ class OllamaChat::Chat
|
|
|
619
613
|
msg = "Currently lost connection to ollama server and cannot send command."
|
|
620
614
|
log(:warn, msg, warn: true)
|
|
621
615
|
rescue Interrupt
|
|
622
|
-
|
|
616
|
+
feedback("Type /quit to quit.")
|
|
623
617
|
ensure
|
|
624
618
|
self.server_socket_message = nil
|
|
625
619
|
end
|
|
626
620
|
0
|
|
627
621
|
rescue OllamaChat::OllamaChatQuitError
|
|
628
|
-
|
|
622
|
+
feedback("Goodbye.")
|
|
629
623
|
rescue ComplexConfig::AttributeMissing, ComplexConfig::ConfigurationSyntaxError => e
|
|
630
624
|
log(:error, e)
|
|
631
625
|
fix_config(e)
|
|
@@ -718,7 +712,7 @@ class OllamaChat::Chat
|
|
|
718
712
|
# @note Progress is reported to STDOUT during processing
|
|
719
713
|
def add_documents_from_argv(document_list)
|
|
720
714
|
if document_list.any?(&:empty?)
|
|
721
|
-
|
|
715
|
+
feedback("Clearing collection #{bold{documents.collection}}.", type: :info)
|
|
722
716
|
documents.clear
|
|
723
717
|
document_list.reject!(&:empty?)
|
|
724
718
|
end
|
|
@@ -730,7 +724,7 @@ class OllamaChat::Chat
|
|
|
730
724
|
File.expand_path(doc)
|
|
731
725
|
end
|
|
732
726
|
end
|
|
733
|
-
|
|
727
|
+
feedback("Collection #{bold{documents.collection}}: Adding #{document_list.size} documents…", type: :info)
|
|
734
728
|
count = 1
|
|
735
729
|
document_list.each_slice(25) do |docs|
|
|
736
730
|
docs.each do |doc|
|
|
@@ -116,11 +116,11 @@ module OllamaChat::Clipboard
|
|
|
116
116
|
text = last_message_content(content: false)
|
|
117
117
|
perform_copy_to_clipboard(text:, edit:)
|
|
118
118
|
log(:info, "Copied to clipboard", data: { bytes: format_bytes(text.bytesize) }) if text
|
|
119
|
-
|
|
119
|
+
feedback("The last response has been successfully copied to the system clipboard.", type: :info)
|
|
120
120
|
true
|
|
121
121
|
rescue OllamaChat::OllamaChatError => e
|
|
122
122
|
log(:error, e, data: { method: __method__ })
|
|
123
|
-
|
|
123
|
+
feedback(e.message, type: :warn)
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
# Pastes content from the system clipboard into the chat.
|
|
@@ -137,11 +137,11 @@ module OllamaChat::Clipboard
|
|
|
137
137
|
def paste_from_clipboard(edit: false)
|
|
138
138
|
result = perform_paste_from_clipboard(edit:)
|
|
139
139
|
log(:info, "Pasted from clipboard", data: { bytes: format_bytes(result.bytesize) }) if result
|
|
140
|
-
|
|
140
|
+
feedback("The clipboard content has been successfully copied to the chat.", type: :info)
|
|
141
141
|
result
|
|
142
142
|
rescue OllamaChat::OllamaChatError => e
|
|
143
143
|
log(:error, e, data: { method: __method__ })
|
|
144
|
-
|
|
144
|
+
feedback(e.message, type: :warn)
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
# Read the full text from STDIN and return it.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Shared collection search primitives for document retrieval and LLM
|
|
2
|
+
# reranking.
|
|
3
|
+
#
|
|
4
|
+
# Extracted from {OllamaChat::Tools::SearchKnowledge} so that the
|
|
5
|
+
# `/collection query` command and the trigger mechanism can reuse
|
|
6
|
+
# the same search + rerank pipeline without duplicating logic.
|
|
7
|
+
module OllamaChat::CollectionSearch
|
|
8
|
+
include Kramdown::ANSI::Width
|
|
9
|
+
|
|
10
|
+
# Searches the active document collection for records matching
|
|
11
|
+
# the given query.
|
|
12
|
+
#
|
|
13
|
+
# @param query [String] the search query
|
|
14
|
+
# @param tags [Array<String>, nil] tag filter
|
|
15
|
+
# @param text_size [Integer, nil] max snippet size
|
|
16
|
+
# @param text_count [Integer, nil] max number of snippets
|
|
17
|
+
# @param min_similarity [Float, nil] minimum similarity threshold
|
|
18
|
+
#
|
|
19
|
+
# @return [Array<Documentrix::Utils::TagResult>] matching records
|
|
20
|
+
def find_document_records(
|
|
21
|
+
query,
|
|
22
|
+
tags: nil, text_size: nil, text_count: nil, min_similarity: nil
|
|
23
|
+
)
|
|
24
|
+
tags = Documentrix::Utils::Tags.new(
|
|
25
|
+
tags, valid_tag: /\A#*([-\w.\]\[]+)/
|
|
26
|
+
)
|
|
27
|
+
effective_query = query.first(
|
|
28
|
+
config.embedding.model.context_length
|
|
29
|
+
)
|
|
30
|
+
embedding_prompt = config.embedding.model.prompt?
|
|
31
|
+
log(
|
|
32
|
+
:info, 'Embedding search request',
|
|
33
|
+
data: {
|
|
34
|
+
model: config.embedding.model.to_h,
|
|
35
|
+
collection: collection,
|
|
36
|
+
query: effective_query,
|
|
37
|
+
prompt: embedding_prompt,
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
records = @documents.find_where(
|
|
41
|
+
effective_query,
|
|
42
|
+
tags:,
|
|
43
|
+
prompt: embedding_prompt,
|
|
44
|
+
text_size:,
|
|
45
|
+
text_count:,
|
|
46
|
+
min_similarity:
|
|
47
|
+
)
|
|
48
|
+
log(
|
|
49
|
+
:info, 'Embedding search response',
|
|
50
|
+
data: {
|
|
51
|
+
collection: collection,
|
|
52
|
+
query: effective_query,
|
|
53
|
+
result_count: records.size,
|
|
54
|
+
top: records.first&.similarity&.round(4),
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
records
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Reranks a set of records using the LLM with the `rerank` prompt.
|
|
61
|
+
#
|
|
62
|
+
# @param query [String] the original search query
|
|
63
|
+
# @param records [Array<Documentrix::Utils::TagResult>] candidate records
|
|
64
|
+
# @param prompt_name [String, nil] the prompt template to use for
|
|
65
|
+
# reranking; defaults to `'rerank'`
|
|
66
|
+
# @return [Array<Documentrix::Utils::TagResult>] filtered records
|
|
67
|
+
def rerank_records(query, records, prompt_name: nil)
|
|
68
|
+
candidates = records.each_with_index.map { |r, i|
|
|
69
|
+
"[#{i}] #{r.tags_set.to_s(link: false)} #{truncate(r.text.squeeze, length: 300)}"
|
|
70
|
+
}.join("\n")
|
|
71
|
+
|
|
72
|
+
prompt_name ||= 'default'
|
|
73
|
+
rerank_prompt = prompt(prompt_name, context: 'rerank') or
|
|
74
|
+
raise 'missing prompt %s' % prompt_name.inspect
|
|
75
|
+
rerank_prompt = rerank_prompt.to_s
|
|
76
|
+
.named_placeholders_interpolate({ query:, candidates: })
|
|
77
|
+
|
|
78
|
+
begin
|
|
79
|
+
if response = generate(prompt: rerank_prompt).full?
|
|
80
|
+
indices = response.scan(/\d+/).map(&:to_i)
|
|
81
|
+
.select { |i| (0...records.size).include?(i) }
|
|
82
|
+
records = records.values_at(*indices) if indices.any?
|
|
83
|
+
end
|
|
84
|
+
rescue => e
|
|
85
|
+
log(:error, e, data: { context: 'rerank' })
|
|
86
|
+
end
|
|
87
|
+
records
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Injects relevant snippets from the session's trigger collection
|
|
91
|
+
# into the conversation as a tool message.
|
|
92
|
+
#
|
|
93
|
+
# Reads the first entry of the session's `trigger` hash, performs a
|
|
94
|
+
# vector search in the configured collection, reranks the candidates
|
|
95
|
+
# via the LLM, and appends a `trigger_inject` tool message containing
|
|
96
|
+
# the surviving snippets (text, similarity, tags). The message is
|
|
97
|
+
# grouped with the current user message via `group_uuid`.
|
|
98
|
+
#
|
|
99
|
+
# @param content [String] the parsed user message text used as the
|
|
100
|
+
# search query
|
|
101
|
+
# @param group_uuid [String, nil] the group UUID for message grouping
|
|
102
|
+
# (shared with the user message and runtime-info message)
|
|
103
|
+
#
|
|
104
|
+
# @return [self, nil] self if snippets were injected, nil if no
|
|
105
|
+
# trigger is configured, the trigger or embedding is disabled, the
|
|
106
|
+
# collection or prompt is missing, or no snippets survived reranking
|
|
107
|
+
def trigger_inject(content, group_uuid:)
|
|
108
|
+
embedding.on? or return
|
|
109
|
+
trigger = session.trigger&.first or return
|
|
110
|
+
collection, config = trigger
|
|
111
|
+
unless config['enabled']
|
|
112
|
+
log(:info, 'Trigger for collection %s disabled.' % collection)
|
|
113
|
+
return
|
|
114
|
+
end
|
|
115
|
+
unless col = database_collection?(collection)
|
|
116
|
+
log(:error, 'Unknown collection named %s' % collection)
|
|
117
|
+
return
|
|
118
|
+
end
|
|
119
|
+
prompt_name = config['prompt_name']
|
|
120
|
+
unless rerank_prompt = prompt(prompt_name, context: 'rerank').full?(:to_s)
|
|
121
|
+
log(
|
|
122
|
+
:error, 'Unknown rerank prompt named %s' % prompt_name,
|
|
123
|
+
data: { config: }
|
|
124
|
+
)
|
|
125
|
+
return
|
|
126
|
+
end
|
|
127
|
+
records = []
|
|
128
|
+
switch_collection(collection) do
|
|
129
|
+
records = find_document_records(content, text_count: config['text_count'])
|
|
130
|
+
records.empty? and return
|
|
131
|
+
pre_rerank = records.size
|
|
132
|
+
records = rerank_records(content, records, prompt_name:)
|
|
133
|
+
log(:info, 'Trigger: %d/%d passed rerank for %s' % [ records.size, pre_rerank, collection ],
|
|
134
|
+
data: { collection:, prompt_name: })
|
|
135
|
+
records.empty? and return
|
|
136
|
+
end
|
|
137
|
+
snippets = records.map { |record|
|
|
138
|
+
{
|
|
139
|
+
text: record.text,
|
|
140
|
+
similarity: record.similarity.to_f,
|
|
141
|
+
tags: record.tags_set.to_s(link: false),
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
message_content = {
|
|
145
|
+
prompt: prompt('snippets_trigger').to_s,
|
|
146
|
+
collection: {
|
|
147
|
+
name: collection,
|
|
148
|
+
description: col.description&.to_s,
|
|
149
|
+
},
|
|
150
|
+
snippets:
|
|
151
|
+
}.to_json
|
|
152
|
+
tool_name = 'trigger_inject'
|
|
153
|
+
messages << OllamaChat::Message.new(
|
|
154
|
+
role: 'user',
|
|
155
|
+
tool_name: ,
|
|
156
|
+
sender_name: tool_name,
|
|
157
|
+
content: message_content,
|
|
158
|
+
group_uuid:
|
|
159
|
+
)
|
|
160
|
+
self
|
|
161
|
+
end
|
|
162
|
+
end
|