ollama_chat 0.0.137 ā 0.0.139
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 +110 -0
- data/Rakefile +1 -1
- data/lib/ollama_chat/chat.rb +1 -1
- data/lib/ollama_chat/command_concern.rb +7 -4
- data/lib/ollama_chat/compaction.rb +1 -1
- data/lib/ollama_chat/conversation.rb +1 -1
- data/lib/ollama_chat/database/models/collection.rb +1 -1
- data/lib/ollama_chat/dialog.rb +1 -1
- data/lib/ollama_chat/http_handling.rb +1 -1
- data/lib/ollama_chat/information.rb +3 -2
- data/lib/ollama_chat/kramdown_ansi.rb +107 -21
- data/lib/ollama_chat/links_set.rb +1 -0
- data/lib/ollama_chat/model_handling.rb +15 -7
- data/lib/ollama_chat/rag_handling.rb +24 -12
- data/lib/ollama_chat/session_management.rb +17 -15
- data/lib/ollama_chat/source_fetching.rb +16 -1
- data/lib/ollama_chat/tools/browse.rb +4 -4
- data/lib/ollama_chat/tools/concern.rb +1 -0
- data/lib/ollama_chat/tools/execute_grep.rb +3 -3
- data/lib/ollama_chat/tools/generate_image.rb +2 -2
- data/lib/ollama_chat/tools/get_url.rb +5 -0
- data/lib/ollama_chat/utils/analyze_directory.rb +19 -19
- data/lib/ollama_chat/utils/fetcher.rb +2 -1
- data/lib/ollama_chat/utils/redact.rb +32 -0
- data/lib/ollama_chat/utils.rb +1 -0
- data/lib/ollama_chat/version.rb +1 -1
- data/lib/ollama_chat/web_searching.rb +1 -1
- data/ollama_chat.gemspec +5 -5
- data/spec/ollama_chat/compaction_spec.rb +1 -1
- data/spec/ollama_chat/information_spec.rb +23 -0
- data/spec/ollama_chat/kramdown_ansi_spec.rb +54 -2
- data/spec/ollama_chat/parsing_spec.rb +5 -4
- data/spec/ollama_chat/rag_handling_spec.rb +62 -3
- data/spec/ollama_chat/tools/get_url_spec.rb +24 -5
- data/spec/ollama_chat/tools/list_directory_spec.rb +10 -3
- data/spec/ollama_chat/utils/analyze_directory_spec.rb +33 -21
- data/spec/ollama_chat/web_searching_spec.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e1a0cfa8c0914331039a424abe408286dce18f50b3b593bc3cac04c551c9b078
|
|
4
|
+
data.tar.gz: ab96f2413e6ab996329d654d1f7dc60b75d99cf6cd2bb93f5c0b1f5bc09627a7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 987632cebff57817b71ad6f56326c85edf921a4f54c1a42fff906e93603ec7c45113be2eb0b57b372987f9050d62213f54985359fdaf84f4bbc5bcd4bcdf8392
|
|
7
|
+
data.tar.gz: 9d9cee9148bc2c3542c9eb543015fa5b16393832f2741b56926f8d6b37a0d0633abea5252d32a5d3b931e350d4e44d874e857cdf84ae72d0c70ab79d8b747183
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,115 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-10-30 v0.0.139
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Added terminal table wrapping for `/help` and `/session list` output. New
|
|
8
|
+
private methods `wrap_table_rows` and `table_column_budgets` in
|
|
9
|
+
`OllamaChat::KramdownANSI` compute per-column width budgets and wrap cell
|
|
10
|
+
content to fit within a configurable percentage of the terminal width.
|
|
11
|
+
- Updated `help_message` and `list_sessions` to collect rows into arrays and
|
|
12
|
+
route them through `wrap_table_rows`, removing manual truncation and heading
|
|
13
|
+
assignment.
|
|
14
|
+
|
|
15
|
+
### Fixes
|
|
16
|
+
|
|
17
|
+
- Fixed a `TypeError` in `Collection.sync` when `chat.documents.collections`
|
|
18
|
+
yielded no collections (e.g., fresh database) by using `.to_a` instead of
|
|
19
|
+
relying on `Tins::NullClass#to_ary`.
|
|
20
|
+
- Improved error handling in `fetch_source` for `OllamaChat::HTTPError` by
|
|
21
|
+
adding a dedicated rescue clause that logs a warning without a backtrace and
|
|
22
|
+
stamps `response_status` and `response_reason` onto the
|
|
23
|
+
`ResponseMetadata.failed` IO.
|
|
24
|
+
- Added a non-2xx HTTP status guard in the `get_url` tool to short-circuit
|
|
25
|
+
processing before document policy dispatch, returning the raw body and a
|
|
26
|
+
status message instead of feeding error pages to the summarizer.
|
|
27
|
+
|
|
28
|
+
### Improvements
|
|
29
|
+
|
|
30
|
+
- Updated the compaction voice report to format context fill percentages as
|
|
31
|
+
rounded integers with the unit "percent" for grammatical completeness
|
|
32
|
+
(e.g., "from 38 percent to 19 percent").
|
|
33
|
+
- Moved `configure_kramdown_ansi_styles` to a private method in
|
|
34
|
+
`OllamaChat::KramdownANSI`.
|
|
35
|
+
- Expanded YARD documentation for `OllamaChat::KramdownANSI` to describe
|
|
36
|
+
styling, parsing, and table wrapping concerns.
|
|
37
|
+
|
|
38
|
+
### Build & CI
|
|
39
|
+
|
|
40
|
+
- Bumped `kramdown-ansi` dependency from `~> 0.7` to `~> 0.9` in `Rakefile`
|
|
41
|
+
and `ollama_chat.gemspec`.
|
|
42
|
+
|
|
43
|
+
### Testing
|
|
44
|
+
|
|
45
|
+
- Updated `compaction_spec.rb` to expect rounded integer percentages in the
|
|
46
|
+
spoken compaction report.
|
|
47
|
+
- Updated `kramdown_ansi_spec.rb` to use a real `OllamaChat::Chat` instance
|
|
48
|
+
instead of a double and added examples for `wrap_table_rows` and
|
|
49
|
+
`table_column_budgets`.
|
|
50
|
+
- Updated `get_url_spec.rb` to stub `response_status: nil` on `SourceIO`
|
|
51
|
+
doubles and added a spec verifying that non-200 statuses skip summarization
|
|
52
|
+
and import.
|
|
53
|
+
|
|
54
|
+
## 2026-10-28 v0.0.138
|
|
55
|
+
|
|
56
|
+
### Features
|
|
57
|
+
|
|
58
|
+
- Added `OllamaChat::Utils::Redact` module with `redact_password` to mask
|
|
59
|
+
credentials in URLs for user-facing display.
|
|
60
|
+
- `memory_restore` now deduplicates entries by checking existing tags before
|
|
61
|
+
adding records, reporting the number of skipped duplicates.
|
|
62
|
+
- `OllamaChat::Utils::AnalyzeDirectory` now returns a `Hash` with `:height` and
|
|
63
|
+
`:entries` keys instead of a bare `Array`. Entry hashes no longer include
|
|
64
|
+
`:type` or `:name`; directories are identified by a trailing `/` in `:path`.
|
|
65
|
+
|
|
66
|
+
### Fixes
|
|
67
|
+
|
|
68
|
+
- `use_model` now raises `OllamaChat::UnknownModelError` if the previous model
|
|
69
|
+
is missing or blank after a cancelled pull, preventing stale metadata from
|
|
70
|
+
being returned.
|
|
71
|
+
- Fixed `update_collection` to use `Set` for the `seen` variable, resolving a
|
|
72
|
+
latent `NoMethodError` when upstream yields a `Set`.
|
|
73
|
+
- Corrected the error message in `pull_model_unless_present` to reference the
|
|
74
|
+
`model` parameter instead of the potentially stale `@model` instance
|
|
75
|
+
variable.
|
|
76
|
+
|
|
77
|
+
### Improvements
|
|
78
|
+
|
|
79
|
+
- Applied password redaction to all user-visible URL displays, including
|
|
80
|
+
`Chat#inspect`, `Dialog#connect_message`, `Information#info_rag`,
|
|
81
|
+
`Information#print_welcome`, `Tools::Browse#execute`,
|
|
82
|
+
`Tools::GenerateImage#execute`, and `Fetcher.get`.
|
|
83
|
+
- Standardized `Set` insertion to use the `<<` idiom across `conversation.rb`,
|
|
84
|
+
`http_handling.rb`, `rag_handling.rb`, `generate_image.rb`, and
|
|
85
|
+
`web_searching.rb`.
|
|
86
|
+
- Added `<<` alias to `LinksSet` to support the standard `Set` insertion idiom.
|
|
87
|
+
- `AnalyzeDirectory` now omits the `:children` key from directory entries when
|
|
88
|
+
the array is empty, both during build and pruning.
|
|
89
|
+
|
|
90
|
+
### Build & CI
|
|
91
|
+
|
|
92
|
+
- Added `lib/ollama_chat/utils/redact.rb` to the gemspec file list.
|
|
93
|
+
|
|
94
|
+
### Testing
|
|
95
|
+
|
|
96
|
+
- Updated `analyze_directory_spec.rb`, `parsing_spec.rb`, and
|
|
97
|
+
`list_directory_spec.rb` to assert the new `AnalyzeDirectory` return
|
|
98
|
+
structure.
|
|
99
|
+
- Added specs for `redact_password` covering URI/String inputs, passthrough for
|
|
100
|
+
URLs without passwords, and non-mutation of the original object.
|
|
101
|
+
- Added specs for `memory_restore` deduplication logic, covering partial and
|
|
102
|
+
full skip scenarios.
|
|
103
|
+
- Updated `web_searching_spec.rb` to expect `<<` instead of `add` for link
|
|
104
|
+
insertion.
|
|
105
|
+
|
|
106
|
+
### Documentation
|
|
107
|
+
|
|
108
|
+
- Updated `execute_grep` tool description to explicitly state that the regex
|
|
109
|
+
runs against file contents.
|
|
110
|
+
- Updated YARD documentation for `AnalyzeDirectory` to reflect the new return
|
|
111
|
+
value structure.
|
|
112
|
+
|
|
3
113
|
## 2026-10-23 v0.0.137
|
|
4
114
|
|
|
5
115
|
### Fixes
|
data/Rakefile
CHANGED
|
@@ -47,7 +47,7 @@ GemHadar do
|
|
|
47
47
|
dependency 'redis', '~> 5.0'
|
|
48
48
|
dependency 'mime-types', '~> 3.0'
|
|
49
49
|
dependency 'reverse_markdown', '~> 3.0'
|
|
50
|
-
dependency 'kramdown-ansi', '~> 0.
|
|
50
|
+
dependency 'kramdown-ansi', '~> 0.9'
|
|
51
51
|
dependency 'complex_config', '~> 0.22', '>= 0.22.2'
|
|
52
52
|
dependency 'tins', '~> 1.61'
|
|
53
53
|
dependency 'search_ui', '>= 0.2'
|
data/lib/ollama_chat/chat.rb
CHANGED
|
@@ -6,6 +6,8 @@ module OllamaChat::CommandConcern
|
|
|
6
6
|
extend Tins::Concern
|
|
7
7
|
|
|
8
8
|
class_methods do
|
|
9
|
+
include OllamaChat::KramdownANSI
|
|
10
|
+
|
|
9
11
|
# @!attribute [rw] commands
|
|
10
12
|
# @return [Hash<Symbol, Command>] A hash mapping command names to
|
|
11
13
|
# `Command` instances.
|
|
@@ -76,8 +78,8 @@ module OllamaChat::CommandConcern
|
|
|
76
78
|
all_separators: true,
|
|
77
79
|
border: :unicode_round,
|
|
78
80
|
}
|
|
79
|
-
table.headings = %w[ CMD SUBCMD OPTS HELP ]
|
|
80
81
|
sorted_commands = self.commands.sort_by { [ _2.category, _2.name ] }.map(&:last)
|
|
82
|
+
rows = []
|
|
81
83
|
cat = nil
|
|
82
84
|
sorted_commands.each do |command|
|
|
83
85
|
command.help or next
|
|
@@ -93,15 +95,16 @@ module OllamaChat::CommandConcern
|
|
|
93
95
|
}
|
|
94
96
|
if cat != command.category
|
|
95
97
|
cat = command.category
|
|
96
|
-
|
|
98
|
+
rows << [ { value: bold{cat}, colspan: 4 } ]
|
|
97
99
|
end
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
rows << [
|
|
101
|
+
'%s' % command.command_names.map { ?/ + _1 }.join(?\n),
|
|
100
102
|
'%s' % subcommands,
|
|
101
103
|
command.options,
|
|
102
104
|
command.help,
|
|
103
105
|
]
|
|
104
106
|
end
|
|
107
|
+
wrap_table_rows(rows, 4, headings: %w[ CMD SUBCMD OPTS HELP ], percentage: 95).each { |row| table << row }
|
|
105
108
|
table
|
|
106
109
|
end
|
|
107
110
|
end
|
|
@@ -327,7 +327,7 @@ module OllamaChat::Compaction
|
|
|
327
327
|
duration.minutes? ? '%M minutes and %u seconds' : '%u seconds'
|
|
328
328
|
)
|
|
329
329
|
speak(
|
|
330
|
-
'Compressed conversation context from %.
|
|
330
|
+
'Compressed conversation context from %.0f percent to %.0f percent in %s.' % [
|
|
331
331
|
100 * result.context_filled_before,
|
|
332
332
|
100 * result.context_filled_after,
|
|
333
333
|
duration_string,
|
|
@@ -51,7 +51,7 @@ class OllamaChat::Database::Models::Collection < Sequel::Model(OllamaChat::DB)
|
|
|
51
51
|
#
|
|
52
52
|
# @param chat [OllamaChat::Chat] the chat instance providing the configuration
|
|
53
53
|
def self.sync(chat)
|
|
54
|
-
all_collections = (%w[ default ] + chat.documents.collections).to_set
|
|
54
|
+
all_collections = (%w[ default ] + chat.documents.collections.to_a).to_set
|
|
55
55
|
all_collections.each do |name|
|
|
56
56
|
where(name: name.to_s).first and next
|
|
57
57
|
create(
|
data/lib/ollama_chat/dialog.rb
CHANGED
|
@@ -142,7 +142,7 @@ module OllamaChat::Dialog
|
|
|
142
142
|
# distinguish the connection type, e.g. `"embedding "` for a dedicated
|
|
143
143
|
# embedding host (default: `""`)
|
|
144
144
|
def connect_message(base_url:, prefix: '')
|
|
145
|
-
feedback green { "Connecting to #{prefix}ollama #{base_url.
|
|
145
|
+
feedback green { "Connecting to #{prefix}ollama #{redact_password(base_url).inspect}ā¦" }, type: :info
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
# Displays a success-level feedback message confirming that a
|
|
@@ -61,7 +61,7 @@ module OllamaChat::HTTPHandling
|
|
|
61
61
|
# @yield [IO] yields a temporary file handle for the caller to consume.
|
|
62
62
|
# @return [Object] the result of the block or the fetcher.
|
|
63
63
|
def get_url(url, headers: nil, cache: nil, remember: true, &block)
|
|
64
|
-
remember and links
|
|
64
|
+
remember and links << url.to_s
|
|
65
65
|
headers = config.request_headers?.to_h | headers
|
|
66
66
|
OllamaChat::Utils::Fetcher.get(
|
|
67
67
|
url,
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
# chat.usage
|
|
17
17
|
module OllamaChat::Information
|
|
18
18
|
extend Tins::Concern
|
|
19
|
+
include OllamaChat::Utils::Redact
|
|
19
20
|
|
|
20
21
|
included do
|
|
21
22
|
include UserAgent
|
|
@@ -190,7 +191,7 @@ module OllamaChat::Information
|
|
|
190
191
|
def info_rag(output: STDOUT)
|
|
191
192
|
if embedding.on?
|
|
192
193
|
if base_url = OC::OLLAMA::EMBEDDING_URL?
|
|
193
|
-
output.puts "š Connected to embedding ollama server on: #{base_url
|
|
194
|
+
output.puts "š Connected to embedding ollama server on: #{redact_password(base_url)}"
|
|
194
195
|
end
|
|
195
196
|
output.puts "šļø Current RAG model is #{bold{@embedding_model}}"
|
|
196
197
|
if @embedding_model_options.present?
|
|
@@ -220,7 +221,7 @@ module OllamaChat::Information
|
|
|
220
221
|
def print_welcome(output: STDOUT)
|
|
221
222
|
feedback(<<~EOT, output:)
|
|
222
223
|
š Running ollama_chat version: #{bold{OllamaChat::VERSION}}
|
|
223
|
-
š Connected to ollama server version: #{bold{server_version}} on: #{bold{server_url}}
|
|
224
|
+
š Connected to ollama server version: #{bold{server_version}} on: #{bold{redact_password(server_url)}}
|
|
224
225
|
EOT
|
|
225
226
|
end
|
|
226
227
|
|
|
@@ -1,30 +1,22 @@
|
|
|
1
|
-
# A module that provides Kramdown::ANSI styling
|
|
2
|
-
#
|
|
1
|
+
# A module that provides Kramdown::ANSI styling, parsing, and
|
|
2
|
+
# terminal table wrapping for OllamaChat.
|
|
3
3
|
#
|
|
4
|
-
# This module handles
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
4
|
+
# This module handles three concerns:
|
|
5
|
+
#
|
|
6
|
+
# * **Styling configuration** ā sets up ANSI styles either from the
|
|
7
|
+
# `KRAMDOWN_ANSI_OLLAMA_CHAT_STYLES` environment variable or from
|
|
8
|
+
# built-in defaults.
|
|
9
|
+
# * **Markdown parsing** ā converts raw markdown into ANSI-formatted
|
|
10
|
+
# terminal output using the configured styles, with a fallback for
|
|
11
|
+
# invalid encodings.
|
|
12
|
+
# * **Table wrapping** ā computes per-column width budgets and wraps
|
|
13
|
+
# cell content so that `Terminal::Table` output stays within a
|
|
14
|
+
# configurable percentage of the terminal width.
|
|
9
15
|
#
|
|
10
16
|
# @example Configuring custom ANSI styles via environment variable
|
|
11
17
|
# Set KRAMDOWN_ANSI_OLLAMA_CHAT_STYLES to a JSON object containing style
|
|
12
18
|
# definitions for customizing markdown output formatting in the terminal.
|
|
13
19
|
module OllamaChat::KramdownANSI
|
|
14
|
-
# The configure_kramdown_ansi_styles method sets up ANSI styling for
|
|
15
|
-
# Kramdown::ANSI output by checking for specific environment variables and
|
|
16
|
-
# falling back to default styles.
|
|
17
|
-
#
|
|
18
|
-
# @return [ Hash ] a hash of ANSI styles configured either from environment
|
|
19
|
-
# variables or using default settings
|
|
20
|
-
def configure_kramdown_ansi_styles
|
|
21
|
-
if json = OC::KRAMDOWN_ANSI_OLLAMA_CHAT_STYLES?
|
|
22
|
-
Kramdown::ANSI::Styles.from_json(json).ansi_styles
|
|
23
|
-
else
|
|
24
|
-
Kramdown::ANSI::Styles.new.ansi_styles
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
20
|
# The kramdown_ansi_parse method processes content using Kramdown::ANSI with
|
|
29
21
|
# custom ANSI styles.
|
|
30
22
|
#
|
|
@@ -49,6 +41,22 @@ module OllamaChat::KramdownANSI
|
|
|
49
41
|
retry
|
|
50
42
|
end
|
|
51
43
|
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# The configure_kramdown_ansi_styles method sets up ANSI styling for
|
|
47
|
+
# Kramdown::ANSI output by checking for specific environment variables and
|
|
48
|
+
# falling back to default styles.
|
|
49
|
+
#
|
|
50
|
+
# @return [ Hash ] a hash of ANSI styles configured either from environment
|
|
51
|
+
# variables or using default settings
|
|
52
|
+
def configure_kramdown_ansi_styles
|
|
53
|
+
if json = OC::KRAMDOWN_ANSI_OLLAMA_CHAT_STYLES?
|
|
54
|
+
Kramdown::ANSI::Styles.from_json(json).ansi_styles
|
|
55
|
+
else
|
|
56
|
+
Kramdown::ANSI::Styles.new.ansi_styles
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
52
60
|
# The kramdown_markdown_remove method strips markdown formatting, emoji,
|
|
53
61
|
# ANSI escape sequences, and box-drawing characters from content, yielding
|
|
54
62
|
# plain text suitable for TTS playback.
|
|
@@ -74,4 +82,82 @@ module OllamaChat::KramdownANSI
|
|
|
74
82
|
.gsub(/\s{2,}/, ' ')
|
|
75
83
|
.strip
|
|
76
84
|
end
|
|
85
|
+
|
|
86
|
+
# Wraps the cells of a terminal table to fit within a terminal-width
|
|
87
|
+
# budget.
|
|
88
|
+
#
|
|
89
|
+
# Terminal::Table sizes each column to its widest cell, so a single long
|
|
90
|
+
# cell forces the whole table wider than the terminal. This computes a
|
|
91
|
+
# per-column budget from the widest cell in each column across all rows,
|
|
92
|
+
# then wraps each cell to it. Cells expressed as Hashes (e.g. colspan
|
|
93
|
+
# rows) are passed through untouched.
|
|
94
|
+
#
|
|
95
|
+
# @param rows [Array<Array<String, Hash>>] the data rows to wrap
|
|
96
|
+
# @param cols [Integer] the number of columns in the table
|
|
97
|
+
# @param headings [Array<String>, nil] the heading row; when given it is
|
|
98
|
+
# measured for the budget and returned first, right-padded to each
|
|
99
|
+
# column's allocated width so Terminal::Table sizes columns to the
|
|
100
|
+
# full budget even when no data cell reaches it
|
|
101
|
+
# @param percentage [Numeric] the percentage of terminal width to use for
|
|
102
|
+
# the total budget (default: 90)
|
|
103
|
+
# @return [Array<Array<String, Hash>>] wrapped rows; the padded heading
|
|
104
|
+
# row is returned first when `headings` was supplied
|
|
105
|
+
def wrap_table_rows(rows, cols, headings: nil, percentage: 90)
|
|
106
|
+
all = rows + (headings ? [headings] : [])
|
|
107
|
+
budget = table_column_budgets(all, cols, percentage:)
|
|
108
|
+
wrap_row = ->(row) {
|
|
109
|
+
row.each_with_index.map { |cell, i|
|
|
110
|
+
cell.is_a?(Hash) and next cell
|
|
111
|
+
Kramdown::ANSI::Width.wrap(cell.to_s, length: budget[i], reflow: false)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
result = rows.map(&wrap_row)
|
|
115
|
+
if headings
|
|
116
|
+
padded = headings.each_with_index.map { |h, i|
|
|
117
|
+
w = Kramdown::ANSI::Width.display_width(h)
|
|
118
|
+
w < budget[i] ? h + ' ' * (budget[i] - w) : h
|
|
119
|
+
}
|
|
120
|
+
result.unshift(padded)
|
|
121
|
+
end
|
|
122
|
+
result
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Computes a per-column display-width budget for a terminal table.
|
|
126
|
+
#
|
|
127
|
+
# @param rows [Array<Array<String, Hash>>] the rows to measure (Hash cells
|
|
128
|
+
# are ignored)
|
|
129
|
+
# @param cols [Integer] the number of columns
|
|
130
|
+
# @param percentage [Numeric] the percentage of terminal width to use for
|
|
131
|
+
# the total budget (default: 90)
|
|
132
|
+
# @return [Array<Integer>] the per-column width budget
|
|
133
|
+
def table_column_budgets(rows, cols, percentage: 90)
|
|
134
|
+
max_w = Array.new(cols, 0)
|
|
135
|
+
rows.each do |row|
|
|
136
|
+
row.each_with_index do |cell, i|
|
|
137
|
+
cell.is_a?(Hash) and next
|
|
138
|
+
w = cell.to_s.lines.map { |l| Kramdown::ANSI::Width.display_width(l) }.max || 0
|
|
139
|
+
max_w[i] = [max_w[i], w].max
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
total = max_w.sum
|
|
143
|
+
budget = Kramdown::ANSI::Width.width(percentage:) - (3 * cols + 1)
|
|
144
|
+
return Array.new(cols, (budget.to_f / cols).to_i) if total.zero?
|
|
145
|
+
if total <= budget
|
|
146
|
+
extra = budget - total
|
|
147
|
+
max_w.map { |w| w + ((w.to_f / total) * extra).floor }
|
|
148
|
+
else
|
|
149
|
+
share = (budget.to_f / cols).to_i
|
|
150
|
+
alloc = max_w.map { |w| [w, share].min }
|
|
151
|
+
unused = budget - alloc.sum
|
|
152
|
+
return alloc if unused.zero?
|
|
153
|
+
capped = (0...cols).to_a.select { |i| alloc[i] < max_w[i] }
|
|
154
|
+
need = capped.sum { |i| max_w[i] - alloc[i] }
|
|
155
|
+
return alloc if need.zero?
|
|
156
|
+
capped.each do |i|
|
|
157
|
+
extra = ((max_w[i] - alloc[i]).to_f / need * unused).floor
|
|
158
|
+
alloc[i] = [max_w[i], alloc[i] + extra].min
|
|
159
|
+
end
|
|
160
|
+
alloc
|
|
161
|
+
end
|
|
162
|
+
end
|
|
77
163
|
end
|
|
@@ -465,7 +465,7 @@ module OllamaChat::ModelHandling
|
|
|
465
465
|
if model_metadata = model_present?(model, ollama:)
|
|
466
466
|
return model_metadata
|
|
467
467
|
end
|
|
468
|
-
raise OllamaChat::UnknownModelError, "unknown model named #{
|
|
468
|
+
raise OllamaChat::UnknownModelError, "unknown model named #{model.inspect}"
|
|
469
469
|
end
|
|
470
470
|
end
|
|
471
471
|
|
|
@@ -587,17 +587,25 @@ module OllamaChat::ModelHandling
|
|
|
587
587
|
old_model = @model
|
|
588
588
|
|
|
589
589
|
if model.blank?
|
|
590
|
-
|
|
590
|
+
new_model = choose_model('', old_model)
|
|
591
591
|
else
|
|
592
|
-
|
|
592
|
+
new_model = choose_model(model, config.model.name)
|
|
593
593
|
end
|
|
594
594
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
595
|
+
unless model_metadata = model_present?(new_model, ollama:)
|
|
596
|
+
feedback("Model #{bold{new_model}} not found locally.", type: :warn)
|
|
597
|
+
unless confirm?(prompt: "Pull it from remote? (y/n) ", yes: /\Ay/i)
|
|
598
|
+
feedback("Cancelled.", type: :cancel)
|
|
599
|
+
old_model.full? && model_present?(old_model, ollama:) or
|
|
600
|
+
raise OllamaChat::UnknownModelError, "no usable model after cancel"
|
|
601
|
+
return @model_metadata
|
|
602
|
+
end
|
|
603
|
+
model_metadata = pull_model_unless_present(new_model, ollama:)
|
|
599
604
|
end
|
|
600
605
|
|
|
606
|
+
@model, @model_metadata = new_model, model_metadata
|
|
607
|
+
session.update(current_model: @model)
|
|
608
|
+
|
|
601
609
|
if old_model != @model
|
|
602
610
|
reconfigure_model_options(profile:, keep_options:)
|
|
603
611
|
if @model_metadata&.can?('thinking')
|
|
@@ -122,7 +122,7 @@ module OllamaChat::RAGHandling
|
|
|
122
122
|
selected.merge(remaining)
|
|
123
123
|
break
|
|
124
124
|
else
|
|
125
|
-
selected
|
|
125
|
+
selected << choice
|
|
126
126
|
end
|
|
127
127
|
end
|
|
128
128
|
end
|
|
@@ -202,7 +202,7 @@ module OllamaChat::RAGHandling
|
|
|
202
202
|
selected.merge(remaining)
|
|
203
203
|
break
|
|
204
204
|
else
|
|
205
|
-
selected
|
|
205
|
+
selected << choice
|
|
206
206
|
end
|
|
207
207
|
end
|
|
208
208
|
end
|
|
@@ -213,14 +213,25 @@ module OllamaChat::RAGHandling
|
|
|
213
213
|
|
|
214
214
|
feedback("Restoring #{selected.size} collection(s): #{selected.join(', ')}")
|
|
215
215
|
|
|
216
|
-
total
|
|
216
|
+
total = 0
|
|
217
|
+
skipped = 0
|
|
217
218
|
collections.slice(*selected).sort.each do |col, group|
|
|
218
219
|
create_memory_collection(col)
|
|
219
220
|
switch_collection(col) do
|
|
221
|
+
existing_tags = Set.new
|
|
222
|
+
@documents.each_record do |r|
|
|
223
|
+
r.tags.each { |t| existing_tags << t.to_s(link: false) }
|
|
224
|
+
end
|
|
225
|
+
|
|
220
226
|
group.each do |entry|
|
|
227
|
+
tags = Array(entry['tags'])
|
|
228
|
+
if tags.any? { |t| existing_tags.include?(t) }
|
|
229
|
+
skipped += 1
|
|
230
|
+
next
|
|
231
|
+
end
|
|
221
232
|
@documents.add(
|
|
222
233
|
[entry['text']],
|
|
223
|
-
tags
|
|
234
|
+
tags:,
|
|
224
235
|
batch_size: 1,
|
|
225
236
|
source: entry['source']
|
|
226
237
|
)
|
|
@@ -230,9 +241,10 @@ module OllamaChat::RAGHandling
|
|
|
230
241
|
end
|
|
231
242
|
|
|
232
243
|
log(:info, 'Memory restored',
|
|
233
|
-
data: { file: filename.to_s, collections: selected, records: total })
|
|
244
|
+
data: { file: filename.to_s, collections: selected, records: total, skipped: })
|
|
234
245
|
feedback(
|
|
235
|
-
"Restored #{total} record(s) across #{selected.size} collection(s) from #{filename.to_s.inspect}."
|
|
246
|
+
"Restored #{total} record(s) across #{selected.size} collection(s) from #{filename.to_s.inspect}." +
|
|
247
|
+
(skipped.zero? ? '' : " (#{skipped} duplicate(s) skipped)."),
|
|
236
248
|
type: :success
|
|
237
249
|
)
|
|
238
250
|
end
|
|
@@ -374,7 +386,7 @@ module OllamaChat::RAGHandling
|
|
|
374
386
|
source_tags = Hash.new { |h, k| h[k] = Set.new }
|
|
375
387
|
@documents.records.each do |record|
|
|
376
388
|
source = record.source.full? or next
|
|
377
|
-
sources
|
|
389
|
+
sources << source
|
|
378
390
|
source_tags[source].merge(record.tags)
|
|
379
391
|
end
|
|
380
392
|
display_map = {}
|
|
@@ -599,12 +611,12 @@ module OllamaChat::RAGHandling
|
|
|
599
611
|
end
|
|
600
612
|
|
|
601
613
|
sources = {}
|
|
602
|
-
seen
|
|
614
|
+
seen = Set.new
|
|
603
615
|
@documents.each_record do |record|
|
|
604
616
|
source = @documents.normalize_source(record.source) or next
|
|
605
617
|
if matched_sources.nil? || matched_sources.member?(source)
|
|
606
|
-
seen.
|
|
607
|
-
seen
|
|
618
|
+
seen.member?(source) and next
|
|
619
|
+
seen << source
|
|
608
620
|
unless @documents.source_modified?(source)
|
|
609
621
|
infobar.puts "Source #{source.to_s.inspect} is unmodified. => Skipping."
|
|
610
622
|
next
|
|
@@ -615,8 +627,8 @@ module OllamaChat::RAGHandling
|
|
|
615
627
|
end
|
|
616
628
|
|
|
617
629
|
if patterns = col.patterns.full?
|
|
618
|
-
new_sources = all_file_set(patterns).map(&:to_s).reject { seen.
|
|
619
|
-
new_sources.each { seen
|
|
630
|
+
new_sources = all_file_set(patterns).map(&:to_s).reject { seen.member?(_1) }.to_set
|
|
631
|
+
new_sources.each { seen << _1 }
|
|
620
632
|
new_sources.each { sources[_1] = [] }
|
|
621
633
|
end
|
|
622
634
|
|
|
@@ -107,22 +107,23 @@ module OllamaChat::SessionManagement
|
|
|
107
107
|
all_separators: true,
|
|
108
108
|
border: :unicode_round,
|
|
109
109
|
}
|
|
110
|
-
|
|
111
|
-
now
|
|
110
|
+
headings = %w[ ID NAME SIZE #TOK COUNT UPDATED ].map { |h| bold { h } }
|
|
111
|
+
now = Time.now
|
|
112
|
+
rows = []
|
|
112
113
|
models::Session.order(Sequel.desc(:updated_at)).each do |s|
|
|
113
|
-
name
|
|
114
|
-
name
|
|
115
|
-
name
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
es
|
|
125
|
-
|
|
114
|
+
name = s.name
|
|
115
|
+
name = session.id == s.id ? bold { name } : name
|
|
116
|
+
name = if pid = s.locked?
|
|
117
|
+
if pid == $$
|
|
118
|
+
"#{name} š"
|
|
119
|
+
else
|
|
120
|
+
"#{name} š"
|
|
121
|
+
end
|
|
122
|
+
else
|
|
123
|
+
name
|
|
124
|
+
end
|
|
125
|
+
es = s.estimate_tokens
|
|
126
|
+
rows << [
|
|
126
127
|
s.id.to_s,
|
|
127
128
|
name,
|
|
128
129
|
es.bytes_formatted,
|
|
@@ -131,6 +132,7 @@ module OllamaChat::SessionManagement
|
|
|
131
132
|
s.age(now:),
|
|
132
133
|
]
|
|
133
134
|
end
|
|
135
|
+
wrap_table_rows(rows, 6, headings:).each { |row| table << row }
|
|
134
136
|
table.align_column 0, :right
|
|
135
137
|
table.align_column 2, :left
|
|
136
138
|
table.align_column 3, :right
|
|
@@ -33,6 +33,7 @@ module OllamaChat::SourceFetching
|
|
|
33
33
|
#
|
|
34
34
|
# @yield [ tmp ]
|
|
35
35
|
def fetch_source(source, check_exist: false, &block)
|
|
36
|
+
confirm_notification = false
|
|
36
37
|
source = source.ask_and_send_or_self(:to_path).to_s
|
|
37
38
|
case source
|
|
38
39
|
when %r{\A!(.*)}
|
|
@@ -72,14 +73,28 @@ module OllamaChat::SourceFetching
|
|
|
72
73
|
else
|
|
73
74
|
raise "invalid source #{source.inspect}"
|
|
74
75
|
end
|
|
76
|
+
rescue OllamaChat::HTTPError => e
|
|
77
|
+
msg = "Fetching source #{source.to_s.inspect}: #{e.class} #{e}"
|
|
78
|
+
feedback(msg, type: :warn)
|
|
79
|
+
log(:warn, msg, data: { source: source.to_s, status: e.status })
|
|
80
|
+
confirm_notification = true
|
|
81
|
+
msg = OllamaChat::Utils::Fetcher::ResponseMetadata.failed(msg)
|
|
82
|
+
msg.response_status = e.status
|
|
83
|
+
msg.response_reason = e.message
|
|
84
|
+
block.(msg)
|
|
85
|
+
msg
|
|
75
86
|
rescue => e
|
|
76
87
|
msg = "Fetching source #{source.to_s.inspect}: #{e.class} #{e}"
|
|
77
88
|
feedback("#{msg}\n#{e.backtrace * ?\n}", type: :warn)
|
|
78
89
|
log(:error, msg, data: { source: source.to_s, error: e.class })
|
|
79
|
-
|
|
90
|
+
confirm_notification = true
|
|
80
91
|
msg = OllamaChat::Utils::Fetcher::ResponseMetadata.failed(msg)
|
|
81
92
|
block.(msg)
|
|
82
93
|
msg
|
|
94
|
+
ensure
|
|
95
|
+
if confirm_notification
|
|
96
|
+
confirm?(prompt: "\nā Press any key to continue (%s). ", output: STDERR, timeout: 3)
|
|
97
|
+
end
|
|
83
98
|
end
|
|
84
99
|
|
|
85
100
|
# Adds an image to the images collection from the given source IO and source
|
|
@@ -60,10 +60,10 @@ class OllamaChat::Tools::Browse
|
|
|
60
60
|
result = browse_url(url)
|
|
61
61
|
|
|
62
62
|
message = if result.success?
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
"Opened #{redact_password(url).inspect} in browser."
|
|
64
|
+
else
|
|
65
|
+
"Could not open #{redact_password(url).inspect} in browser."
|
|
66
|
+
end
|
|
67
67
|
|
|
68
68
|
{
|
|
69
69
|
success: result.success?,
|