ollama_chat 0.0.99 → 0.0.101

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +112 -0
  3. data/README.md +51 -1
  4. data/Rakefile +3 -2
  5. data/bin/ollama_chat_log +64 -0
  6. data/lib/ollama_chat/chat.rb +63 -29
  7. data/lib/ollama_chat/clipboard.rb +7 -2
  8. data/lib/ollama_chat/commands.rb +182 -213
  9. data/lib/ollama_chat/database/migrations/006_rename_system_prompt_context_to_system.rb +13 -0
  10. data/lib/ollama_chat/database/models/prompt.rb +14 -23
  11. data/lib/ollama_chat/database/models/session.rb +20 -7
  12. data/lib/ollama_chat/dialog.rb +1 -1
  13. data/lib/ollama_chat/document_cache.rb +5 -1
  14. data/lib/ollama_chat/favourites_management.rb +4 -5
  15. data/lib/ollama_chat/follow_chat.rb +54 -40
  16. data/lib/ollama_chat/history.rb +3 -0
  17. data/lib/ollama_chat/information.rb +17 -4
  18. data/lib/ollama_chat/logging.rb +45 -6
  19. data/lib/ollama_chat/message.rb +27 -6
  20. data/lib/ollama_chat/message_list.rb +41 -9
  21. data/lib/ollama_chat/model_handling.rb +52 -33
  22. data/lib/ollama_chat/ollama_chat_config/default_config.yml +318 -314
  23. data/lib/ollama_chat/parsing.rb +22 -6
  24. data/lib/ollama_chat/personae_management.rb +22 -9
  25. data/lib/ollama_chat/prompt_handling.rb +23 -57
  26. data/lib/ollama_chat/prompt_management.rb +93 -71
  27. data/lib/ollama_chat/rag_handling.rb +9 -2
  28. data/lib/ollama_chat/session_management.rb +84 -21
  29. data/lib/ollama_chat/source_fetching.rb +22 -3
  30. data/lib/ollama_chat/system_prompt_management.rb +4 -246
  31. data/lib/ollama_chat/tools/browse.rb +4 -2
  32. data/lib/ollama_chat/tools/compute_bmi.rb +2 -1
  33. data/lib/ollama_chat/tools/copy_to_clipboard.rb +3 -2
  34. data/lib/ollama_chat/tools/delete_file.rb +5 -2
  35. data/lib/ollama_chat/tools/directory_structure.rb +5 -1
  36. data/lib/ollama_chat/tools/eval_ruby.rb +8 -3
  37. data/lib/ollama_chat/tools/execute_grep.rb +7 -1
  38. data/lib/ollama_chat/tools/execute_ri.rb +2 -0
  39. data/lib/ollama_chat/tools/file_context.rb +1 -0
  40. data/lib/ollama_chat/tools/gem_path_lookup.rb +8 -6
  41. data/lib/ollama_chat/tools/generate_image.rb +1 -0
  42. data/lib/ollama_chat/tools/generate_password.rb +13 -2
  43. data/lib/ollama_chat/tools/get_current_weather.rb +1 -0
  44. data/lib/ollama_chat/tools/get_cve.rb +1 -0
  45. data/lib/ollama_chat/tools/get_endoflife.rb +1 -0
  46. data/lib/ollama_chat/tools/get_ghr.rb +1 -0
  47. data/lib/ollama_chat/tools/get_jira_issue.rb +2 -0
  48. data/lib/ollama_chat/tools/get_location.rb +3 -0
  49. data/lib/ollama_chat/tools/get_rfc.rb +1 -0
  50. data/lib/ollama_chat/tools/get_time.rb +5 -1
  51. data/lib/ollama_chat/tools/get_url.rb +1 -0
  52. data/lib/ollama_chat/tools/move_file.rb +5 -2
  53. data/lib/ollama_chat/tools/open_file_in_editor.rb +4 -0
  54. data/lib/ollama_chat/tools/paste_from_clipboard.rb +1 -0
  55. data/lib/ollama_chat/tools/paste_into_editor.rb +7 -13
  56. data/lib/ollama_chat/tools/patch_file.rb +192 -50
  57. data/lib/ollama_chat/tools/read_file.rb +63 -42
  58. data/lib/ollama_chat/tools/resolve_tag.rb +3 -1
  59. data/lib/ollama_chat/tools/retrieve_document_snippets.rb +6 -1
  60. data/lib/ollama_chat/tools/roll_dice.rb +1 -0
  61. data/lib/ollama_chat/tools/run_tests.rb +7 -2
  62. data/lib/ollama_chat/tools/search_web.rb +1 -0
  63. data/lib/ollama_chat/tools/write_file.rb +17 -11
  64. data/lib/ollama_chat/utils/log_viewer.rb +119 -0
  65. data/lib/ollama_chat/utils/tag_resolver.rb +1 -1
  66. data/lib/ollama_chat/utils.rb +1 -0
  67. data/lib/ollama_chat/uuid_v7.rb +33 -0
  68. data/lib/ollama_chat/version.rb +1 -1
  69. data/lib/ollama_chat/web_searching.rb +3 -0
  70. data/lib/ollama_chat.rb +9 -0
  71. data/ollama_chat.gemspec +10 -9
  72. data/spec/assets/example.epub +0 -0
  73. data/spec/ollama_chat/chat_spec.rb +37 -10
  74. data/spec/ollama_chat/follow_chat_spec.rb +11 -2
  75. data/spec/ollama_chat/message_list_spec.rb +94 -29
  76. data/spec/ollama_chat/parsing_spec.rb +11 -2
  77. data/spec/ollama_chat/session_management_spec.rb +324 -0
  78. data/spec/ollama_chat/source_fetching_spec.rb +26 -0
  79. data/spec/ollama_chat/tools/directory_structure_spec.rb +1 -1
  80. data/spec/ollama_chat/tools/execute_ri_spec.rb +3 -3
  81. data/spec/ollama_chat/tools/gem_path_lookup_spec.rb +8 -2
  82. data/spec/ollama_chat/tools/get_jira_issue_spec.rb +3 -3
  83. data/spec/ollama_chat/tools/paste_into_editor_spec.rb +1 -35
  84. data/spec/ollama_chat/tools/patch_file_spec.rb +136 -74
  85. data/spec/ollama_chat/tools/read_file_spec.rb +85 -0
  86. data/spec/ollama_chat/tools/resolve_tag_spec.rb +2 -2
  87. data/spec/ollama_chat/tools/write_file_spec.rb +15 -11
  88. data/spec/ollama_chat/utils/log_viewer_spec.rb +137 -0
  89. data/spec/ollama_chat/web_searching_spec.rb +2 -2
  90. metadata +34 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0818076703ebd6e8d8babbe5898a6a0069863ce7cebf18658f70e8d4b5592c51'
