ollama_chat 0.0.126 → 0.0.127

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93612bbed949bff6bad6cf89f581b324165c0aa672f6f92b675b63a6a2dce7e0
4
- data.tar.gz: 79aac9f7dada8df87035af5bdfd2913d16012af44a781b7a20c44a457163b313
3
+ metadata.gz: a72761f0d0b34c0ba21859589410e914dda692d669a39629e4e6bb8f6e2857ea
4
+ data.tar.gz: 374fa95c62c5510e94c6a7624200a6193a15e9ed842a4b49b5364d43eeecac52
5
5
  SHA512:
6
- metadata.gz: 2eb715b50338ddb686e6aab7debd264591e1018216adef57146237a862a5f37f217a31bb481a724b4bb8fcc99144c1a40679e3048c64cb88f0eaaabd546661fc
7
- data.tar.gz: 8fa30b91ba125939cd506078bcaf6c9549bb43aa6323f9186e0acae218c7814fd8add54aa75b39f907d4affc78889c52a45b2479ec57c4218d76e61e67843ed3
6
+ metadata.gz: ea024b4607101da7037f0234970cb6e655c6d2b1af46fc1eac1b8dc901aebd5ef95a091d079653bfb58e86d381c4bb56173172f9fb0861e9d8a3424667bfdd32
7
+ data.tar.gz: dfbbe6cddbb4aac1f047cb39719ed58e8920e22a42775cb9f18a4d3731d0c53cc7c632733241f58f6cd6c132e5979b0a3117bf38e7e0095417310e839fb7421b
data/CHANGES.md CHANGED
@@ -1,5 +1,67 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-10-06 v0.0.127
4
+
5
+ ## Changes
6
+
7
+ * **Switches and Configuration**
8
+ * Added `memory_trigger` switch, registered in `/toggle` (regexp,
9
+ completion list, help string, and fallback feedback).
10
+ * Refactored `Switch` to accept callable values alongside plain values;
11
+ plain values are now wrapped via a new private `adhoc_closure` method
12
+ using a `UNSET` sentinel for read/write disambiguation.
13
+ * Hardened `Switch#initialize` with an arity guard: callable values
14
+ responding to `:arity` must return `-1` (optional single parameter
15
+ defaulting to `UNSET`), otherwise a `TypeError` is raised.
16
+ * Updated `set` methods in `OllamaChat::Switches` to explicitly return
17
+ `nil` for consistent behavior.
18
+ * Added `@raise [TypeError]` tag to `Switch#initialize` and expanded
19
+ `@param value` YARD documentation for `CombinedSwitch`.
20
+ * Added arity guard (`ask_and_send(:arity) <= 0`) to
21
+ `CombinedSwitch#initialize`.
22
+
23
+ * **Session and Model Updates**
24
+ * Added `trigger_enabled?` and `set_trigger_enabled` to the `Session`
25
+ model to encapsulate trigger hash shape.
26
+ * Added YARD documentation for `adhoc_closure` and the `:memory_trigger`
27
+ reader.
28
+
29
+ * **Conversation and Compaction**
30
+ * Added `/conversation compact summary` command.
31
+ * Introduced `show_compaction_summary` in `Compaction` to retrieve the
32
+ most recent summary message via `messages.find_summary` and render its
33
+ content (narrative + `tool_calls` lookup index) through the pager,
34
+ prefixed with a size header and entry count.
35
+ * Registered the new `compact summary` subcommand in the `/conversation`
36
+ regexp alternation, completion list, help text, and dispatch block.
37
+ * Excluded system-injected messages (`runtime_information`,
38
+ `trigger_inject`) from the compaction summary's `tool_calls` block to
39
+ keep the index focused on user-initiated tool calls.
40
+
41
+ * **RAG and Search**
42
+ * Moved `set_documents_collection` above the `private` keyword in
43
+ `rag_handling.rb` to make it callable from outside `RAGHandling`.
44
+ * Updated `choose_collection` to call `set_documents_collection` instead
45
+ of assigning `@documents.collection` directly.
46
+ * Replaced direct `chat.documents.collection` reads/writes in `chat.rb`
47
+ and `search_knowledge.rb` with public accessors `chat.collection` and
48
+ `chat.set_documents_collection`.
49
+ * Consolidated two `chat.log` calls in `search_knowledge.rb` into one,
50
+ adding `rerank:` and `pre_rerank:` to the structured data hash with
51
+ `.compact`.
52
+ * Removed `String#%` interpolation in the old rerank log line and
53
+ eliminated the `collection_name` local variable.
54
+ * Updated `trigger_inject` in `collection_search.rb` to include the
55
+ surviving `records` array (text, similarity, tags) in the log `data:`
56
+ hash instead of only logging the count.
57
+
58
+ * **Shell Execution**
59
+ * Forced pager for all `execute_shell` output by passing `force: true` to
60
+ `use_pager`, ensuring consistent UX with other pager-based tools.
61
+ * Updated test-mode spec examples to include a `stub_success` call with
62
+ sample stdout to exercise the always-active pager path with real
63
+ content.
64
+
3
65
  ## 2026-10-05 v0.0.126
