ollama_chat 0.0.123 → 0.0.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +68 -0
  3. data/Rakefile +1 -1
  4. data/lib/ollama_chat/asr.rb +3 -3
  5. data/lib/ollama_chat/chat.rb +16 -14
  6. data/lib/ollama_chat/clipboard.rb +4 -4
  7. data/lib/ollama_chat/collection_search.rb +66 -0
  8. data/lib/ollama_chat/commands.rb +79 -52
  9. data/lib/ollama_chat/compaction.rb +32 -15
  10. data/lib/ollama_chat/config_handling.rb +13 -7
  11. data/lib/ollama_chat/conversation.rb +8 -5
  12. data/lib/ollama_chat/database/models/app_state.rb +7 -5
  13. data/lib/ollama_chat/database/models/session.rb +4 -2
  14. data/lib/ollama_chat/database/session_locking.rb +1 -1
  15. data/lib/ollama_chat/dialog.rb +8 -8
  16. data/lib/ollama_chat/favourites_management.rb +4 -4
  17. data/lib/ollama_chat/feedback.rb +41 -0
  18. data/lib/ollama_chat/file_editing.rb +5 -4
  19. data/lib/ollama_chat/follow_chat.rb +33 -28
  20. data/lib/ollama_chat/information.rb +31 -12
  21. data/lib/ollama_chat/input_content.rb +5 -5
  22. data/lib/ollama_chat/message_editing.rb +3 -3
  23. data/lib/ollama_chat/message_list.rb +69 -13
  24. data/lib/ollama_chat/message_output.rb +7 -7
  25. data/lib/ollama_chat/model_handling.rb +71 -45
  26. data/lib/ollama_chat/oc.rb +3 -1
  27. data/lib/ollama_chat/ollama_chat_config/default_config.yml +8 -0
  28. data/lib/ollama_chat/parsing.rb +7 -7
  29. data/lib/ollama_chat/personae_management.rb +45 -27
  30. data/lib/ollama_chat/prompt_management.rb +54 -50
  31. data/lib/ollama_chat/rag_handling.rb +87 -25
  32. data/lib/ollama_chat/server_socket.rb +1 -1
  33. data/lib/ollama_chat/session_management.rb +66 -51
  34. data/lib/ollama_chat/source_fetching.rb +7 -5
  35. data/lib/ollama_chat/speaker.rb +15 -1
  36. data/lib/ollama_chat/state_selectors.rb +3 -2
  37. data/lib/ollama_chat/switches.rb +1 -3
  38. data/lib/ollama_chat/system_prompt_management.rb +14 -2
  39. data/lib/ollama_chat/tool_calling.rb +5 -5
  40. data/lib/ollama_chat/tools/concern.rb +1 -0
  41. data/lib/ollama_chat/tools/directory_structure.rb +6 -6
  42. data/lib/ollama_chat/tools/execute_shell.rb +1 -1
  43. data/lib/ollama_chat/tools/forget.rb +113 -0
  44. data/lib/ollama_chat/tools/memorize.rb +116 -0
  45. data/lib/ollama_chat/tools/roll_dice.rb +1 -1
  46. data/lib/ollama_chat/tools/run_tests.rb +1 -1
  47. data/lib/ollama_chat/tools/search_knowledge.rb +3 -59
  48. data/lib/ollama_chat/tools.rb +2 -0
  49. data/lib/ollama_chat/utils/audio_player.rb +1 -0
  50. data/lib/ollama_chat/version.rb +1 -1
  51. data/lib/ollama_chat/vim.rb +4 -2
  52. data/lib/ollama_chat/web_searching.rb +5 -5
  53. data/lib/ollama_chat.rb +2 -0
  54. data/ollama_chat.gemspec +6 -6
  55. data/spec/ollama_chat/asr_spec.rb +5 -5
  56. data/spec/ollama_chat/chat_spec.rb +2 -2
  57. data/spec/ollama_chat/clipboard_spec.rb +3 -3
  58. data/spec/ollama_chat/commands_spec.rb +24 -4
  59. data/spec/ollama_chat/compaction_spec.rb +21 -15
  60. data/spec/ollama_chat/config_handling_spec.rb +9 -12
  61. data/spec/ollama_chat/conversation_spec.rb +17 -18
  62. data/spec/ollama_chat/favourites_management_spec.rb +6 -6
  63. data/spec/ollama_chat/file_editing_spec.rb +3 -3
  64. data/spec/ollama_chat/follow_chat_spec.rb +3 -1
  65. data/spec/ollama_chat/http_handling_spec.rb +10 -10
  66. data/spec/ollama_chat/information_spec.rb +4 -4
  67. data/spec/ollama_chat/input_content_spec.rb +1 -1
  68. data/spec/ollama_chat/message_editing_spec.rb +5 -5
  69. data/spec/ollama_chat/message_list_spec.rb +76 -2
  70. data/spec/ollama_chat/message_output_spec.rb +7 -7
  71. data/spec/ollama_chat/prompt_management_spec.rb +12 -14
  72. data/spec/ollama_chat/rag_handling_spec.rb +31 -32
  73. data/spec/ollama_chat/session_management_spec.rb +16 -9
  74. data/spec/ollama_chat/source_fetching_spec.rb +2 -2
  75. data/spec/ollama_chat/speaker_spec.rb +92 -0
  76. data/spec/ollama_chat/state_selectors_spec.rb +1 -1
  77. data/spec/ollama_chat/system_prompt_management_spec.rb +4 -4
  78. data/spec/ollama_chat/think_control_spec.rb +2 -2
  79. data/spec/ollama_chat/tool_calling_spec.rb +4 -4
  80. data/spec/ollama_chat/tools/execute_shell_spec.rb +7 -7
  81. data/spec/ollama_chat/tools/forget_spec.rb +144 -0
  82. data/spec/ollama_chat/tools/memorize_spec.rb +204 -0
  83. data/spec/ollama_chat/tools/search_knowledge_spec.rb +8 -8
  84. data/spec/ollama_chat/tts_spec.rb +13 -19
  85. data/spec/ollama_chat/utils/audio_player_spec.rb +5 -3
  86. data/spec/ollama_chat/web_searching_spec.rb +2 -2
  87. metadata +17 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2d0c5676e03433f245a21795872a9d7bd64357306ec691de3012195b5403bef