4
- data.tar.gz: 7e9f768d41bff3560c2d22ac20ace84da0b580b3b17f8f9d1ffd3522af5d8db1
3
+ metadata.gz: 55511ef08f35b482ac7529dae4b9e4d9cb490bd37467fac563b966f3ad87f569
4
+ data.tar.gz: 26846b60e0ef32214b21a1e56128b4be6dbe6bd0808f195c8130186c01f8975c
5
5
  SHA512:
6
- metadata.gz: 4864950b3246106793b51ffe74e3ead6dad4683f1b9fcaf710bf7f672264c646dc90dacdbcef437725b725230b8d0b70938045729ca982c83845ebce41bb773d
7
- data.tar.gz: 5b99426668efc34bbb89a17faf2dcf016c1a6ada4c69d44882ebb40bcb836907741f96b5fb66ee99ea75b78abbdd34069ddbc186d525acd015302bcea95e55a7
6
+ metadata.gz: 1d7960e9303887e5ef3571b79bffbf2cd30785c8db4051e6fc7a16f828e7d5dbe8754006a5356074c83fb989dd35b27b5311d1e676c330f4303a81014d16c4ac
7
+ data.tar.gz: dfcb0fee96b6093e0d30cd7cb4894aa25752054236d85a14161519fb781d1697c7011816441dbdddfeb3477872c057b9382c594b2691c40f3238c9c727d43843
data/CHANGES.md CHANGED
@@ -1,5 +1,117 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-07-26 v0.0.101
4
+
5
+ ### Added
6
+
7
+ - Introduced a `clean` subcommand to `/conversation` to remove tool content,
8
+ images, and thinking from messages in place, including a confirmation prompt
9
+ for the destructive operation.
10
+ - Implemented `OllamaChat::Utils::LogViewer` and the `bin/ollama_chat_log`
11
+ utility for real-time, colorized JSON log tailing with regex filtering.
12
+ - Added `estimate_tokens` and `count_messages` helper methods to
13
+ `OllamaChat::Database::Models::Session`.
14
+ - Implemented `clean_messages!` in `MessageList` with associated YARD
15
+ documentation.
16
+
17
+ ### Changed
18
+
19
+ - Enhanced file patching integrity by replacing `mtime` and `line_count` checks
20
+ with a CRC32 `checksum`. This acts as a "proof of reading": since LLMs cannot
21
+ calculate CRC32 in their head, echoing the checksum proves they have the
22
+ latest file context including linenumbers without requiring cryptographic
23
+ security.
24
+ - Updated `read_file` to conditionally return `checksum` only when full-file
25
+ line numbers are requested, optimizing token usage for non-patching workflows.
26
+ - Refactored `OllamaChat::Logging#log` to use `deep_transform` for payload
27
+ sanitization, handling circular references and non-serializable objects.
28
+ - Standardized the `tool: name` variable across 28+ tool classes to ensure
29
+ consistent audit trails.
30
+ - Expanded logging coverage for file operations, grep, tests, RAG handling,
31
+ session management, and clipboard operations.
32
+ - Split `OllamaChat::FollowChat#eval_stats` into a data extraction method
33
+ `stats_hash` and a separate formatting method.
34
+ - Updated `/conversation` command to support the `-c` flag for `save` and added
35
+ path validation for `save` and `load` subcommands.
36
+ - Modified `lib/ollama_chat/tools/patch_file.rb` to validate that `edits` is an
37
+ `Array` and handle `checksum` mismatches.
38
+
39
+ ### Fixed
40
+
41
+ - Improved option parsing scope within the `/conversation` command by moving
42
+ logic inside the `when 'save'` block.
43
+
44
+ ## 2026-07-23 v0.0.100
45
+
46
+ ### New Features
47
+
48
+ - **UUID v7 Integration**: Implemented a compatibility shim via
49
+ `OllamaChat::UUIDV7.generate` that utilizes native `SecureRandom.uuid_v7` for
50
+ Ruby **3.3**+ and a custom RFC 9562 compliant implementation for older
51
+ versions.
52
+ - **Document Parsing & Embeddings**:
53
+ - Added EPUB support using `pandoc` via the `epub_read` method and updated
54
+ `parse_source` to handle `application/epub+zip`.
55
+ - Enhanced retrieval precision by prepending source identifiers to document
56
+ chunks in `embed_source`.
57
+ - **Structured Logging**: Implemented a JSON formatter in `OllamaChat::Logging`
58
+ with a `data:` keyword argument for structured context, including separate
59
+ capture of backtraces and tool execution details.
60
+ - **Prompt Management Refactor**:
61
+ - Unified prompt API using a `context:` keyword argument across `prompt`,
62
+ `each_prompt`, `delete_prompt`, `store_prompt`, and `write_prompt`.
63
+ - Introduced dynamic context parameters (defaulting to `'prompt'`) and
64
+ restructured `default_config.yml` to nest prompts under `prompt:`,
65
+ `suggest:`, and `system:`.
66
+ - Promoted `/suggest` to a top-level command and simplified the `/system`
67
+ command.
68
+ - **Conversation Grouping**: Introduced `group_uuid` in `OllamaChat::Message`
69
+ to enable conversation grouping, including a `repair_group_uuids` method in
70
+ `OllamaChat::SessionManagement` for legacy data migration.
71
+ - **Tool Enhancements**:
72
+ - **`read_file`**: Added support for line numbering via the `line_numbers`
73
+ parameter and range extraction using `start_line` and `end_line`. Now
74
+ returns total `line_count` and file `mtime` in ISO8601 format.
75
+ - **`patch_file`**: Refactored to support coordinate-based patching via an
76
+ `edits` parameter for targeted line-range replacements, including overlap
77
+ detection and freshness checks using `mtime`.
78
+ - **`generate_password`**: Added `base64url` alphabet support and critical
79
+ security warnings to discourage the use of `eval_ruby` for secret
80
+ generation.
81
+
82
+ ### Improvements & Refactoring
83
+
84
+ - **CLI & UX**:
85
+ - Refactored command help strings with emoji prefixes and a more concise,
86
+ scannable multi-line format using heredocs.
87
+ - Added a welcome message upon startup displaying the version, connected
88
+ server version, and URL via `print_welcome`.
89
+ - Standardized the creation workflow for prompts and personae with a source
90
+ selection menu (`[CLIPBOARD]`, `[FILES]`, `[EMPTY/MANUAL]`).
91
+ - **Application Flow**:
92
+ - Implemented `OllamaChatQuitError` to handle graceful application
93
+ shutdowns via exception-based control flow.
94
+ - Added a `[QUIT-APP]` option to the session chooser after deletion.
95
+ - **Internal Logic**:
96
+ - Optimized `ps_read` by replacing manual read loops with `IO.copy_stream`.
97
+ - Centralized Ollama chat request logging through a new `call_ollama_chat`
98
+ wrapper.
99
+ - Refactored model option reconfiguration into a dedicated
100
+ `reconfigure_model_options` method in
101
+ `lib/ollama_chat/model_handling.rb`.
102
+ - Updated dependencies: `ollama-ruby` to **1.23** and `tins` to **1.54**.
103
+
104
+ ### Bug Fixes & Stability
105
+
106
+ - Added error logging (`chat.log(:error, e)`) to the `rescue` blocks of all 33
107
+ tool execution methods.
108
+ - Fixed a typo in the Ghostscript error message within `ps_read`.
109
+ - Added defensive handling for `JSON.load` results in `model_handling.rb` using
110
+ `.full? || {}` to prevent `nil` propagation.
111
+ - Removed implicit fallbacks to the last assistant response in
112
+ `copy_to_clipboard` and `paste_into_editor`, making `text` a required
113
+ parameter.
114
+
3
115
  ## 2026-07-13 v0.0.99