4
66
 
5
67
  * **Tool Registry Refactor**: Refactored the tool registry to store tool
@@ -712,7 +712,7 @@ class OllamaChat::Chat
712
712
  # @note Progress is reported to STDOUT during processing
713
713
  def add_documents_from_argv(document_list)
714
714
  if document_list.any?(&:empty?)
715
- feedback("Clearing collection #{bold{documents.collection}}.", type: :info)
715
+ feedback("Clearing collection #{bold{collection}}.", type: :info)
716
716
  documents.clear
717
717
  document_list.reject!(&:empty?)
718
718
  end
@@ -724,7 +724,7 @@ class OllamaChat::Chat
724
724
  File.expand_path(doc)
725
725
  end
726
726
  end
727
- feedback("Collection #{bold{documents.collection}}: Adding #{document_list.size} documents…", type: :info)
727
+ feedback("Collection #{bold{collection}}: Adding #{document_list.size} documents…", type: :info)
728
728
  count = 1
729
729
  document_list.each_slice(25) do |docs|
730
730
  docs.each do |doc|
@@ -130,8 +130,20 @@ module OllamaChat::CollectionSearch
130
130
  records.empty? and return
131
131
  pre_rerank = records.size
132
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: })
133
+ log(
134
+ :info,
135
+ 'Trigger: %d/%d passed rerank for %s' % [ records.size, pre_rerank, collection ],
136
+ data: {
137
+ collection:,
138
+ prompt_name:,
139
+ records: records.map { |r|
140
+ {
141
+ text: r.text,
142
+ similarity: r.similarity&.round(4),
143
+ tags: r.tags_set.to_s(link: false)
144
+ }
145
+ }
146
+ })
135
147
  records.empty? and return
136
148
  end