4
- data.tar.gz: 933dca2b53647c523b9849d958cc1625106c97500caf2814bc731458a0e683f4
3
+ metadata.gz: 0d2664dce7c6742ff5c950e64330b84325bcbb573467edde9aceddc3fa209641
4
+ data.tar.gz: 87baa515acb398c9da1b7cf6c7fce173d946e4bd4d79ad0f1eb9dfbd2526235a
5
5
  SHA512:
6
- metadata.gz: 4589b5f1cf689bb35bdf26c288e59f40b87ef926eaf7f91e187c3fa729958c9d042f784eb3bf9a4b6e64966c201312713c2e20607291c6aabc10d306cf4cf272
7
- data.tar.gz: 19102c3d01a2ebb4bb14b8ea617b09a7e168d0175225024a490dc85e23b41ce3c7eba249d82cc671f5530da0090e945f8f6358d17ca7f6b7587a413002e27dc2
6
+ metadata.gz: a091addc87c893f700f405147386dc8fe8fcc5c3134ad12bb1ffda570ec8a0f64ac4acbaeaed70116a6c61d0765e14e2f085fa8d2c0b98496fb0881a56fdb0f8
7
+ data.tar.gz: 6d078a9c69b50719dda02e9c81a32d51929627ab6e50c7cae023630543e717dd2c1a86166495f339875178108e0ded60f9f451ebbec1275f16f06771c9b1b215
data/CHANGES.md CHANGED
@@ -1,5 +1,73 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-10-02 v0.0.124
4
+
5
+ ### Added
6
+
7
+ * `OllamaChat::Feedback` module to replace raw `STDOUT.puts` and
8
+ `STDERR.puts` calls with typed, testable system notifications.
9
+ * Supports types: `:success`, `:cancel`, `:denied`, `:warn`, `:info`,
10
+ `:alarm`.
11
+ * Integrates with `use_pager` and handles newline formatting via
12
+ `output.print`.
13
+ * `memorize` tool to embed timestamped entries into per-persona
14
+ `memory-<persona_name>` Documentrix collections.
15
+ * `forget` tool to clear a single memory record by its ISO-8601 timestamp
16
+ tag.
17
+ * `/collection query` subcommand to debug collections, supporting optional
18
+ `-e` (edit) and `-r` (rerank) flags.
19
+ * `/config env` subcommand to display the Ollama Chat environment variable
20
+ tree using `OC.view`.
21
+ * `/last -v` flag to voice previous assistant messages via
22
+ `MessageList#speak_last`.
23
+ * `OllamaChat::CollectionSearch` module extracted from `SearchKnowledge`
24
+ tool, providing `find_document_records` and `rerank_records` methods mixed
25
+ into `Chat`.
26
+ * `refresh_system_prompt` method in `SystemPromptManagement` to
27
+ re-interpolate the active system prompt with fresh `persona` and
28
+ `runtime_info` values after collection mutations.
29
+ * Voice announcement in the compaction report, including
30
+ `context_filled_before`, `context_filled_after`, and `duration` in
31
+ `Compaction::Result`.
32
+ * `Speaker#wait_for_speaker` method to join the speaker thread and block
33
+ until completion.
34
+ * `spec/ollama_chat/speaker_spec.rb` with tests for `OllamaChat::Speaker`.
35
+
36
+ ### Changed
37
+
38
+ * Replaced `str % {…}` with `named_placeholders_interpolate({…})` for
39
+ DB-prompt call sites to handle unknown placeholders gracefully.
40
+ * Updated `directory_structure` tool description from "Tree viewer" to "File
41
+ locator" and added guidance to prefer it over shell `find` or `ls -R`.
42
+ * Routed prompt change feedback through the `chat` instance in `app_state.rb`
43
+ to ensure messages target the active chat session.
44
+ * Switched spec stubs from `allow` to `expect` in `http_handling_spec.rb`,
45
+ `message_list_spec.rb`, `execute_shell_spec.rb`, `tts_spec.rb`,
46
+ `compaction_spec.rb`, `audio_player_spec.rb`, and `asr_spec.rb` to enforce
47
+ required calls.
48
+ * Bumped `const_conf` dependency from `~> **0.3**` to `~> **0.9**`.
49
+ * Updated `rerank_records` to accept an optional `prompt_name:` keyword
50
+ argument.
51
+ * Improved new session name generation by extracting
52
+ `new_random_session_name` into `SessionManagement` and handling `nil`
53
+ cancellation in `determine_valid_new_name_for_session`.
54
+ * Allowed naming new sessions via `choose_session` by threading an optional
55
+ `name:` keyword through `new_session` and `Session.with_defaults`.
56
+ * Logged stored memory text in the `memorize` tool by adding `text:` to the
57
+ `chat.log` data hash.
58
+ * Extracted context usage helpers `context_usage_colored` and
59
+ `context_percentage` to eliminate duplicated format logic.
60
+ * Updated `output_eval_stats` in `follow_chat.rb` to print colored context
61
+ usage.
62
+
63
+ ### Fixed
64
+
65
+ * Fixed indentation bug in `model_handling.rb` import/export by changing
66
+ `sub` to `gsub`.
67
+ * Guarded source link rendering in `search_knowledge` against `nil` or empty
68
+ `source` values.
69
+ * Suppressed `report_on_exception` on `AudioPlayer` background thread.
70
+
3
71
  ## 2026-09-25 v0.0.123