4
116
 
5
117
  ### Changed
data/README.md CHANGED
@@ -120,7 +120,7 @@ A source isn't just a filename; it can be any of the following:
120
120
  * **Remote URLs**: Any `http://` or `https://` link. The command will fetch the
121
121
  web content and parse it before importing.
122
122
  * **Shell Commands (`!`)**: Any source starting with an exclamation mark is
123
- executed as a shell command, and its **STDOUT** is imported as the content.
123
+ executed as a shell command, and its **STDOUT** is imported as the content.
124
124
  * *Example*: `/input !ls -la` imports the current directory listing.
125
125
  * *Example*: `/input !git branch` tells the AI which branch you are on.
126
126
 
@@ -358,6 +358,56 @@ functionality:
358
358
  These parameters provide greater flexibility in how you interact with
359
359
  `ollama_chat`, whether from the command line or integrated tools like `vim`.
360
360
 
361
+ ### Using `ollama_chat_log` to Inspect Application Logs
362
+
363
+ OllamaChat produces structured JSON logs that capture every tool execution,
364
+ conversation state, and internal event. The `ollama_chat_log` utility allows you
365
+ to filter, colorize, and tail these logs in real-time, displaying the full
366
+ structured data payload by default.
367
+
368
+ #### Basic Usage
369
+
370
+ By default, `ollama_chat_log` reads the current session's log file located at
371
+ `~/.local/state/ollama_chat/chat.json` (or wherever the default points).
372
+
373
+ ```bash
374
+ # Show the last 10 log entries
375
+ $ ollama_chat_log
376
+
377
+ # Show the last 50 entries
378
+ $ ollama_chat_log -n 50
379
+ ```
380
+
381
+ #### Real-time Tailing
382
+
383
+ Use the `-f` flag to follow the log file as new entries are appended, similar to
384
+ `tail -f`.
385
+
386
+ ```bash
387
+ $ ollama_chat_log -f
388
+ ```
389
+
390
+ #### Filtering Logs with `-m`
391
+
392
+ The `-m` flag allows you to filter logs based on nested JSON paths using dot
393
+ notation. You can match for key presence or substring matches within values.
394
+
395
+ - **Key Presence**: `-m "data.tool"` (shows only logs that have a `tool` key)
396
+ - **Substring Match**: `-m "data.tool=read_file"` (shows logs where the tool is `read_file`)
397
+ - **Nested Paths**: `-m "data.function.name=patch_file"`
398
+ - **Regexp Match**: `-m "data.tool=^(write_file|patch_file)$"`
399
+
400
+ You can chain multiple `-m` flags together. All patterns must match for a log
401
+ entry to be displayed (AND logic).
402
+
403
+ ```bash
404
+ # Show only successful file reads
405
+ $ ollama_chat_log -m "data.tool=read_file" -m "level=info"
406
+
407
+ # Follow and watch for specific tool executions
408
+ $ ollama_chat_log -f -m "data.tool=execute_grep"
409
+ ```
410
+
361
411
  ## Available Tools