137
149
  snippets = records.map { |record|
@@ -161,13 +161,13 @@ module OllamaChat::Commands
161
161
 
162
162
  command(
163
163
  name: :toggle,
164
- regexp: %r(^/toggle(?:\s+(markdown|stream|location|runtime_info|voice|think_loud|think_strip|embedding)(?:\s+(-[yn]))?)?$),
165
- complete: [ 'toggle', %w[ markdown stream location runtime_info voice think_loud think_strip embedding ] ],
164
+ regexp: %r(^/toggle(?:\s+(markdown|stream|location|runtime_info|voice|think_loud|think_strip|embedding|memory_trigger)(?:\s+(-[yn]))?)?$),
165
+ complete: [ 'toggle', %w[ markdown stream location runtime_info voice think_loud think_strip embedding memory_trigger ] ],
166
166
  options: '[-y|-n]',
167
167
  help: <<~EOT
168
168
  🎛️ Toggle feature switches
169
- (markdown, stream, location, runtime_info,
170
- voice, think_loud, think_strip, embedding)
169
+ (markdown, stream, location, runtime_info,
170
+ voice, think_loud, think_strip, embedding, memory_trigger)
171
171
  Options: -y (on), -n (off)
172
172
  EOT
173
173
  ) do |toggle_name, flag|
@@ -191,7 +191,7 @@ module OllamaChat::Commands
191
191
  end
192
192
  else
193
193
  feedback(
194
- "Available toggles: markdown|stream|location|runtime_info|voice|think_loud|think_strip|embedding"
194
+ "Available toggles: markdown|stream|location|runtime_info|voice|think_loud|think_strip|embedding|memory_trigger"
195
195
  )
196
196
  end
197
197
  :next
@@ -391,14 +391,15 @@ module OllamaChat::Commands
391
391
 
392
392
  command(
393
393
  name: :conversation,
394
- regexp: %r(^/conversation\s+(clean|compact|save|load|summarize|report)((?:\s+-[sc])*)(?:\s+([^-].*\.jsonl?))?$),
395
- complete: [ 'conversation', %w[ save load clean compact summarize report ] ],
394
+ regexp: %r(^/conversation\s+(clean|compact summary|compact|save|load|summarize|report)((?:\s+-[sc])*)(?:\s+([^-].*\.jsonl?))?$),
395
+ complete: [ 'conversation', %w[ compact clean report compact\ summary summarize save load ] ],
396
396
  options: '[-s|-c] [FILENAME]',
397
397
  help: <<~EOT
398
398
  💾 Manage conversation content:
399
399
  - save/load: Export/import as .json or .jsonl
400
400
  - clean: Remove tool content, images, thinking
401
401
  - compact: Summarize old messages, keep recent
402
+ - compact summary: Show last compaction summary
402
403
  - summarize: Per-message narrative (-s sentence)
403
404
  - report: Generate a session report document
404
405
  EOT
@@ -426,6 +427,8 @@ module OllamaChat::Commands
426
427
  else
427
428
  feedback("Denied.", type: :denied)
428
429
  end
430
+ when 'compact summary'
431
+ show_compaction_summary
429
432
  when 'compact'
430
433
  compact_with_retry
431
434
  when 'summarize'
@@ -134,6 +134,33 @@ module OllamaChat::Compaction
134
134
  false
135
135
  end
136
136
 
137
+ # Displays the most recent compaction summary in the pager.
138
+ #
139
+ # Shows the narrative and the tool-call lookup index (the
140
+ # `lookup_group` entries with short UUIDs) carried in the summary
141
+ # message, prefixed with size metadata.
142
+ #
143
+ # @return [nil] always returns nil (command block handles :next).
144
+ def show_compaction_summary
145
+ unless msg = messages.find_summary
146
+ feedback('No compaction summary yet.', type: :info)
147
+ return
148
+ end
149
+ es = OllamaChat::TokenEstimator.estimate(msg.content.to_s)
150
+ use_pager do |output|
151
+ output.puts <<~EOT
152
+ #{bold{'Compaction Summary'}}
153
+ #{'─' * Tins::Terminal.columns}
154
+
155
+ #{kramdown_ansi_parse(msg.content.to_s)}
156
+
157
+ #{'─' * Tins::Terminal.columns}
158
+ Size: 👾#{es.bytes_formatted} 🧩#{es.tokens_formatted}
159
+ Tools: #{msg.tool_calls&.size || 0} lookup_group entries
160
+ EOT
161
+ end
162
+ end
163
+
137
164
  private
138
165
 
139
166
  # Serializes non-system messages into a readable block for the prompt.
@@ -172,7 +199,7 @@ module OllamaChat::Compaction
172
199
  def build_tool_entries(messages)
173
200
  messages.filter_map do |msg|
174
201
  next unless tool_name = msg.tool_name.full?
175
- next if tool_name == 'runtime_information'
202
+ next if %w[ runtime_information trigger_inject ].include?(tool_name)
176
203
  uuid = msg.group_uuid.to_s[-8..]
177
204
  summary = OllamaChat::Compaction.tool_summary_line(
178
205
  tool_name, msg.content.to_s
@@ -65,6 +65,27 @@ class OllamaChat::Database::Models::Session < Sequel::Model(OllamaChat::DB)
65
65
  messages.to_s.count(?\n)
66
66
  end
67
67
 
68
+ # Returns the `enabled` flag of the first configured trigger entry.
69
+ #
70
+ # @return [Boolean, nil] true when the trigger is active, false when
71
+ # explicitly disabled, nil when no trigger is configured
72
+ def trigger_enabled?
73
+ trigger&.values&.first&.[]('enabled')
74
+ end
75
+
76
+ # Sets the `enabled` flag of the first configured trigger entry and
77
+ # persists the change.
78
+ #
79
+ # No-op when no trigger is configured.
80
+ #
81
+ # @param value [Boolean] the new enabled state
82
+ def set_trigger_enabled(value)
83
+ if hash = trigger&.values&.first
84
+ hash['enabled'] = value
85
+ save
86
+ end
87
+ end
88
+
68
89
  # @!attribute [v] id
69
90
  # @return [Integer] The primary key for the session.
70
91
  #
@@ -66,6 +66,14 @@ module OllamaChat::RAGHandling
66
66
  @documents.collection
67
67
  end
68
68
 
69
+ # Sets the documents collection to collection.
70
+ #
71
+ # @param collection [String, Symbol] the name of the collection to set
72
+ # @return [String, Symbol] the newly set collection name
73
+ def set_documents_collection(collection)
74
+ @documents.collection = collection
75
+ end
76
+
69
77
  # Retrieves a hash of collection names and their descriptions from the
70
78
  # database.
71
79
  #
@@ -264,14 +272,6 @@ module OllamaChat::RAGHandling
264
272
  end
265
273
  end
266
274
 
267
- # Sets the documents collection to collection.
268
- #
269
- # @param collection [String, Symbol] the name of the collection to set
270
- # @return [String, Symbol] the newly set collection name
271
- def set_documents_collection(collection)
272
- @documents.collection = collection
273
- end
274
-
275
275
  # The choose_collection method presents a menu to select or create a document
276
276
  # collection. It displays existing collections along with options to create a
277
277
  # new one or exit.
@@ -290,12 +290,12 @@ module OllamaChat::RAGHandling
290
290
  case collection = collection&.to_s
291
291
  when '[NEW]'
292
292
  if name = create_collection
293
- @documents.collection = name
293
+ set_documents_collection(name)
294
294
  end
295
295
  when nil, '[EXIT]'
296
296
  feedback("Exiting chooser.")
297
297
  when /./
298
- @documents.collection = collection
298
+ set_documents_collection(collection)
299
299
  end
300
300
  ensure
301
301
  if collection
@@ -99,31 +99,70 @@ module OllamaChat::Switches
99
99
  # switch.off? # Returns false
100
100
  # switch.on? # Returns true
101
101
  class Switch
102
+ UNSET = Object.new.freeze
103
+
102
104
  # Initializes a new Switch instance.
103
105
  #
104
106
  # @param msg [Hash{Boolean => String}] a hash containing true and false messages
105
- # @param value [Object] the initial state of the switch (coerced to boolean)
107
+ # @param value [Object, Proc] the initial state of the switch. A plain
108
+ # value is coerced to a boolean and wrapped in an internal closure;
109
+ # a callable is used directly and must be invokable both bare (read)
110
+ # and with an explicit boolean (write). If the callable responds to
111
+ # `arity`, it must return -1 (a `TypeError` is raised otherwise);
112
+ # callables without an `arity` method are accepted as-is.
106
113
  # @param callbacks [Hash{[Boolean, Boolean] => Proc}] optional mapping of
107
114
  # state transitions to callback procs. Keys are `[old_value, new_value]`.
115
+ # @raise [TypeError] if `value` is a callable that responds to `arity`
116
+ # and the arity is not -1
108
117
  def initialize(msg:, value:, callbacks: {})
109
- @value = !!value
118
+ if value.respond_to?(:call)
119
+ if arity = value.ask_and_send(:arity)
120
+ arity != -1 and raise TypeError, 'value Proc requires arity -1'
121
+ end
122
+ @value = value
123
+ else
124
+ @value = adhoc_closure(value)
125
+ end
110
126
  @msg = msg
111
127
  @callbacks = callbacks.to_h
112
128
  end
113
129
 
130
+ # Wraps a plain (non-callable) value in a closure so that every switch
131
+ # state is stored and accessed uniformly via a `Proc`.
132
+ #
133
+ # The returned closure reads the coerced (`!!`) boolean state when called
134
+ # with no argument (or the `UNSET` sentinel) and rebinds the captured
135
+ # `original_value` when called with an explicit value.
136
+ #
137
+ # @param original_value [Object] the initial value to be coerced to a
138
+ # boolean on read
139
+ # @return [Proc] a closure that reads or writes the switch state
140
+ def adhoc_closure(original_value)
141
+ -> new_value = UNSET {
142
+ if new_value == UNSET
143
+ !!original_value
144
+ else
145
+ original_value = new_value
146
+ end
147
+ }
148
+ end
149
+ private :adhoc_closure
150
+
114
151
  # @!attribute [r] value
115
152
  # @return [Boolean] the current state of the switch
116
- attr_reader :value
153
+ def value
154
+ @value.()
155
+ end
117
156
 
118
157
  # Assigns a boolean value to the switch and optionally displays the result.
119
158
  #
120
159
  # @param value [Object] the value to be coerced to a boolean and assigned
121
160
  # @param show [Boolean] determines whether to display the status message
122
161
  # @param output [IO] the output stream to write the message to
123
- # @return [String, nil, Boolean] the result of the display operation or the show flag
124
162
  def set(value, show: false, output: STDOUT)
125
- @value = !!value
163
+ @value.(value)
126
164
  show && self.show(output:)
165
+ nil
127
166
  end
128
167
 
129
168
  # Toggles the current boolean value and optionally displays the result.
@@ -131,7 +170,7 @@ module OllamaChat::Switches
131
170
  # @param show [Boolean] determines whether to show the value after toggling
132
171
  # @return [String, nil, Boolean] the result of the display operation or the show flag
133
172
  def toggle(show: true)
134
- @value = !@value
173
+ @value.(!@value.())
135
174
  show && self.show
136
175
  end
137
176
 
@@ -175,10 +214,10 @@ module OllamaChat::Switches
175
214
  # @param value [Object] the value to be coerced to a boolean and saved
176
215
  # @param show [Boolean] whether to show the updated value
177
216
  # @param output [IO] the output stream to use when showing the value
178
- # @return [String, nil, Boolean] the result of the display operation or the show flag
179
217
  def set(value, show: false, output: STDOUT)
180
218
  @chat.session.update("#{attribute}": !!value)
181
219
  show && self.show(output:)
220
+ nil
182
221
  end
183
222
 
184
223
  # Toggles the value of a session attribute and optionally displays the new state.
@@ -207,9 +246,18 @@ module OllamaChat::Switches
207
246
  class CombinedSwitch
208
247
  # Initializes a new CombinedSwitch instance.
209
248
  #
210
- # @param value [Proc] the proc used to determine the current state
211
- # @param msg [Hash{Boolean => String}] the message hash containing true and false keys
249
+ # @param value [Proc] the proc used to determine the current state.
250
+ # Must be invokable with no arguments. If the proc responds to
251
+ # `arity`, it must return 0 or -1 (a `TypeError` is raised
252
+ # otherwise); procs without an `arity` method are accepted as-is.
253
+ # @param msg [Hash{Boolean => String}] the message hash containing
254
+ # true and false keys
255
+ # @raise [TypeError] if `value` responds to `arity` and returns a
256
+ # value greater than 0 (the proc must be invokable with no args)
212
257
  def initialize(value:, msg:)
258
+ if arity = value.ask_and_send(:arity)
259
+ arity <= 0 or raise TypeError, 'value Proc requires arity <= 0'
260
+ end
213
261
  @value = value
214
262
  @msg = msg
215
263
  end
@@ -279,6 +327,14 @@ module OllamaChat::Switches
279
327
  # @return [OllamaChat::Switches::DatabaseSwitch] the tools support setting switch
280
328
  attr_reader :tools_support
281
329
 
330
+ # Controls whether the session's memory trigger is active. The enabled
331
+ # state is read from and written to the session's `trigger` hash; the
332
+ # switch is a no-op when no trigger is configured.
333
+ #
334
+ # @!attribute [r] memory_trigger
335
+ # @return [OllamaChat::Switches::Switch] the memory trigger switch instance
336
+ attr_reader :memory_trigger
337
+
282
338
  # Initializes the various switches for configuring the application's behavior.
283
339
  #
284
340
  # This method creates and configures the database and local switch objects
@@ -387,5 +443,19 @@ module OllamaChat::Switches
387
443
  false => "Tools support disabled.",
388
444
  }
389
445
  )
446
+ @memory_trigger = Switch.new(
447
+ value: -> new_value = OllamaChat::Switches::Switch::UNSET {
448
+ if new_value == OllamaChat::Switches::Switch::UNSET
449
+ chat.session.trigger_enabled?
450
+ else
451
+ chat.session.set_trigger_enabled(new_value)
452
+ nil
453
+ end
454
+ },
455
+ msg: {
456
+ true => "Memory trigger active.",
457
+ false => "Memory trigger inactive.",
458
+ },
459
+ )
390
460
  end
391
461
  end
@@ -139,7 +139,7 @@ class OllamaChat::Tools::ExecuteShell
139
139
 
140
140
  # Display command output through the pager.
141
141
  def display(stdout, stderr, exit_code)
142
- chat.use_pager do |io|
142
+ chat.use_pager(force: true) do |io|
143
143
  if stdout.present?
144
144
  io.puts bold { "stdout:" }, ""
145
145
  io.puts stdout
@@ -123,28 +123,29 @@ class OllamaChat::Tools::SearchKnowledge
123
123
  raise OllamaChat::ToolFunctionArgumentError,
124
124
  "Collection #{collection} is disabled."
125
125
  end
126
- old_collection = chat.documents.collection
127
- chat.documents.collection = collection
126
+ old_collection = chat.collection
127
+ chat.set_documents_collection(collection)
128
128
  end
129
129
 
130
130
  records = chat.find_document_records(query, tags:, text_size:, text_count:, min_similarity:)
131
131
 
132
+ pre_rerank = records.size
133
+ prompt_name = 'default'
132
134
  if rerank && records.any?
133
- pre_rerank = records.size
134
- prompt_name = 'default'
135
135
  records = chat.rerank_records(query, records, prompt_name:)
136
- chat.log(:info, 'Tool: %d/%d passed rerank for %s' % [ records.size, pre_rerank, collection ],
137
- data: { collection:, prompt_name: })
138
136
  end
139
137
 
140
138
  chat.log(:info, "Snippets retrieved", data: {
141
- tool: name, collection: chat.documents.collection, hits: records.size
142
- })
139
+ tool: name,
140
+ collection: chat.collection,
141
+ rerank:,
142
+ pre_rerank:,
143
+ hits: records.size
144
+ }.compact)
143
145
 