4
72
 
5
73
  ### Added
data/Rakefile CHANGED
@@ -55,7 +55,7 @@ GemHadar do
55
55
  dependency 'pdf-reader', '~> 2.0'
56
56
  dependency 'bigdecimal', '~> 3.1'
57
57
  dependency 'csv', '~> 3.0'
58
- dependency 'const_conf', '~> 0.3'
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'
@@ -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
- STDERR.puts "ASR: #{executable} not found in PATH."
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
- STDERR.puts "ASR: #{executable} produced no output."
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
- STDERR.puts "ASR transcription failed: #{e.message}"
64
+ chat.feedback("ASR transcription failed: #{e.message}", type: :warn)
65
65
  nil
66
66
  end
67
67
 
@@ -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
- STDOUT.puts
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
- STDOUT.puts "\nType /help to display the chat help."
208
+ feedback("\nType /help to display the chat help.")
207
209
 
208
210
  interact_with_user
209
211
  end
@@ -434,19 +436,19 @@ class OllamaChat::Chat
434
436
  case what
435
437
  when 'messages', nil
436
438
  messages.clear
437
- STDOUT.puts "Cleared messages."
439
+ feedback("Cleared messages.", type: :info)
438
440
  when 'links'
439
441
  links.clear
440
- STDOUT.puts "Cleared links."
442
+ feedback("Cleared links.", type: :info)
441
443
  when 'history'
442
444
  clear_history
443
- STDOUT.puts "Cleared history."
445
+ feedback("Cleared history.", type: :info)
444
446
  when 'tags'
445
447
  @documents.clear
446
- STDOUT.puts "Cleared all tags."
448
+ feedback("Cleared all tags.", type: :info)
447
449
  when 'images'
448
450
  messages.clear_images
449
- STDOUT.puts "Cleared all images."
451
+ feedback("Cleared all images.", type: :info)
450
452
  when 'all'