362
412
 
363
413
  The assistant can interact with the system using a variety of tools to gather
data/Rakefile CHANGED
@@ -39,7 +39,7 @@ GemHadar do
39
39
  )
40
40
 
41
41
  dependency 'excon', '~> 1.0'
42
- dependency 'ollama-ruby', '~> 1.22'
42
+ dependency 'ollama-ruby', '~> 1.23'
43
43
  dependency 'documentrix', '>= 0.6.0'
44
44
  dependency 'unix_socks', '>= 0.4'
45
45
  dependency 'rss', '~> 0.3'
@@ -49,7 +49,7 @@ 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.53'
52
+ dependency 'tins', '~> 1.55'
53
53
  dependency 'search_ui', '>= 0.2'
54
54
  dependency 'amatch', '~> 0.4'
55
55
  dependency 'pdf-reader', '~> 2.0'
@@ -60,6 +60,7 @@ GemHadar do
60
60
  dependency 'infobar', '>= 0.13.1'
61
61
  dependency 'rubyzip', '~> 3.0'
62
62
  dependency 'sequel', '~> 5.0'
63
+ dependency 'file-tail', '~> 1.4'
63
64
  development_dependency 'all_images', '~> 0.12'
64
65
  development_dependency 'rspec', '~> 3.2'
65
66
  development_dependency 'kramdown', '~> 2.0'
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ollama_chat'
4
+ require 'file/tail'
5
+ require 'tins/xt'
6
+
7
+ opts = Tins::GO.go 'fm:n:h:', defaults: { ?n => 10 }
8
+ log_path = ARGV.shift || OC::OLLAMA::CHAT::LOGFILE
9
+
10
+ if opts[?h]
11
+ puts <<~EOT
12
+ Usage: #{File.basename($0)} [OPTIONS] [LOGFILE]
13
+
14
+ Options:
15
+ -f Follow the log file (real-time tailing)
16
+ -n NUMBER Rewind and show the last NUMBER of lines before starting
17
+ -m MATCH Filter logs by JSON key paths and values (can be used multiple times)
18
+ - Key presence match: -m "data.tool"
19
+ - Substring match: -m "data.tool=read_file" or -m "level=ERROR"
20
+ - Regexp match: -m "data.tool=^(write_file|patch_file)$"
21
+ - Nested path match: -m "data.function.name=write_file"
22
+ -h Show this help
23
+
24
+ LOGFILE Path to the JSON log file (defaults to the current chat log)
25
+ EOT
26
+ exit 0
27
+ end
28
+
29
+ unless File.exist?(log_path)
30
+ STDERR.puts "Error: Log file not found at #{log_path.inspect}"
31
+ exit 1
32
+ end
33
+
34
+ # The processing loop
35
+ def process_logs(path, follow: false, rewind: 0, display_data: true, match: nil)
36
+ if follow
37
+ # Real-time tailing implementation
38
+ puts "Watching logs... (Ctrl+C to stop)"
39
+ File::Tail::Logfile.open(path, backward: rewind) do |log|
40
+ log.tail do |line|
41
+ output = OllamaChat::Utils::LogViewer.format_line(line, display_data:, match:)
42
+ output or next
43
+ puts output
44
+ end
45
+ end
46
+ else
47
+ File::Tail::Logfile.open(path, backward: rewind) do |log|
48
+ log.each do |line|
49
+ output = OllamaChat::Utils::LogViewer.format_line(line, display_data:, match:)
50
+ output or next
51
+ puts output
52
+ end
53
+ end
54
+ end
55
+ rescue Interrupt
56
+ puts "\nStopping log viewer..."
57
+ end
58
+
59
+ process_logs(
60
+ log_path,
61
+ follow: opts[?f],
62
+ rewind: opts[?n].to_i,
63
+ match: opts[?m]
64
+ )
@@ -15,6 +15,7 @@ require 'csv'
15
15
  require 'socket'
16
16
  require 'shellwords'
17
17
  require 'context_spook'
18
+ require 'zlib'
18
19
 
19
20
  # A chat client for interacting with Ollama models through a terminal
20
21
  # interface.
@@ -116,6 +117,7 @@ class OllamaChat::Chat
116
117
  else
117
118
  messages.read_conversation_jsonl(session.messages.to_s)
118
119
  end
120
+ repair_group_uuids
119
121
  embedding_enabled.set(config.embedding.enabled && !@opts[?E])
120
122
  @documents = setup_documents
121
123
  @cache = setup_cache
@@ -124,7 +126,6 @@ class OllamaChat::Chat
124
126
  @tool_call_results = Hash.new { |h, name| h[name] = [] }
125
127
  setup_personae_directory
126
128
  @opts[?S] and init_server_socket
127
- info_session
128
129
  rescue ComplexConfig::AttributeMissing, ComplexConfig::ConfigurationSyntaxError => e
129
130
  fix_config(e)
130
131
  end
@@ -160,9 +161,9 @@ class OllamaChat::Chat
160
161
  # @return [Array] a list of images to be sent with the next prompt
161
162
  attr_reader :images
162
163
 
163
- # Provides read-only access to the cache instance used by the object
164
+ # Provides read-only access to the cache instance used by the object.
164
165
  #
165
- # @attr_reader [Cache] the cache instance
166
+ # @return [OllamaChat::RedisCache, nil] the cache instance
166
167
  attr_reader :cache
167
168
 
168
169
  # The start method initializes the chat session by displaying information,
@@ -183,6 +184,7 @@ class OllamaChat::Chat
183
184
  setup_persona_from_session
184
185
  setup_system_prompt
185
186
 
187
+ print_welcome
186
188
  info_session
187
189
 
188
190
  STDOUT.puts "\nType /help to display the chat help."
@@ -255,7 +257,7 @@ class OllamaChat::Chat
255
257
  ),
256
258
  ]
257
259
 
