ollama_chat 0.0.100 → 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.
- checksums.yaml +4 -4
- data/CHANGES.md +41 -0
- data/README.md +51 -1
- data/Rakefile +1 -0
- data/bin/ollama_chat_log +64 -0
- data/lib/ollama_chat/chat.rb +13 -4
- data/lib/ollama_chat/clipboard.rb +7 -2
- data/lib/ollama_chat/commands.rb +20 -4
- data/lib/ollama_chat/database/models/session.rb +20 -7
- data/lib/ollama_chat/favourites_management.rb +2 -0
- data/lib/ollama_chat/follow_chat.rb +43 -30
- data/lib/ollama_chat/history.rb +3 -0
- data/lib/ollama_chat/logging.rb +24 -3
- data/lib/ollama_chat/message_list.rb +11 -0
- data/lib/ollama_chat/model_handling.rb +1 -0
- data/lib/ollama_chat/prompt_handling.rb +5 -1
- data/lib/ollama_chat/prompt_management.rb +7 -1
- data/lib/ollama_chat/rag_handling.rb +9 -2
- data/lib/ollama_chat/session_management.rb +19 -7
- data/lib/ollama_chat/source_fetching.rb +20 -2
- data/lib/ollama_chat/tools/browse.rb +1 -1
- data/lib/ollama_chat/tools/compute_bmi.rb +2 -2
- data/lib/ollama_chat/tools/copy_to_clipboard.rb +1 -1
- data/lib/ollama_chat/tools/delete_file.rb +3 -2
- data/lib/ollama_chat/tools/directory_structure.rb +1 -1
- data/lib/ollama_chat/tools/eval_ruby.rb +2 -2
- data/lib/ollama_chat/tools/execute_grep.rb +5 -1
- data/lib/ollama_chat/tools/execute_ri.rb +1 -1
- data/lib/ollama_chat/tools/file_context.rb +1 -1
- data/lib/ollama_chat/tools/gem_path_lookup.rb +1 -1
- data/lib/ollama_chat/tools/generate_image.rb +1 -1
- data/lib/ollama_chat/tools/generate_password.rb +1 -1
- data/lib/ollama_chat/tools/get_current_weather.rb +1 -1
- data/lib/ollama_chat/tools/get_cve.rb +1 -1
- data/lib/ollama_chat/tools/get_endoflife.rb +1 -1
- data/lib/ollama_chat/tools/get_ghr.rb +1 -1
- data/lib/ollama_chat/tools/get_jira_issue.rb +1 -1
- data/lib/ollama_chat/tools/get_location.rb +1 -1
- data/lib/ollama_chat/tools/get_rfc.rb +1 -1
- data/lib/ollama_chat/tools/get_time.rb +1 -1
- data/lib/ollama_chat/tools/get_url.rb +1 -1
- data/lib/ollama_chat/tools/move_file.rb +3 -2
- data/lib/ollama_chat/tools/open_file_in_editor.rb +1 -1
- data/lib/ollama_chat/tools/paste_from_clipboard.rb +1 -1
- data/lib/ollama_chat/tools/paste_into_editor.rb +1 -1
- data/lib/ollama_chat/tools/patch_file.rb +60 -42
- data/lib/ollama_chat/tools/read_file.rb +14 -5
- data/lib/ollama_chat/tools/resolve_tag.rb +1 -1
- data/lib/ollama_chat/tools/retrieve_document_snippets.rb +6 -2
- data/lib/ollama_chat/tools/roll_dice.rb +1 -1
- data/lib/ollama_chat/tools/run_tests.rb +5 -2
- data/lib/ollama_chat/tools/search_web.rb +1 -1
- data/lib/ollama_chat/tools/write_file.rb +15 -11
- data/lib/ollama_chat/utils/log_viewer.rb +119 -0
- data/lib/ollama_chat/utils.rb +1 -0
- data/lib/ollama_chat/version.rb +1 -1
- data/lib/ollama_chat/web_searching.rb +3 -0
- data/ollama_chat.gemspec +7 -6
- data/spec/ollama_chat/chat_spec.rb +35 -8
- data/spec/ollama_chat/follow_chat_spec.rb +5 -0
- data/spec/ollama_chat/message_list_spec.rb +9 -0
- data/spec/ollama_chat/tools/patch_file_spec.rb +40 -12
- data/spec/ollama_chat/tools/read_file_spec.rb +9 -0
- data/spec/ollama_chat/utils/log_viewer_spec.rb +137 -0
- data/spec/ollama_chat/web_searching_spec.rb +2 -2
- metadata +22 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55511ef08f35b482ac7529dae4b9e4d9cb490bd37467fac563b966f3ad87f569
|
|
4
|
+
data.tar.gz: 26846b60e0ef32214b21a1e56128b4be6dbe6bd0808f195c8130186c01f8975c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d7960e9303887e5ef3571b79bffbf2cd30785c8db4051e6fc7a16f828e7d5dbe8754006a5356074c83fb989dd35b27b5311d1e676c330f4303a81014d16c4ac
|
|
7
|
+
data.tar.gz: dfcb0fee96b6093e0d30cd7cb4894aa25752054236d85a14161519fb781d1697c7011816441dbdddfeb3477872c057b9382c594b2691c40f3238c9c727d43843
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
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
|
+
|
|
3
44
|
## 2026-07-23 v0.0.100
|
|
4
45
|
|
|
5
46
|
### New Features
|
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
|
@@ -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'
|
data/bin/ollama_chat_log
ADDED
|
@@ -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
|
+
)
|
data/lib/ollama_chat/chat.rb
CHANGED
|
@@ -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.
|
|
@@ -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
|
-
# @
|
|
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,
|
|
@@ -289,7 +290,13 @@ class OllamaChat::Chat
|
|
|
289
290
|
# @param opts [Hash] keyword arguments for the chat call
|
|
290
291
|
# @param block [Proc] optional handler block
|
|
291
292
|
def call_ollama_chat(**opts, &block)
|
|
292
|
-
|
|
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
|
|
293
300
|
ollama.chat(**opts, &block)
|
|
294
301
|
end
|
|
295
302
|
|
|
@@ -414,6 +421,8 @@ class OllamaChat::Chat
|
|
|
414
421
|
# The method also handles server socket messages, manages chat history, and
|
|
415
422
|
# ensures proper cleanup and configuration handling throughout the
|
|
416
423
|
# interaction.
|
|
424
|
+
#
|
|
425
|
+
# @return [Integer] returns 0 on successful completion or raises an error
|
|
417
426
|
def interact_with_user
|
|
418
427
|
loop do
|
|
419
428
|
content = nil
|
|
@@ -634,7 +643,7 @@ class OllamaChat::Chat
|
|
|
634
643
|
end
|
|
635
644
|
end
|
|
636
645
|
|
|
637
|
-
# Adds documents from command line arguments to the document collection
|
|
646
|
+
# Adds documents from command line arguments to the document collection.
|
|
638
647
|
#
|
|
639
648
|
# Processes a list of document paths or URLs, handling both local files and
|
|
640
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:
|
|
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
|
-
|
|
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
|
data/lib/ollama_chat/commands.rb
CHANGED
|
@@ -523,21 +523,37 @@ module OllamaChat::Commands
|
|
|
523
523
|
|
|
524
524
|
command(
|
|
525
525
|
name: :conversation,
|
|
526
|
-
regexp: %r(^/conversation\s+(save|load)(
|
|
527
|
-
complete: [ 'conversation', %w[ save load ] ],
|
|
526
|
+
regexp: %r(^/conversation\s+(clean|save|load)(\s+-c)?(?:\s+([^-].*))?$),
|
|
527
|
+
complete: [ 'conversation', %w[ save load clean ] ],
|
|
528
528
|
options: '[-c]',
|
|
529
529
|
help: <<~EOT
|
|
530
530
|
💾 Save/Load conversation state
|
|
531
|
-
(-c to clean
|
|
531
|
+
(-c to clean before saving)
|
|
532
|
+
Or clean inplace (removes tool content, images, and thinking)
|
|
532
533
|
EOT
|
|
533
534
|
) do |subcommand,opts,path|
|
|
534
|
-
|
|
535
|
+
if %w[ save load ].include?(subcommand) && path.blank?
|
|
536
|
+
STDERR.puts "Require a path as argument to save/load!"
|
|
537
|
+
next :next
|
|
538
|
+
end
|
|
535
539
|
case subcommand
|
|
536
540
|
when 'save'
|
|
541
|
+
opts = go_command('c', opts.to_s)
|
|
537
542
|
save_conversation(path, clean: opts[?c])
|
|
538
543
|
when 'load'
|
|
539
544
|
load_conversation(path)
|
|
540
545
|
repair_group_uuids
|
|
546
|
+
when 'clean'
|
|
547
|
+
if confirm?(
|
|
548
|
+
prompt: '🔔 Clean tool content, images, and thinking from conversation? (y/n) ',
|
|
549
|
+
yes: /\Ay/i
|
|
550
|
+
)
|
|
551
|
+
then
|
|
552
|
+
messages.clean_messages!
|
|
553
|
+
STDOUT.puts "Conversation cleaned."
|
|
554
|
+
else
|
|
555
|
+
STDOUT.puts 'Cancelled.'
|
|
556
|
+
end
|
|
541
557
|
end
|
|
542
558
|
:next
|
|
543
559
|
end
|
|
@@ -50,6 +50,21 @@ class OllamaChat::Database::Models::Session < Sequel::Model(OllamaChat::DB)
|
|
|
50
50
|
Tins::Duration.new(updated_at ? now - updated_at : 0)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
# Estimates the token and byte size of the session's message history.
|
|
54
|
+
#
|
|
55
|
+
# @return [OllamaChat::TokenEstimator::Estimate] the estimated token and byte counts
|
|
56
|
+
def estimate_tokens
|
|
57
|
+
size_bytes = messages.to_s.size
|
|
58
|
+
OllamaChat::TokenEstimator.estimate(size_bytes)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Counts the number of messages in the session's JSONL history.
|
|
62
|
+
#
|
|
63
|
+
# @return [Integer] the number of messages stored in the session
|
|
64
|
+
def count_messages
|
|
65
|
+
messages.to_s.count(?\n)
|
|
66
|
+
end
|
|
67
|
+
|
|
53
68
|
# @!attribute [v] id
|
|
54
69
|
# @return [Integer] The primary key for the session.
|
|
55
70
|
#
|
|
@@ -128,13 +143,11 @@ class OllamaChat::Database::Models::Session < Sequel::Model(OllamaChat::DB)
|
|
|
128
143
|
# @!attribute [v] updated_at
|
|
129
144
|
# @return [Time, nil] The timestamp of the last update to the session.
|
|
130
145
|
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
# @param chat [OllamaChat::Chat] the active chat instance used to
|
|
135
|
-
#
|
|
136
|
-
# @return [OllamaChat::Database::Models::Session] a new session
|
|
137
|
-
# instance with default attributes
|
|
146
|
+
# Factory method that initializes a new Session model instance with a set
|
|
147
|
+
# of default values derived from the active chat configuration.
|
|
148
|
+
#
|
|
149
|
+
# @param chat [OllamaChat::Chat] the active chat instance used to extract defaults
|
|
150
|
+
# @return [OllamaChat::Database::Models::Session] a new session instance with default attributes
|
|
138
151
|
def self.with_defaults(chat)
|
|
139
152
|
tools_default_enabled =
|
|
140
153
|
chat.config.tools.functions.to_h.
|
|
@@ -54,6 +54,7 @@ module OllamaChat::FavouritesManagement
|
|
|
54
54
|
return
|
|
55
55
|
when SearchUI::Wrapper
|
|
56
56
|
models::Favourite.create(context: type, name: chosen.value)
|
|
57
|
+
log(:info, "Favourite added", data: { context: type, name: chosen.value })
|
|
57
58
|
end
|
|
58
59
|
end
|
|
59
60
|
end
|
|
@@ -80,6 +81,7 @@ module OllamaChat::FavouritesManagement
|
|
|
80
81
|
return
|
|
81
82
|
when SearchUI::Wrapper
|
|
82
83
|
models::Favourite.where(context: type, name: chosen.value).destroy
|
|
84
|
+
log(:info, "Favourite removed", data: { context: type, name: chosen.value })
|
|
83
85
|
end
|
|
84
86
|
end
|
|
85
87
|
end
|
|
@@ -15,12 +15,13 @@ class OllamaChat::FollowChat
|
|
|
15
15
|
|
|
16
16
|
# Initializes a new instance of OllamaChat::FollowChat.
|
|
17
17
|
#
|
|
18
|
-
# @param [OllamaChat::Chat]
|
|
18
|
+
# @param chat [OllamaChat::Chat] The chat object, which represents the
|
|
19
19
|
# conversation context.
|
|
20
|
-
# @param [#to_a]
|
|
20
|
+
# @param messages [#to_a] A collection of message objects, representing the
|
|
21
21
|
# conversation history.
|
|
22
|
-
# @param [String]
|
|
23
|
-
# @param [
|
|
22
|
+
# @param group_uuid [String, nil] The group UUID for the conversation.
|
|
23
|
+
# @param voice [String, nil] (optional) The voice to speak with.
|
|
24
|
+
# @param output [IO] (optional) The output stream where terminal output
|
|
24
25
|
# should be printed. Defaults to STDOUT.
|
|
25
26
|
#
|
|
26
27
|
# @return [OllamaChat::FollowChat] A new instance of OllamaChat::FollowChat.
|
|
@@ -167,21 +168,20 @@ class OllamaChat::FollowChat
|
|
|
167
168
|
)
|
|
168
169
|
result = OllamaChat::Tools.registered[name].execute(tool_call, chat:)
|
|
169
170
|
if confirmed == :explicit
|
|
170
|
-
chat.log(:info, "Tool execution confirmed", data: { tool: name,
|
|
171
|
+
chat.log(:info, "Tool execution confirmed", data: { tool: name, confirmed: })
|
|
171
172
|
else
|
|
172
|
-
chat.log(:info, "Tool execution confirmed", data: { tool: name,
|
|
173
|
+
chat.log(:info, "Tool execution confirmed", data: { tool: name, confirmed: })
|
|
173
174
|
end
|
|
174
175
|
end
|
|
175
176
|
|
|
176
177
|
chat.tool_call_results[name] << result
|
|
177
|
-
|
|
178
178
|
data = nil
|
|
179
179
|
message = begin
|
|
180
180
|
data = JSON.parse(result)
|
|
181
|
-
chat.log(:info, "Tool result", data:)
|
|
182
|
-
data['message']
|
|
181
|
+
chat.log(:info, "Tool result", data: { result: data })
|
|
182
|
+
data.to_hash['message'] if data.respond_to?(:to_hash)
|
|
183
183
|
rescue
|
|
184
|
-
chat.log(:info, "Tool result", data: {
|
|
184
|
+
chat.log(:info, "Tool result", data: { result: })
|
|
185
185
|
nil
|
|
186
186
|
end
|
|
187
187
|
warn =
|
|
@@ -346,39 +346,52 @@ class OllamaChat::FollowChat
|
|
|
346
346
|
end
|
|
347
347
|
end
|
|
348
348
|
|
|
349
|
-
#
|
|
350
|
-
#
|
|
349
|
+
# Extracts performance statistics from an Ollama response and formats them
|
|
350
|
+
# into a hash of human-readable strings and metrics.
|
|
351
351
|
#
|
|
352
|
-
# @param response [
|
|
353
|
-
#
|
|
354
|
-
|
|
355
|
-
# the evaluation process including durations, counts, and rates, styled
|
|
356
|
-
# with colors and formatting
|
|
357
|
-
def eval_stats(response)
|
|
352
|
+
# @param response [Object] the response object containing evaluation metrics
|
|
353
|
+
# @return [Hash] a hash of formatted statistics including durations, counts, and rates
|
|
354
|
+
def stats_hash(response)
|
|
358
355
|
eval_duration = response.eval_duration.to_f / 1e9
|
|
359
356
|
prompt_eval_duration = response.prompt_eval_duration.to_f / 1e9
|
|
360
|
-
|
|
361
|
-
eval_duration: Tins::Duration.new(eval_duration),
|
|
357
|
+
{
|
|
358
|
+
eval_duration: Tins::Duration.new(eval_duration).to_s,
|
|
362
359
|
eval_count: response.eval_count.to_i,
|
|
363
|
-
eval_rate:
|
|
364
|
-
prompt_eval_duration: Tins::Duration.new(prompt_eval_duration),
|
|
360
|
+
eval_rate: "%.2f t/s" % (response.eval_count.to_i / eval_duration),
|
|
361
|
+
prompt_eval_duration: Tins::Duration.new(prompt_eval_duration).to_s,
|
|
365
362
|
prompt_eval_count: response.prompt_eval_count.to_i,
|
|
366
|
-
prompt_eval_rate:
|
|
367
|
-
total_duration: Tins::Duration.new(response.total_duration / 1e9),
|
|
368
|
-
load_duration: Tins::Duration.new(response.load_duration / 1e9),
|
|
369
|
-
}
|
|
363
|
+
prompt_eval_rate: "%.2f t/s" % (response.prompt_eval_count.to_i / prompt_eval_duration),
|
|
364
|
+
total_duration: Tins::Duration.new(response.total_duration / 1e9).to_s,
|
|
365
|
+
load_duration: Tins::Duration.new(response.load_duration / 1e9).to_s,
|
|
366
|
+
}
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# The eval_stats method processes a statistics hash and formats it into a
|
|
370
|
+
# colored, readable string output.
|
|
371
|
+
#
|
|
372
|
+
# @param stats [Hash] the statistics hash containing evaluation metrics
|
|
373
|
+
#
|
|
374
|
+
# @return [String] a formatted string with statistical information about
|
|
375
|
+
# the evaluation process including durations, counts, and rates, styled
|
|
376
|
+
# with colors and formatting
|
|
377
|
+
def eval_stats(stats)
|
|
378
|
+
stats_text = stats.
|
|
379
|
+
deep_transform(value: -> idx, v { idx =~ /_rate\z/ ? bold(v) + color(111) : v }).
|
|
380
|
+
map { _1 * ?= } * ' '
|
|
370
381
|
'📊 ' + color(111) {
|
|
371
382
|
Kramdown::ANSI::Width.wrap(stats_text, percentage: 90).gsub(/(?<!\A)^/, ' ')
|
|
372
383
|
}
|
|
373
384
|
end
|
|
374
385
|
|
|
375
|
-
# The output_eval_stats method
|
|
376
|
-
#
|
|
386
|
+
# The output_eval_stats method extracts performance statistics from the response,
|
|
387
|
+
# logs them to the chat history, and prints a formatted summary to the terminal.
|
|
377
388
|
#
|
|
378
|
-
# @param response [
|
|
389
|
+
# @param response [Object] the response object containing evaluation data
|
|
379
390
|
def output_eval_stats(response)
|
|
380
391
|
response.done or return
|
|
381
|
-
|
|
392
|
+
stats = stats_hash(response)
|
|
393
|
+
chat.log(:info, 'Ollama chat response received', data: { stats: })
|
|
394
|
+
@output.puts "", eval_stats(stats)
|
|
382
395
|
end
|
|
383
396
|
|
|
384
397
|
# The debug_output method conditionally outputs the response object using jj
|
data/lib/ollama_chat/history.rb
CHANGED
|
@@ -81,6 +81,7 @@ module OllamaChat::History
|
|
|
81
81
|
read_io(input:).to_a
|
|
82
82
|
Reline::HISTORY.clear
|
|
83
83
|
Reline::HISTORY.push(*history_data)
|
|
84
|
+
log(:info, "History initialized", data: { entries: history_data.size })
|
|
84
85
|
end
|
|
85
86
|
end
|
|
86
87
|
|
|
@@ -96,6 +97,7 @@ module OllamaChat::History
|
|
|
96
97
|
OllamaChat::Utils::JSONJSONLIO.new('as.jsonl').
|
|
97
98
|
write_io(output:, collection: Reline::HISTORY)
|
|
98
99
|
session.history = output.string
|
|
100
|
+
log(:info, "History saved", data: { entries: Reline::HISTORY.size })
|
|
99
101
|
end
|
|
100
102
|
end
|
|
101
103
|
|
|
@@ -113,6 +115,7 @@ module OllamaChat::History
|
|
|
113
115
|
Reline::HISTORY.clear
|
|
114
116
|
ensure
|
|
115
117
|
save_history
|
|
118
|
+
log(:info, "History cleared")
|
|
116
119
|
end
|
|
117
120
|
end
|
|
118
121
|
end
|
data/lib/ollama_chat/logging.rb
CHANGED
|
@@ -31,15 +31,36 @@ module OllamaChat::Logging
|
|
|
31
31
|
payload = {
|
|
32
32
|
msg: ,
|
|
33
33
|
data: data || {}
|
|
34
|
-
}
|
|
34
|
+
}.symbolize_keys_recursive
|
|
35
35
|
|
|
36
36
|
if msg.is_a?(Exception)
|
|
37
|
+
if backtrace = msg.ask_and_send(:backtrace)
|
|
38
|
+
payload[:data][:backtrace] = backtrace
|
|
39
|
+
end
|
|
37
40
|
payload[:msg] = msg = "#{msg.class}: #{msg.message}"
|
|
38
|
-
payload[:data][:backtrace] = msg.ask_and_send(:backtrace)
|
|
39
41
|
else
|
|
40
|
-
payload[:msg] = msg.to_s
|
|
42
|
+
payload[:msg] = msg = msg.to_s
|
|
41
43
|
end
|
|
42
44
|
|
|
45
|
+
# Sanitize payload for safe JSON serialization: break circular references
|
|
46
|
+
# and coerce non-serializable objects to strings while preserving
|
|
47
|
+
# primitives/hashes/arrays
|
|
48
|
+
payload = payload.deep_transform(
|
|
49
|
+
circular: ?…,
|
|
50
|
+
value: -> node {
|
|
51
|
+
keep = [
|
|
52
|
+
responding?(:to_hash), responding?(:to_ary), responding?(:to_int),
|
|
53
|
+
true, false, nil,
|
|
54
|
+
]
|
|
55
|
+
case node
|
|
56
|
+
when *keep
|
|
57
|
+
node
|
|
58
|
+
else
|
|
59
|
+
node.to_s
|
|
60
|
+
end
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
43
64
|
logger.send(severity, payload)
|
|
44
65
|
warn and self.warn(msg)
|
|
45
66
|
nil
|
|
@@ -204,6 +204,17 @@ class OllamaChat::MessageList
|
|
|
204
204
|
end
|
|
205
205
|
end
|
|
206
206
|
|
|
207
|
+
# Cleans the messages in the list by replacing them with stripped versions.
|
|
208
|
+
# This is a destructive (mutating) operation.
|
|
209
|
+
#
|
|
210
|
+
# @param messages [Array<OllamaChat::Message>] the messages to clean.
|
|
211
|
+
# Defaults to all current messages.
|
|
212
|
+
# @return [OllamaChat::MessageList] self to allow for method chaining.
|
|
213
|
+
def clean_messages!(messages: @messages)
|
|
214
|
+
@messages = clean_messages(messages:)
|
|
215
|
+
self
|
|
216
|
+
end
|
|
217
|
+
|
|
207
218
|
# Displays the most recent messages from the conversation history.
|
|
208
219
|
#
|
|
209
220
|
# This method prints a specified number of trailing messages to the console
|
|
@@ -88,7 +88,11 @@ module OllamaChat::PromptHandling
|
|
|
88
88
|
patterns = Array(patterns.full? || '**/*.{txt,md}')
|
|
89
89
|
filename = choose_filename(patterns)
|
|
90
90
|
|
|
91
|
-
filename.read if filename&.exist?
|
|
91
|
+
content = filename.read if filename&.exist?
|
|
92
|
+
log(:info, "Prompt loaded from file", data: {
|
|
93
|
+
file: filename&.to_s, bytes: content&.size
|
|
94
|
+
})
|
|
95
|
+
content
|
|
92
96
|
end
|
|
93
97
|
|
|
94
98
|
|
|
@@ -92,7 +92,8 @@ module OllamaChat::PromptManagement
|
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
prompt_content = edit_text(content)
|
|
95
|
-
store_prompt(name, prompt_content, context:)
|
|
95
|
+
store_prompt(name, prompt_content, context:)
|
|
96
|
+
log(:info, "Prompt added", data: { name:, context: })
|
|
96
97
|
true
|
|
97
98
|
end
|
|
98
99
|
end
|
|
@@ -114,6 +115,7 @@ module OllamaChat::PromptManagement
|
|
|
114
115
|
yes: /\Ay/i
|
|
115
116
|
) or return
|
|
116
117
|
selected_prompt.destroy
|
|
118
|
+
log(:info, "Prompt deleted", data: { name: selected_prompt.name, context: })
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
# Interactively selects an existing prompt and allows the user to edit its
|
|
@@ -125,6 +127,7 @@ module OllamaChat::PromptManagement
|
|
|
125
127
|
selected_prompt = choose_prompt(context:, prompt: 'Which spell needs some fine-tuning? %s') or return
|
|
126
128
|
selected_prompt.metadata['content'] = edit_text(selected_prompt.metadata['content'].to_s)
|
|
127
129
|
selected_prompt.save
|
|
130
|
+
log(:info, "Prompt edited", data: { name: selected_prompt.name, context: })
|
|
128
131
|
self
|
|
129
132
|
end
|
|
130
133
|
|
|
@@ -164,6 +167,7 @@ module OllamaChat::PromptManagement
|
|
|
164
167
|
duplicated_prompt.name = name
|
|
165
168
|
duplicated_prompt.metadata['default'] = false
|
|
166
169
|
duplicated_prompt.save
|
|
170
|
+
log(:info, "Prompt duplicated", data: { name:, old_name: selected_prompt.name, context: })
|
|
167
171
|
self
|
|
168
172
|
end
|
|
169
173
|
|
|
@@ -197,6 +201,7 @@ module OllamaChat::PromptManagement
|
|
|
197
201
|
prompt_name = determine_valid_new_name_for_prompt('to import', context:) or return
|
|
198
202
|
prompt_content = filename.read
|
|
199
203
|
store_prompt(prompt_name, prompt_content, context:)
|
|
204
|
+
log(:info, "Prompt imported", data: { name: prompt_name, source: filename.to_s, context: })
|
|
200
205
|
STDOUT.puts "Imported prompt as #{prompt_name.inspect}."
|
|
201
206
|
self
|
|
202
207
|
end
|
|
@@ -220,6 +225,7 @@ module OllamaChat::PromptManagement
|
|
|
220
225
|
)
|
|
221
226
|
filename = determine_valid_output_filename('to write to') or return
|
|
222
227
|
filename.write(selected_prompt.to_s)
|
|
228
|
+
log(:info, "Prompt exported", data: { name: selected_prompt.name, dest: filename.to_s, context: })
|
|
223
229
|
STDOUT.puts "Prompt #{selected_prompt.name.inspect} was exported as #{filename.to_path.inspect}?"
|
|
224
230
|
self
|
|
225
231
|
end
|