451
453
  if confirm?(
452
454
  prompt: '🔔 Are you sure to clear messages and collection? (y/n) ',
@@ -457,9 +459,9 @@ class OllamaChat::Chat
457
459
  @documents.clear
458
460
  links.clear
459
461
  clear_history
460
- STDOUT.puts "Cleared messages and collection #{bold{collection}}."
462
+ feedback("Cleared messages and collection #{bold{collection}}.", type: :info)
461
463
  else
462
- STDOUT.puts 'Cancelled.'
464
+ feedback("Denied.", type: :denied)
463
465
  end
464
466
  end
465
467
  persona_profile
@@ -517,7 +519,7 @@ class OllamaChat::Chat
517
519
  type = message.type.full?(:to_sym) || :socket_input
518
520
  content = message.content
519
521
  @parse_content = message.parse
520
- STDOUT.puts color(112) { "Received a server socket message. Processing now…" }
522
+ feedback color(112) { "Received a server socket message. Processing now…" }, type: :info
521
523
  else
522
524
  raise
523
525
  end
@@ -619,13 +621,13 @@ class OllamaChat::Chat
619
621
  msg = "Currently lost connection to ollama server and cannot send command."
620
622
  log(:warn, msg, warn: true)
621
623
  rescue Interrupt
622
- STDOUT.puts "Type /quit to quit."
624
+ feedback("Type /quit to quit.")
623
625
  ensure
624
626
  self.server_socket_message = nil
625
627
  end
626
628
  0
627
629
  rescue OllamaChat::OllamaChatQuitError
628
- STDOUT.puts "Goodbye."
630
+ feedback("Goodbye.")
629
631
  rescue ComplexConfig::AttributeMissing, ComplexConfig::ConfigurationSyntaxError => e
630
632
  log(:error, e)
631
633
  fix_config(e)
@@ -718,7 +720,7 @@ class OllamaChat::Chat
718
720
  # @note Progress is reported to STDOUT during processing
719
721
  def add_documents_from_argv(document_list)
720
722
  if document_list.any?(&:empty?)
721
- STDOUT.puts "Clearing collection #{bold{documents.collection}}."
723
+ feedback("Clearing collection #{bold{documents.collection}}.", type: :info)
722
724
  documents.clear
723
725
  document_list.reject!(&:empty?)
724
726
  end
@@ -730,7 +732,7 @@ class OllamaChat::Chat
730
732
  File.expand_path(doc)
731
733
  end
732
734
  end
733
- STDOUT.puts "Collection #{bold{documents.collection}}: Adding #{document_list.size} documents…"
735
+ feedback("Collection #{bold{documents.collection}}: Adding #{document_list.size} documents…", type: :info)
734
736
  count = 1
735
737
  document_list.each_slice(25) do |docs|
736
738
  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
- STDOUT.puts "The last response has been successfully copied to the system clipboard."
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
- STDERR.puts e.message
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
- STDOUT.puts "The clipboard content has been successfully copied to the chat."
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
- STDERR.puts e.message
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,66 @@
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
+ @documents.find_where(
28
+ query.first(config.embedding.model.context_length),
29
+ tags:,
30
+ prompt: config.embedding.model.prompt?,
31
+ text_size:,
32
+ text_count:,
33
+ min_similarity:
34
+ )
35
+ end
36
+
37
+ # Reranks a set of records using the LLM with the `rerank` prompt.
38
+ #
39
+ # @param query [String] the original search query
40
+ # @param records [Array<Documentrix::Utils::TagResult>] candidate records
41
+ # @param prompt_name [String, nil] the prompt template to use for
42
+ # reranking; defaults to `'rerank'`
43
+ # @return [Array<Documentrix::Utils::TagResult>] filtered records
44
+ def rerank_records(query, records, prompt_name: nil)
45
+ candidates = records.each_with_index.map { |r, i|
46
+ "[#{i}] #{truncate(r.text.strip, length: 300)}"
47
+ }.join("\n")
48
+
49
+ prompt_name ||= 'rerank'
50
+ rerank_prompt = prompt(prompt_name) or
51
+ raise 'missing prompt %s' % prompt_name.inspect
52
+ rerank_prompt = rerank_prompt.to_s
53
+ .named_placeholders_interpolate({ query:, candidates: })
54
+
55
+ begin
56
+ if response = generate(prompt: rerank_prompt).full?
57
+ indices = response.scan(/\d+/).map(&:to_i)
58
+ .select { |i| (0...records.size).include?(i) }
59
+ records = records.values_at(*indices) if indices.any?
60
+ end
61
+ rescue => e
62
+ log(:error, e, data: { context: 'rerank' })
63
+ end
64
+ records
65
+ end
66
+ end