258
- content = ollama.chat(
260
+ content = call_ollama_chat(
259
261
  model: @model,
260
262
  messages: ,
261
263
  options: model_options,
@@ -265,22 +267,39 @@ class OllamaChat::Chat
265
267
  )&.message&.content.to_s
266
268
 
267
269
  if content.empty?
268
- log(
269
- :warn,
270
- "Sent #{prompt.inspect} to LLM for generation, received no content!"
271
- )
270
+ log(:warn, "LLM generation returned empty content", data: { prompt: })
272
271
  else
273
- log(
274
- :info,
275
- "Sent #{prompt.inspect} to LLM for generation, received #{content.inspect}"
276
- )
272
+ log(:info, "LLM generation successful", data: { prompt:, content: })
277
273
  end
278
274
 
279
275
  content
280
276
  end
281
277
 
278
+ # Returns a human-readable string representation of the Chat object,
279
+ # including the class name and the base URL.
280
+ #
281
+ # @return [String] the inspected string
282
+ def inspect
283
+ "#<#{self.class}: #{base_url.to_s}>"
284
+ end
285
+
282
286
  private
283
287
 
288
+ # Wrapper for ollama.chat to provide consistent logging of all arguments.
289
+ #
290
+ # @param opts [Hash] keyword arguments for the chat call
291
+ # @param block [Proc] optional handler block
292
+ def call_ollama_chat(**opts, &block)
293
+ if debug
294
+ log(:debug, "Ollama chat request sent", data: opts)
295
+ else
296
+ clean_opts = opts.dup
297
+ clean_opts = clean_opts.slice!(:messages, :tools)
298
+ log(:info, "Ollama chat request sent", data: clean_opts)
299
+ end
300
+ ollama.chat(**opts, &block)
301
+ end
302
+
284
303
  # @return [Module] The module containing the database models.
285
304
  def models
286
305
  OllamaChat::Database::Models
@@ -328,6 +347,14 @@ class OllamaChat::Chat
328
347
  # @param prefill_prompt [String, nil] The text to prefill, or nil to clear it
329
348
  attr_writer :prefill_prompt
330
349
 
350
+ # Signals a graceful shutdown of the application.
351
+ #
352
+ # Raises {OllamaChat::OllamaChatQuitError} which is caught by the main
353
+ # interaction loop to terminate the session cleanly.
354
+ def quit_app
355
+ raise OllamaChat::OllamaChatQuitError
356
+ end
357
+
331
358
  # Handles user input commands and processes chat interactions.
332
359
  #
333
360
  # @param content [String] The input content to process
@@ -394,13 +421,16 @@ class OllamaChat::Chat
394
421
  # The method also handles server socket messages, manages chat history, and
395
422
  # ensures proper cleanup and configuration handling throughout the
396
423
  # interaction.
424
+ #
425
+ # @return [Integer] returns 0 on successful completion or raises an error
397
426
  def interact_with_user
398
427
  loop do
399
428
  content = nil
429
+ group_uuid = OllamaChat::UUIDV7.generate
400
430
  tools_were_called = false
401
431
  enable_content_parsing
402
- type = :terminal_input
403
- input_prompt = bold { color(172) { message_type(@images) + " user" } } + bold { "> " }
432
+ type = :terminal_input
433
+ input_prompt = bold { color(172) { message_type(@images) + " user" } } + bold { "> " }
404
434
  begin
405
435
  tools_were_called = handle_tool_call_results? { |index, tool_name, content|
406
436
  messages << OllamaChat::Message.new(
@@ -408,7 +438,8 @@ class OllamaChat::Chat
408
438
  sender_name: tool_name,
409
439
  tool_name: ,
410
440
  content: ,
411
- images: @images.dup
441
+ images: @images.dup,
442
+ group_uuid:
412
443
  )
413
444
  }
414
445
  tools_were_called and type = :tool_input
@@ -444,8 +475,6 @@ class OllamaChat::Chat
444
475
  next
445
476
  when :redo
446
477
  redo
447
- when :return
448
- return
449
478
  when String
450
479
  content = next_action
451
480
  end
@@ -458,6 +487,14 @@ class OllamaChat::Chat
458
487
 
459
488
  parse_content? and content = parse_content(content, @images)
460
489
 
490
+ messages << OllamaChat::Message.new(
491
+ role: 'user',
492
+ sender_name: user_name,
493
+ content: ,
494
+ images: @images.dup,
495
+ group_uuid:
496
+ )
497
+
461
498
  if runtime_info.on?
462
499
  tool_name = 'runtime_information'
463
500
  messages << OllamaChat::Message.new(
@@ -465,22 +502,17 @@ class OllamaChat::Chat
465
502
  tool_name: ,
466
503
  sender_name: tool_name,
467
504
  content: dynamic_runtime_information,
468
- images: @images.dup
505
+ images: @images.dup,
506
+ group_uuid:
469
507
  )
470
508
  end
471
-
472
- messages << OllamaChat::Message.new(
473
- role: 'user',
474
- sender_name: user_name,
475
- content: ,
476
- images: @images.dup
477
- )
478
509
  end
479
510
  @images.clear
480
511
  handler = OllamaChat::FollowChat.new(
481
512
  chat: self,
482
513
  messages:,
483
- voice: (voices.selected if voice.on?)
514
+ voice: (voices.selected if voice.on?),
515
+ group_uuid:
484
516
  )
485
517
  begin
486
518
  retried = false
@@ -493,7 +525,7 @@ class OllamaChat::Chat
493
525
  }