144
- collection_name = chat.documents.collection
145
146
  message =
146
147
  if records.any?
147
- "Retrieved #{records.size} relevant snippets from collection #{collection_name.inspect} for query #{query.inspect}. See snippets below:\n\n" +
148
+ "Retrieved #{records.size} relevant snippets from collection #{chat.collection.inspect} for query #{query.inspect}. See snippets below:\n\n" +
148
149
  records.map { |record|
149
150
  link = if record.source =~ %r(\Ahttps?://)
150
151
  record.source
@@ -155,14 +156,14 @@ class OllamaChat::Tools::SearchKnowledge
155
156
  [ link, ?# + record.tags.first ]
156
157
  }.flat_map { |l, t| chat.hyperlink(l, t) }.join(' ')
157
158
  else
158
- "No relevant snippets found for query #{query.inspect} in collection #{collection_name.inspect}."
159
+ "No relevant snippets found for query #{query.inspect} in collection #{chat.collection.inspect}."
159
160
  end
160
161
 
161
162
  {
162
163
  prompt: chat.prompt('snippets_retrieval').to_s,
163
164
  collection: {
164
- name: collection_name,
165
- description: chat.database_collection?(collection_name)&.description&.to_s,
165
+ name: chat.collection,
166
+ description: chat.database_collection?(chat.collection)&.description&.to_s,
166
167
  },
167
168
  snippets: records.map do |record|
168
169
  {
@@ -183,7 +184,7 @@ class OllamaChat::Tools::SearchKnowledge
183
184
  chat.log(:error, e, data: { tool: name, query: args.query })
184
185
  { error: e.class.name, message: e.message }.to_json
185
186
  ensure
186
- old_collection and chat.documents.collection = old_collection
187
+ old_collection and chat.set_documents_collection(old_collection)
187
188
  end
188
189
 
189
190
  self
@@ -1,6 +1,6 @@
1
1
  module OllamaChat
2
2
  # OllamaChat version
3
- VERSION = '0.0.126'
3
+ VERSION = '0.0.127'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
data/ollama_chat.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama_chat 0.0.126 ruby lib
2
+ # stub: ollama_chat 0.0.127 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "ollama_chat".freeze
6
- s.version = "0.0.126".freeze
6
+ s.version = "0.0.127".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]
@@ -111,6 +111,21 @@ describe OllamaChat::Commands, protect_env: true do
111
111
  expect(chat.stream).to receive(:set).with(false, show: true)
112
112
  expect(chat.handle_input("/toggle stream -n")).to eq :next
113
113
  end
114
+
115
+ it 'returns :next when input is "/toggle memory_trigger"' do
116
+ expect(chat.memory_trigger).to receive(:toggle)
117
+ expect(chat.handle_input("/toggle memory_trigger")).to eq :next
118
+ end
119
+
120
+ it 'returns :next when input is "/toggle memory_trigger -y"' do
121
+ expect(chat.memory_trigger).to receive(:set).with(true, show: true)
122
+ expect(chat.handle_input("/toggle memory_trigger -y")).to eq :next
123
+ end
124
+
125
+ it 'returns :next when input is "/toggle memory_trigger -n"' do
126
+ expect(chat.memory_trigger).to receive(:set).with(false, show: true)
127
+ expect(chat.handle_input("/toggle memory_trigger -n")).to eq :next
128
+ end
114
129
  end
115
130
 
116
131
  describe '/voice' do
@@ -794,6 +809,11 @@ describe OllamaChat::Commands, protect_env: true do
794
809
  expect(chat.handle_input("/conversation compact")).to eq :next
795
810
  end
796
811
 
812
+ it 'returns :next when input is "/conversation compact summary"' do
813
+ expect(chat).to receive(:show_compaction_summary)
814
+ expect(chat.handle_input("/conversation compact summary")).to eq :next
815
+ end
816
+
797
817
  it 'returns :next when input is "/conversation summarize"' do
798
818
  expect(chat).to receive(:summarize_conversation)
799
819
  .with(sentence: false)
@@ -95,6 +95,34 @@ describe OllamaChat::Switches do
95
95
  }.from(true).to(false)
96
96
  end
97
97
  end
98
+
99
+ context 'callable value (non-adhoc closure)' do
100
+ let(:store) { { enabled: false } }
101
+ let :switch do
102
+ described_class.new(
103
+ value: -> nv = described_class::UNSET do
104
+ nv == described_class::UNSET ? store[:enabled]
105
+ : (store[:enabled] = nv; nil)
106
+ end,
107
+ msg: { true => 'On', false => 'Off' }
108
+ )
109
+ end
110
+
111
+ it 'reads without writing' do
112
+ expect(switch.on?).to eq false
113
+ expect(store[:enabled]).to eq false
114
+ end
115
+
116
+ it 'set calls the closure with the explicit value' do
117
+ expect { switch.set(true) }
118
+ .to change { store[:enabled] }.from(false).to(true)
119
+ end
120
+
121
+ it 'toggle reads then writes the negation' do
122
+ expect { switch.toggle(show: false) }
123
+ .to change { store[:enabled] }.from(false).to(true)
124
+ end
125
+ end
98
126
  end
99
127
 
100
128
  describe OllamaChat::Switches::DatabaseSwitch do
@@ -134,6 +134,7 @@ describe OllamaChat::Tools::ExecuteShell do
134
134
  end
135
135
 
136
136
  it 'skips editor and confirm in test mode' do
137
+ stub_success(stdout: "file1\nfile2", stderr: '')
137
138
  expect(OllamaChat).to receive(:test_mode?).and_return(true)
138
139
  expect(chat).not_to receive(:edit_text)
139
140
  expect(chat).not_to receive(:confirm?)
@@ -118,17 +118,14 @@ describe OllamaChat::Tools::SearchKnowledge do
118
118
  )
119
119
  )
120
120
 
121
- mock_docs = double('Documents')
122
- expect(chat).to receive(:documents).and_return(mock_docs).at_least(:once)
121
+ expect(chat).to receive(:collection).
122
+ and_return('default_collection').at_least(:once)
123
+ expect(chat).to receive(:set_documents_collection).with('tolkien').ordered
124
+ expect(chat).to receive(:set_documents_collection).
125
+ with('default_collection').ordered
123
126
  expect(chat).to receive(:database_collection?)
124
127
  .and_return(double('Col', description: 'Desc', enabled: true))
125
128
  .at_least(:once)
126
-
127
- expect(mock_docs).to receive(:collection).and_return('default_collection').
128
- at_least(:once)
129
- expect(mock_docs).to receive(:collection=).with('tolkien').ordered
130
- expect(mock_docs).to receive(:collection=).with('default_collection').ordered
131
-
132
129
  tool = described_class.new(chat)
133
130
  expect(chat).to receive(:find_document_records).and_return([])
134
131
 
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.126
4
+ version: 0.0.127
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank