ollama_chat 0.0.138 → 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 +51 -0
- data/Rakefile +1 -1
- data/lib/ollama_chat/command_concern.rb +7 -4
- data/lib/ollama_chat/compaction.rb +1 -1
- data/lib/ollama_chat/database/models/collection.rb +1 -1
- data/lib/ollama_chat/kramdown_ansi.rb +107 -21
- data/lib/ollama_chat/session_management.rb +17 -15
- data/lib/ollama_chat/source_fetching.rb +16 -1
- data/lib/ollama_chat/tools/get_url.rb +5 -0
- data/lib/ollama_chat/version.rb +1 -1
- data/ollama_chat.gemspec +3 -3
- data/spec/ollama_chat/compaction_spec.rb +1 -1
- data/spec/ollama_chat/kramdown_ansi_spec.rb +54 -2
- data/spec/ollama_chat/tools/get_url_spec.rb +24 -5
- metadata +3 -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,56 @@
|
|
|
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
|
+
|
|
3
54
|
## 2026-10-28 v0.0.138
|
|
4
55
|
|
|
5
56
|
### Features
|
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'
|
|
@@ -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(
|
|
@@ -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
|
|
@@ -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
|
|
@@ -116,6 +116,11 @@ class OllamaChat::Tools::GetURL
|
|
|
116
116
|
|
|
117
117
|
source, message, content = url, nil, ''
|
|
118
118
|
chat.fetch_source(source, check_exist: false) do |source_io|
|
|
119
|
+
if (status = source_io&.response_status) && status != 200
|
|
120
|
+
content = source_io.read
|
|
121
|
+
message = "Received HTTP status #{status} from #{source}."
|
|
122
|
+
next
|
|
123
|
+
end
|
|
119
124
|
if source_io&.content_type&.to_s&.match?(OllamaChat::Parsing::HAS_AUDIO)
|
|
120
125
|
text = chat.parse_audio(source_io, language:)
|
|
121
126
|
source_io = OllamaChat::Utils::Fetcher::ResponseMetadata.as_text(text)
|
data/lib/ollama_chat/version.rb
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# by hand; it is rewritten on every `rake version` invocation.
|
|
6
6
|
module OllamaChat
|
|
7
7
|
# The current version of the OllamaChat gem as a String.
|
|
8
|
-
VERSION = '0.0.
|
|
8
|
+
VERSION = '0.0.139'
|
|
9
9
|
VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
|
|
10
10
|
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
|
|
11
11
|
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
|
data/ollama_chat.gemspec
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: ollama_chat 0.0.
|
|
2
|
+
# stub: ollama_chat 0.0.139 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "ollama_chat".freeze
|
|
6
|
-
s.version = "0.0.
|
|
6
|
+
s.version = "0.0.139".freeze
|
|
7
7
|
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
|
9
9
|
s.require_paths = ["lib".freeze]
|
|
@@ -41,7 +41,7 @@ Gem::Specification.new do |s|
|
|
|
41
41
|
s.add_runtime_dependency(%q<redis>.freeze, ["~> 5.0".freeze])
|
|
42
42
|
s.add_runtime_dependency(%q<mime-types>.freeze, ["~> 3.0".freeze])
|
|
43
43
|
s.add_runtime_dependency(%q<reverse_markdown>.freeze, ["~> 3.0".freeze])
|
|
44
|
-
s.add_runtime_dependency(%q<kramdown-ansi>.freeze, ["~> 0.
|
|
44
|
+
s.add_runtime_dependency(%q<kramdown-ansi>.freeze, ["~> 0.9".freeze])
|
|
45
45
|
s.add_runtime_dependency(%q<complex_config>.freeze, ["~> 0.22".freeze, ">= 0.22.2".freeze])
|
|
46
46
|
s.add_runtime_dependency(%q<tins>.freeze, ["~> 1.61".freeze])
|
|
47
47
|
s.add_runtime_dependency(%q<search_ui>.freeze, [">= 0.2".freeze])
|
|
@@ -351,7 +351,7 @@ describe OllamaChat::Compaction do
|
|
|
351
351
|
.with(a_string_matching(/Summarized:\s+12 messages/), type: :success)
|
|
352
352
|
expect(chat).to receive(:speak)
|
|
353
353
|
.with(a_string_matching(
|
|
354
|
-
/Compressed conversation context from \d+ to \d+ in \d+ (?:seconds|minutes and \d+ seconds)\./
|
|
354
|
+
/Compressed conversation context from \d+ percent to \d+ percent in \d+ (?:seconds|minutes and \d+ seconds)\./
|
|
355
355
|
))
|
|
356
356
|
|
|
357
357
|
expect(chat.compact_with_retry).to be true
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
describe OllamaChat::KramdownANSI do
|
|
2
|
+
let :argv do
|
|
3
|
+
chat_default_config
|
|
4
|
+
end
|
|
5
|
+
|
|
2
6
|
let :chat do
|
|
3
|
-
|
|
7
|
+
OllamaChat::Chat.new(argv:).expose
|
|
4
8
|
end
|
|
5
9
|
|
|
10
|
+
connect_to_ollama_server
|
|
11
|
+
|
|
6
12
|
describe '#configure_kramdown_ansi_styles', protect_env: true do
|
|
7
13
|
it 'can be configured via env var' do
|
|
8
14
|
const_conf_as(
|
|
@@ -12,7 +18,6 @@ describe OllamaChat::KramdownANSI do
|
|
|
12
18
|
expect(Kramdown::ANSI::Styles).to receive(:from_json).
|
|
13
19
|
with('{"foo":"bar"}').
|
|
14
20
|
and_return(double(ansi_styles: styles))
|
|
15
|
-
|
|
16
21
|
expect(chat.configure_kramdown_ansi_styles).to eq(styles)
|
|
17
22
|
end
|
|
18
23
|
|
|
@@ -100,4 +105,51 @@ describe OllamaChat::KramdownANSI do
|
|
|
100
105
|
expect(result).to include('**bold**')
|
|
101
106
|
end
|
|
102
107
|
end
|
|
108
|
+
|
|
109
|
+
describe '#wrap_table_rows' do
|
|
110
|
+
it 'wraps long cells to fit within terminal budget' do
|
|
111
|
+
long_text = 'word ' * 50
|
|
112
|
+
rows = [[ long_text, 'short' ]]
|
|
113
|
+
result = chat.wrap_table_rows(rows, 2)
|
|
114
|
+
expect(result[0][0]).to include("\n")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'passes Hash cells (colspan) through untouched' do
|
|
118
|
+
rows = [[ { value: 'category', colspan: 4 } ]]
|
|
119
|
+
result = chat.wrap_table_rows(rows, 4)
|
|
120
|
+
expect(result).to eq(rows)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'returns wrapped headings first when supplied' do
|
|
124
|
+
rows = [[ 'a', 'b' ]]
|
|
125
|
+
result = chat.wrap_table_rows(rows, 2, headings: ['H1', 'H2'])
|
|
126
|
+
expect(result.first.map(&:strip)).to eq(['H1', 'H2'])
|
|
127
|
+
expect(result.size).to eq(2)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'handles nil cells by converting to empty string' do
|
|
131
|
+
result = chat.wrap_table_rows([[ nil, 'text' ]], 2)
|
|
132
|
+
expect(result[0][0]).to eq('')
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'preserves short content without wrapping' do
|
|
136
|
+
rows = [[ 'hi', 'there' ]]
|
|
137
|
+
result = chat.wrap_table_rows(rows, 2)
|
|
138
|
+
expect(result[0]).to eq(['hi', 'there'])
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
describe '#table_column_budgets' do
|
|
143
|
+
it 'returns equal budgets when total width is zero' do
|
|
144
|
+
result = chat.table_column_budgets([[ '', '' ]], 2)
|
|
145
|
+
expect(result[0]).to eq(result[1])
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'allocates proportionally based on content width' do
|
|
149
|
+
narrow = 'a'
|
|
150
|
+
wide = 'a' * 20
|
|
151
|
+
result = chat.table_column_budgets([[ narrow, wide ]], 2)
|
|
152
|
+
expect(result[1]).to be > result[0]
|
|
153
|
+
end
|
|
154
|
+
end
|
|
103
155
|
end
|
|
@@ -109,7 +109,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
109
109
|
|
|
110
110
|
context 'with different document policies' do
|
|
111
111
|
let(:url) { 'https://www.example.com/foo' }
|
|
112
|
-
let(:source_io) { double('SourceIO', content_type: double(media_type: 'text'), read: 'raw content') }
|
|
112
|
+
let(:source_io) { double('SourceIO', content_type: double(media_type: 'text'), read: 'raw content', response_status: nil) }
|
|
113
113
|
|
|
114
114
|
before do
|
|
115
115
|
expect(chat).to receive(:fetch_source).and_yield(source_io)
|
|
@@ -191,7 +191,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
191
191
|
let(:url) { 'https://www.example.com/foo' }
|
|
192
192
|
|
|
193
193
|
it 'handles image content types' do
|
|
194
|
-
source_io = double('SourceIO', content_type: double(media_type: 'image', sub_type: 'jpeg'))
|
|
194
|
+
source_io = double('SourceIO', content_type: double(media_type: 'image', sub_type: 'jpeg'), response_status: nil)
|
|
195
195
|
expect(chat).to receive(:fetch_source).and_yield(source_io)
|
|
196
196
|
expect(chat).to receive(:add_image).with(chat.images, source_io, URI.parse(url))
|
|
197
197
|
|
|
@@ -222,7 +222,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
222
222
|
end
|
|
223
223
|
|
|
224
224
|
it 'handles audio content types via ASR transcription' do
|
|
225
|
-
source_io = double('SourceIO', content_type: double(media_type: 'audio', to_s: 'audio/mp3'))
|
|
225
|
+
source_io = double('SourceIO', content_type: double(media_type: 'audio', to_s: 'audio/mp3'), response_status: nil)
|
|
226
226
|
expect(chat).to receive(:fetch_source).and_yield(source_io)
|
|
227
227
|
expect(chat).to receive(:parse_audio).with(source_io, language: 'de').and_return('transcribed text')
|
|
228
228
|
|
|
@@ -235,7 +235,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
235
235
|
end
|
|
236
236
|
|
|
237
237
|
it 'handles video content types via ASR transcription' do
|
|
238
|
-
source_io = double('SourceIO', content_type: double(media_type: 'video', to_s: 'video/mp4'))
|
|
238
|
+
source_io = double('SourceIO', content_type: double(media_type: 'video', to_s: 'video/mp4'), response_status: nil)
|
|
239
239
|
expect(chat).to receive(:fetch_source).and_yield(source_io)
|
|
240
240
|
expect(chat).to receive(:parse_audio).with(source_io, language: nil).and_return('video text')
|
|
241
241
|
|
|
@@ -248,7 +248,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
248
248
|
end
|
|
249
249
|
|
|
250
250
|
it 'handles unsupported media types' do
|
|
251
|
-
source_io = double('SourceIO', content_type: double(media_type: 'font'))
|
|
251
|
+
source_io = double('SourceIO', content_type: double(media_type: 'font'), response_status: nil)
|
|
252
252
|
expect(chat).to receive(:fetch_source).and_yield(source_io)
|
|
253
253
|
|
|
254
254
|
args = double(url:, document_policy: 'ignoring', words: nil, instruction: nil, language: nil)
|
|
@@ -258,5 +258,24 @@ describe OllamaChat::Tools::GetURL do
|
|
|
258
258
|
json = json_object(result)
|
|
259
259
|
expect(json.message).to match(/Cannot fetch.*with content type/)
|
|
260
260
|
end
|
|
261
|
+
|
|
262
|
+
it 'short-circuits on non-200 HTTP status' do
|
|
263
|
+
source_io = double(
|
|
264
|
+
'FailedSourceIO',
|
|
265
|
+
response_status: 403,
|
|
266
|
+
read: 'Access Denied',
|
|
267
|
+
)
|
|
268
|
+
expect(chat).to receive(:fetch_source).and_yield(source_io)
|
|
269
|
+
expect(chat).not_to receive(:summarize_source)
|
|
270
|
+
expect(chat).not_to receive(:import_source)
|
|
271
|
+
|
|
272
|
+
args = double(url:, document_policy: 'summarizing', words: nil, instruction: nil, language: nil)
|
|
273
|
+
tool_call = double(function: double(arguments: args))
|
|
274
|
+
|
|
275
|
+
result = described_class.new(chat).execute(tool_call)
|
|
276
|
+
json = json_object(result)
|
|
277
|
+
expect(json.content).to eq('Access Denied')
|
|
278
|
+
expect(json.message).to match(/HTTP status 403/)
|
|
279
|
+
end
|
|
261
280
|
end
|
|
262
281
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ollama_chat
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.139
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Florian Frank
|
|
@@ -253,14 +253,14 @@ dependencies:
|
|
|
253
253
|
requirements:
|
|
254
254
|
- - "~>"
|
|
255
255
|
- !ruby/object:Gem::Version
|
|
256
|
-
version: '0.
|
|
256
|
+
version: '0.9'
|
|
257
257
|
type: :runtime
|
|
258
258
|
prerelease: false
|
|
259
259
|
version_requirements: !ruby/object:Gem::Requirement
|
|
260
260
|
requirements:
|
|
261
261
|
- - "~>"
|
|
262
262
|
- !ruby/object:Gem::Version
|
|
263
|
-
version: '0.
|
|
263
|
+
version: '0.9'
|
|
264
264
|
- !ruby/object:Gem::Dependency
|
|
265
265
|
name: complex_config
|
|
266
266
|
requirement: !ruby/object:Gem::Requirement
|