494
526
  end
495
527
  prepare_model(@model)
496
- ollama.chat(
528
+ call_ollama_chat(
497
529
  model: @model,
498
530
  messages: sent_messages,
499
531
  options: model_options,
@@ -534,6 +566,8 @@ class OllamaChat::Chat
534
566
  self.server_socket_message = nil
535
567
  end
536
568
  0
569
+ rescue OllamaChat::OllamaChatQuitError
570
+ STDOUT.puts "Goodbye."
537
571
  rescue ComplexConfig::AttributeMissing, ComplexConfig::ConfigurationSyntaxError => e
538
572
  log(:error, e)
539
573
  fix_config(e)
@@ -572,7 +606,7 @@ class OllamaChat::Chat
572
606
  if server_version.version < '0.9.0'.version
573
607
  raise 'require ollama API version 0.9.0 or higher'
574
608
  end
575
- log(:info, "Connection to #{base_url} established.")
609
+ log(:info, "Connection established", data: { base_url: })
576
610
  @ollama
577
611
  end
578
612
 
@@ -609,7 +643,7 @@ class OllamaChat::Chat
609
643
  end
610
644
  end
611
645
 
612
- # Adds documents from command line arguments to the document collection
646
+ # Adds documents from command line arguments to the document collection.
613
647
  #
614
648
  # Processes a list of document paths or URLs, handling both local files and
615
649
  # remote resources.
@@ -109,14 +109,17 @@ module OllamaChat::Clipboard
109
109
  # message to standard error and does not re-raise the exception.
110
110
  #
111
111
  # @param edit [truthy/falsy] If true, opens the content in the editor for
112
- # modification before copying (default: true)
112
+ # modification before copying (default: false)
113
113
  #
114
114
  # @return [TrueClass] if the copying has been performed successfully.
115
115
  def copy_to_clipboard(edit: false)
116
- perform_copy_to_clipboard(edit:)
116
+ text = last_message_content(content: false)
117
+ perform_copy_to_clipboard(text: text, edit:)
118
+ log(:info, "Copied to clipboard", data: { bytes: format_bytes(text.bytesize) }) if text
117
119
  STDOUT.puts "The last response has been successfully copied to the system clipboard."
118
120
  true
119
121
  rescue OllamaChat::OllamaChatError => e
122
+ log(:error, e, data: { method: __method__ })
120
123
  STDERR.puts e.message
121
124
  end
122
125
 
@@ -133,9 +136,11 @@ module OllamaChat::Clipboard
133
136
  # or nil if an error occurred
134
137
  def paste_from_clipboard(edit: false)
135
138
  result = perform_paste_from_clipboard(edit:)
139
+ log(:info, "Pasted from clipboard", data: { bytes: format_bytes(result.bytesize) }) if result
136
140
  STDOUT.puts "The clipboard content has been successfully copied to the chat."
137
141
  result
138
142
  rescue OllamaChat::OllamaChatError => e
143
+ log(:error, e, data: { method: __method__ })
139
144
  STDERR.puts e.message
140
145
  end
141
146
  end