ollama_chat 0.0.106 → 0.0.108
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 +105 -0
- data/README.md +16 -0
- data/Rakefile +1 -1
- data/bin/ollama_chat_send +12 -2
- data/lib/ollama_chat/chat.rb +22 -4
- data/lib/ollama_chat/commands.rb +5 -3
- data/lib/ollama_chat/database/migrations/008_drop_think_mode_constraint.rb +130 -0
- data/lib/ollama_chat/dialog.rb +24 -17
- data/lib/ollama_chat/file_editing.rb +15 -13
- data/lib/ollama_chat/follow_chat.rb +39 -16
- data/lib/ollama_chat/model_handling.rb +3 -1
- data/lib/ollama_chat/ollama_chat_config/default_config.yml +18 -6
- data/lib/ollama_chat/personae_management.rb +22 -18
- data/lib/ollama_chat/prompt_management.rb +9 -12
- data/lib/ollama_chat/rag_handling.rb +27 -23
- data/lib/ollama_chat/say.rb +30 -0
- data/lib/ollama_chat/session_management.rb +5 -3
- data/lib/ollama_chat/state_selectors.rb +2 -1
- data/lib/ollama_chat/think_control.rb +4 -1
- data/lib/ollama_chat/tools/patch_file.rb +5 -1
- data/lib/ollama_chat/tools/search_knowledge.rb +3 -0
- data/lib/ollama_chat/tools/write_file.rb +16 -0
- data/lib/ollama_chat/version.rb +1 -1
- data/lib/ollama_chat/web_searching.rb +6 -4
- data/lib/ollama_chat.rb +10 -0
- data/ollama_chat.gemspec +4 -4
- data/spec/ollama_chat/chat_spec.rb +24 -0
- data/spec/ollama_chat/commands_spec.rb +7 -7
- data/spec/ollama_chat/follow_chat_spec.rb +1 -2
- data/spec/ollama_chat/session_management_spec.rb +1 -1
- data/spec/ollama_chat/tools/search_knowledge_spec.rb +23 -0
- data/spec/ollama_chat/tools/write_file_spec.rb +104 -2
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 857b8cf92f68e5856238c1758cfebe463535ded52de3e4dc591cd83b501ccd63
|
|
4
|
+
data.tar.gz: fc90ce1e2145207dc49dfeb6512804dec0e0e3933c943ec0050221b5867bccd1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 173caf67db6f004400f1f5199e07fcd16acd36c44a531a7d8b5618e5c2fb03f994aa63d03c5e4140d2d443022afd7fccc29d6c17d63f76e2d7085f41b9dd97e9
|
|
7
|
+
data.tar.gz: 5addb13b768f52b9ef3f5e3bd532aa632a37e5b5bf7b3c69c64be2d063b0f74ddc41640dd9e19a87d27e570c581e24f5456cf7b3a4d7c1351eba85e6d152e10b
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,110 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-08-15 v0.0.108
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added `require 'io/console'` to support `IO#raw` for key reads.
|
|
8
|
+
- Added `max` to `THINK_MODE_STATES` in `think_control.rb` to support newer
|
|
9
|
+
Ollama releases (e.g., **0.32.13**).
|
|
10
|
+
- Added migration `008_drop_think_mode_constraint.rb` to drop the `think_mode`
|
|
11
|
+
`CHECK` constraint in SQLite.
|
|
12
|
+
- Added `.obsidian` to `.gitignore` and the `GemHadar` `ignore` list in
|
|
13
|
+
`Rakefile`.
|
|
14
|
+
- Added `require_confirmation: true` to `get_current_weather`, `get_cve`,
|
|
15
|
+
`get_endoflife`, `browse`, `get_jira_issue`, `get_rfc`, and `get_ghr`.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Refactored `confirm?` key reads to use `STDIN.raw(min:, time:, intr: true)`
|
|
20
|
+
instead of `stty` commands.
|
|
21
|
+
- Updated `confirm?` to print prompts to `output` instead of `STDOUT`.
|
|
22
|
+
- Updated glyphs for confirmation: `\u2328\uFE0F` to `\u2328`, `☑️` to `\u2611`,
|
|
23
|
+
and `🚫` to `\u{1F6AB}`.
|
|
24
|
+
- Scoped `Reline` histories for various interactive prompts:
|
|
25
|
+
- `duplicate_session` name prompt now scoped to `:session_name`.
|
|
26
|
+
- `determine_valid_new_name_for_persona` now scoped to `:persona_name`.
|
|
27
|
+
- `add_persona` `[FILES]` patterns prompt now scoped to `:patterns`.
|
|
28
|
+
- `add_new_prompt` `[FILES]` patterns prompt now scoped to `:patterns`.
|
|
29
|
+
- `determine_valid_output_filename` loop now scoped to `:filename`.
|
|
30
|
+
- `/session summarize -f` filename prompt now scoped to `:filename`.
|
|
31
|
+
- `choose_profile_for_model` profile name prompt now scoped to
|
|
32
|
+
`:profile_name`.
|
|
33
|
+
- Scoped `Reline` histories for RAG prompts:
|
|
34
|
+
- `rename_collection` now scopes to `:collection_name`.
|
|
35
|
+
- `create_collection` scopes to `:collection_name`,
|
|
36
|
+
`:collection_description`, and `:patterns`.
|
|
37
|
+
- `edit_collection` description prompt now uses `:collection_description`.
|
|
38
|
+
- Refactored terminal truncation by moving `truncate_for_terminal` from
|
|
39
|
+
`prepare_last_message` to `last_message_with_user`.
|
|
40
|
+
- Added `height:` keyword argument to `last_message_with_user` and
|
|
41
|
+
`display_formatted_terminal_output`.
|
|
42
|
+
- Refactored `display_formatted_terminal_output` to use `move_home` and
|
|
43
|
+
`erase_in_display(nil)` instead of `clear_screen` to prevent `tmux` flicker.
|
|
44
|
+
- Updated `require_confirmation` to `false` for `open_file_in_editor`,
|
|
45
|
+
`paste_into_editor`, and `generate_image`.
|
|
46
|
+
- Removed `jq` parsing from `OLLAMA_CHAT_TOOLS_TEST_RUNNER` in `.envrc`.
|
|
47
|
+
- Introduced `OLLAMA_CHAT_COLLECTION_NAME_REGEXP` in `lib/ollama_chat.rb` to
|
|
48
|
+
enforce safe identifiers.
|
|
49
|
+
- Updated `OLLAMA_CHAT::Chat#initial_collection` to validate names against the
|
|
50
|
+
new regex.
|
|
51
|
+
- Wrapped web search link list output in `use_pager` within
|
|
52
|
+
`OLLAMA_CHAT::WebSearching`.
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
- Added `rescue` for `Interrupt` and `IRB::Abort` during key reads to return
|
|
56
|
+
`nil` and print `"\u274C\uFE0F"`.
|
|
57
|
+
- Fixed `tmux` flicker in vertically split panes by avoiding full-pane redraws.
|
|
58
|
+
- Fixed `OLLAMA_CHAT::Tools::PatchFile` to explicitly warn against redundant
|
|
59
|
+
tool calls.
|
|
60
|
+
|
|
61
|
+
### Refactored
|
|
62
|
+
- Updated YARD `@param output` and `@return` documentation.
|
|
63
|
+
- Simplified `last_message_with_user` to return a single joined string.
|
|
64
|
+
- Simplified `OLLAMA_CHAT::Tools::SearchKnowledge` to raise
|
|
65
|
+
`OLLAMA_CHAT::ToolFunctionArgumentError` on invalid input.
|
|
66
|
+
|
|
67
|
+
## 2026-08-09 v0.0.107
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
|
|
71
|
+
- New `-e` option to `ollama_chat_send` to open input content in the configured
|
|
72
|
+
editor before sending.
|
|
73
|
+
- New `-F` option to `ollama_chat_send` to specify file extension for syntax
|
|
74
|
+
highlighting in the editor.
|
|
75
|
+
- Confirmation prompts to `write_file` when overwriting existing files in
|
|
76
|
+
`overwrite` mode or creating new files in `append` mode.
|
|
77
|
+
- Support for pluggable voice handler architecture via a new `handler` key in
|
|
78
|
+
the `voice` configuration.
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
|
|
82
|
+
- Refactored voice handling to use a dynamic `voice_handler` method in
|
|
83
|
+
`OllamaChat::Chat` to resolve configured classes (e.g., `OllamaChat::Say`).
|
|
84
|
+
- Externalized the suggestion prompt template by adding
|
|
85
|
+
`context_template_suggest` to `default_config.yml` under `prompts`, replacing
|
|
86
|
+
hardcoded heredocs in `prompt_management.rb`.
|
|
87
|
+
- Updated `StateSelectors` to delegate voice list retrieval via
|
|
88
|
+
`voice_handler.ask_and_send(:voices)`.
|
|
89
|
+
- Updated `FollowChat` to instantiate the resolved voice handler dynamically.
|
|
90
|
+
- Refactored `OllamaChat::Say.voices` to extract logic from
|
|
91
|
+
`default_config.yml` ERB and remove RSpec-specific branching.
|
|
92
|
+
- Updated `spec/ollama_chat/commands_spec.rb` to use explicit `.jsonl`
|
|
93
|
+
extensions for `/conversation save` and `/conversation load` tests.
|
|
94
|
+
|
|
95
|
+
### Fixed
|
|
96
|
+
|
|
97
|
+
- Reopened `STDIN` from `/dev/tty` during editing to prevent editor warnings.
|
|
98
|
+
- Ensured `save_conversation` and `load_conversation` expectations match the
|
|
99
|
+
new file naming convention.
|
|
100
|
+
|
|
101
|
+
### Security & Error Handling
|
|
102
|
+
|
|
103
|
+
- Raised `OllamaChat::ToolFunctionArgumentError` when a user declines a file
|
|
104
|
+
operation confirmation.
|
|
105
|
+
- Added `.to_s` call on `prompt` results to correctly handle `Sequel::Model`
|
|
106
|
+
return types.
|
|
107
|
+
|
|
3
108
|
## 2026-08-06 v0.0.106
|
|
4
109
|
|
|
5
110
|
### Added
|
data/README.md
CHANGED
|
@@ -344,6 +344,22 @@ functionality:
|
|
|
344
344
|
$ echo "Hello world" | ollama_chat_send -d /tmp/my_working_dir -r
|
|
345
345
|
```
|
|
346
346
|
|
|
347
|
+
- **Edit Content (`-e`)**: Opens the input content in the configured editor
|
|
348
|
+
before sending. This is useful for refining the message or adding more
|
|
349
|
+
context.
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
$ echo "Draft message..." | ollama_chat_send -e
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
- **File Format (`-F`)**: When using edit mode, specify a file extension to
|
|
356
|
+
enable syntax highlighting in your editor (e.g., `rb` for Ruby, `md` for
|
|
357
|
+
Markdown).
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
$ echo "def hello; end" | ollama_chat_send -e -F rb
|
|
361
|
+
```
|
|
362
|
+
|
|
347
363
|
- **Help (`-h` or `--help`)**: Displays usage information and available options.
|
|
348
364
|
|
|
349
365
|
```bash
|
data/Rakefile
CHANGED
|
@@ -20,7 +20,7 @@ GemHadar do
|
|
|
20
20
|
test_dir 'spec'
|
|
21
21
|
ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', '.AppleDouble', '.bundle',
|
|
22
22
|
'.yardoc', 'doc', 'tags', 'corpus', 'coverage', '/config/searxng/*',
|
|
23
|
-
'.starscope.db', 'cscope.out', 'errors.lst', 'tmp/*'
|
|
23
|
+
'.starscope.db', 'cscope.out', 'errors.lst', 'tmp/*', '.obsidian'
|
|
24
24
|
package_ignore '.all_images.yml', '.tool-versions', '.gitignore', 'VERSION',
|
|
25
25
|
'.rspec', '.github', '.contexts', '.envrc', '.yardopts'
|
|
26
26
|
|
data/bin/ollama_chat_send
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
3
|
require 'ollama_chat'
|
|
4
|
+
include OllamaChat::FileEditing
|
|
4
5
|
require 'tins/go'
|
|
5
6
|
include Tins::GO
|
|
6
7
|
|
|
7
|
-
opts = go 'f:d:
|
|
8
|
+
opts = go 'f:d:F:rteph', ARGV
|
|
8
9
|
|
|
9
10
|
# Displays the usage information for the ollama_chat_send command.
|
|
10
11
|
#
|
|
@@ -23,6 +24,8 @@ def usage(rc = 0)
|
|
|
23
24
|
-p Send input with source parsing enabled (defaults to disabled)
|
|
24
25
|
-f CONFIG file to read
|
|
25
26
|
-d DIR the working directory to derive the socket file from
|
|
27
|
+
-e Edit content in editor before sending
|
|
28
|
+
-F FORMAT file extension for editor (e.g., rb, md)
|
|
26
29
|
-h Show this help message
|
|
27
30
|
|
|
28
31
|
Send data to a running Ollama Chat client via standard input.
|
|
@@ -38,8 +41,15 @@ begin
|
|
|
38
41
|
else
|
|
39
42
|
opts[?r] ? :socket_input_with_response : :socket_input
|
|
40
43
|
end
|
|
44
|
+
content = STDIN.read
|
|
45
|
+
if opts[?e]
|
|
46
|
+
basename = %w[ text ]
|
|
47
|
+
basename << opts[?F]&.sub(/\A(?=[^.])/, ?.) || '.md'
|
|
48
|
+
STDIN.reopen('/dev/tty')
|
|
49
|
+
content = edit_text(content, basename:)
|
|
50
|
+
end
|
|
41
51
|
response = OllamaChat::ServerSocket.send_to_server_socket(
|
|
42
|
-
|
|
52
|
+
content,
|
|
43
53
|
type:,
|
|
44
54
|
config:,
|
|
45
55
|
working_dir: opts[?d],
|
data/lib/ollama_chat/chat.rb
CHANGED
|
@@ -160,6 +160,21 @@ class OllamaChat::Chat
|
|
|
160
160
|
# @return [OllamaChat::RedisCache, nil] the cache instance
|
|
161
161
|
attr_reader :cache
|
|
162
162
|
|
|
163
|
+
|
|
164
|
+
# Returns the voice handler class based on the configuration.
|
|
165
|
+
#
|
|
166
|
+
# This method resolves the voice handler class specified in the config.
|
|
167
|
+
# If running within RSpec, it returns an empty array to avoid side effects.
|
|
168
|
+
#
|
|
169
|
+
# @return [Class, Array] the voice handler class or an empty array if in test mode
|
|
170
|
+
def voice_handler
|
|
171
|
+
defined?(RSpec) and return []
|
|
172
|
+
case config.voice.handler
|
|
173
|
+
when 'OllamaChat::Say'
|
|
174
|
+
OllamaChat::Say
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
163
178
|
# The start method initializes the chat session by displaying information,
|
|
164
179
|
# then prompts the user for input to begin interacting with the chat.
|
|
165
180
|
def start
|
|
@@ -214,11 +229,14 @@ class OllamaChat::Chat
|
|
|
214
229
|
#
|
|
215
230
|
# @return [ Symbol ] the collection name symbol
|
|
216
231
|
def initial_collection
|
|
217
|
-
|
|
218
|
-
session&.current_collection.full? ||
|
|
232
|
+
c = session&.current_collection.full? ||
|
|
219
233
|
config.embedding.collection.full? ||
|
|
220
|
-
|
|
221
|
-
|
|
234
|
+
'default'
|
|
235
|
+
if c =~ /\A#{OllamaChat::COLLECTION_NAME_REGEXP.source}\z/
|
|
236
|
+
c
|
|
237
|
+
else
|
|
238
|
+
'default'
|
|
239
|
+
end.to_sym
|
|
222
240
|
end
|
|
223
241
|
|
|
224
242
|
# The initial_system_prompt method returns the system prompt for the initial
|
data/lib/ollama_chat/commands.rb
CHANGED
|
@@ -296,8 +296,10 @@ module OllamaChat::Commands
|
|
|
296
296
|
when 'summarize'
|
|
297
297
|
opts = go_command('fs', opts)
|
|
298
298
|
if opts[?f] and
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
filename = switch_history(:filename) {
|
|
300
|
+
ask?(prompt: "❓ Enter filename: ")
|
|
301
|
+
}.full? { Pathname.new(_1) }
|
|
302
|
+
then
|
|
301
303
|
if filename.exist? && !confirm?(
|
|
302
304
|
prompt: "🔔 File #{filename.to_s.inspect} already exists, overwrite? (y/n) ",
|
|
303
305
|
yes: /\Ay/i
|
|
@@ -807,7 +809,7 @@ module OllamaChat::Commands
|
|
|
807
809
|
|
|
808
810
|
command(
|
|
809
811
|
name: :input,
|
|
810
|
-
regexp: %r(^/input(?:\s+(path|context|embedding|summary)(?:\s*(?=\z))?)?((?:\s+-(?:[apre]|c\s
|
|
812
|
+
regexp: %r(^/input(?:\s+(path|context|embedding|summary)(?:\s*(?=\z))?)?((?:\s+-(?:[apre]|c\s*#{OllamaChat::COLLECTION_NAME_REGEXP.source}|w\s*\d+|t\s*[-\w\.]+(?:,[-\w\.]+)*))*)(?:\s+([^-].*))?$),
|
|
811
813
|
optional: true,
|
|
812
814
|
complete: [ 'input', %w[ path context embedding summary ] ],
|
|
813
815
|
options: <<~EOT,
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Removes the `think_mode` CHECK constraint from the `sessions` table.
|
|
2
|
+
#
|
|
3
|
+
# The set of valid think modes is model-dependent and evolving upstream
|
|
4
|
+
# (e.g., `max` was only added mid-2026, and numeric token budgets are
|
|
5
|
+
# under discussion), so the schema should not pin a fixed vocabulary.
|
|
6
|
+
# State validation is handled in Ruby via
|
|
7
|
+
# `OllamaChat::ThinkControl::THINK_MODE_STATES` and the state selectors.
|
|
8
|
+
#
|
|
9
|
+
# SQLite cannot alter CHECK constraints in place, so the table is rebuilt
|
|
10
|
+
# using the same shadow-table pattern as
|
|
11
|
+
# `004_add_profile_to_model_options.rb`.
|
|
12
|
+
Sequel.migration do
|
|
13
|
+
up do
|
|
14
|
+
create_table(:sessions_new) do
|
|
15
|
+
primary_key :id
|
|
16
|
+
String :name, null: false
|
|
17
|
+
String :current_model
|
|
18
|
+
String :current_collection
|
|
19
|
+
String :default_persona_name
|
|
20
|
+
String :current_system_prompt
|
|
21
|
+
Boolean :tools_enabled, null: false
|
|
22
|
+
Text :tools_default_enabled
|
|
23
|
+
String :think_mode, null: false
|
|
24
|
+
Bool :think_loud_enabled, null: false
|
|
25
|
+
Bool :embedding_enabled, null: false
|
|
26
|
+
String :document_policy, null: false
|
|
27
|
+
Bool :runtime_info_enabled, null: false
|
|
28
|
+
Bool :think_strip_enabled, null: false
|
|
29
|
+
Boolean :markdown_enabled, null: false
|
|
30
|
+
Boolean :stream_enabled, null: false
|
|
31
|
+
Boolean :location_enabled, null: false
|
|
32
|
+
Boolean :voice_enabled, null: false
|
|
33
|
+
String :current_voice, null: false
|
|
34
|
+
Text :working_directory
|
|
35
|
+
Integer :locked_by_pid
|
|
36
|
+
Text :model_options
|
|
37
|
+
Text :messages, null: false
|
|
38
|
+
Text :links, null: false, default: ''
|
|
39
|
+
Text :history, null: false, default: ''
|
|
40
|
+
String :context_format, null: false, default: 'JSON'
|
|
41
|
+
Time :created_at
|
|
42
|
+
Time :updated_at
|
|
43
|
+
unique [:name]
|
|
44
|
+
|
|
45
|
+
constraint(
|
|
46
|
+
:document_policy,
|
|
47
|
+
document_policy: %w[ ignoring embedding importing summarizing ]
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
source = from(:sessions).select(
|
|
52
|
+
:id, :name, :current_model, :current_collection,
|
|
53
|
+
:default_persona_name, :current_system_prompt,
|
|
54
|
+
:tools_enabled, :tools_default_enabled, :think_mode,
|
|
55
|
+
:think_loud_enabled, :embedding_enabled, :document_policy,
|
|
56
|
+
:runtime_info_enabled, :think_strip_enabled,
|
|
57
|
+
:markdown_enabled, :stream_enabled, :location_enabled,
|
|
58
|
+
:voice_enabled, :current_voice, :working_directory,
|
|
59
|
+
:locked_by_pid, :model_options, :messages, :links,
|
|
60
|
+
:history, :context_format, :created_at, :updated_at
|
|
61
|
+
)
|
|
62
|
+
from(:sessions_new).insert(source)
|
|
63
|
+
|
|
64
|
+
drop_table(:sessions)
|
|
65
|
+
rename_table(:sessions_new, :sessions)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
down do
|
|
69
|
+
# Restores the original `think_mode` constraint. All rows that
|
|
70
|
+
# predate the up-migration satisfy it; any newer value (e.g. `max`)
|
|
71
|
+
# would fail the insert, which is the desired loud failure on
|
|
72
|
+
# rollback.
|
|
73
|
+
create_table(:sessions_old) do
|
|
74
|
+
primary_key :id
|
|
75
|
+
String :name, null: false
|
|
76
|
+
String :current_model
|
|
77
|
+
String :current_collection
|
|
78
|
+
String :default_persona_name
|
|
79
|
+
String :current_system_prompt
|
|
80
|
+
Boolean :tools_enabled, null: false
|
|
81
|
+
Text :tools_default_enabled
|
|
82
|
+
String :think_mode, null: false
|
|
83
|
+
Bool :think_loud_enabled, null: false
|
|
84
|
+
Bool :embedding_enabled, null: false
|
|
85
|
+
String :document_policy, null: false
|
|
86
|
+
Bool :runtime_info_enabled, null: false
|
|
87
|
+
Bool :think_strip_enabled, null: false
|
|
88
|
+
Boolean :markdown_enabled, null: false
|
|
89
|
+
Boolean :stream_enabled, null: false
|
|
90
|
+
Boolean :location_enabled, null: false
|
|
91
|
+
Boolean :voice_enabled, null: false
|
|
92
|
+
String :current_voice, null: false
|
|
93
|
+
Text :working_directory
|
|
94
|
+
Integer :locked_by_pid
|
|
95
|
+
Text :model_options
|
|
96
|
+
Text :messages, null: false
|
|
97
|
+
Text :links, null: false, default: ''
|
|
98
|
+
Text :history, null: false, default: ''
|
|
99
|
+
String :context_format, null: false, default: 'JSON'
|
|
100
|
+
Time :created_at
|
|
101
|
+
Time :updated_at
|
|
102
|
+
unique [:name]
|
|
103
|
+
|
|
104
|
+
constraint(
|
|
105
|
+
:think_mode,
|
|
106
|
+
think_mode: %w[ disabled enabled low medium high ]
|
|
107
|
+
)
|
|
108
|
+
constraint(
|
|
109
|
+
:document_policy,
|
|
110
|
+
document_policy: %w[ ignoring embedding importing summarizing ]
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
source = from(:sessions).select(
|
|
115
|
+
:id, :name, :current_model, :current_collection,
|
|
116
|
+
:default_persona_name, :current_system_prompt,
|
|
117
|
+
:tools_enabled, :tools_default_enabled, :think_mode,
|
|
118
|
+
:think_loud_enabled, :embedding_enabled, :document_policy,
|
|
119
|
+
:runtime_info_enabled, :think_strip_enabled,
|
|
120
|
+
:markdown_enabled, :stream_enabled, :location_enabled,
|
|
121
|
+
:voice_enabled, :current_voice, :working_directory,
|
|
122
|
+
:locked_by_pid, :model_options, :messages, :links,
|
|
123
|
+
:history, :context_format, :created_at, :updated_at
|
|
124
|
+
)
|
|
125
|
+
from(:sessions_old).insert(source)
|
|
126
|
+
|
|
127
|
+
drop_table(:sessions)
|
|
128
|
+
rename_table(:sessions_old, :sessions)
|
|
129
|
+
end
|
|
130
|
+
end
|
data/lib/ollama_chat/dialog.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'io/console'
|
|
2
|
+
|
|
1
3
|
# A module that provides interactive selection and configuration functionality
|
|
2
4
|
# for OllamaChat.
|
|
3
5
|
#
|
|
@@ -35,27 +37,32 @@ module OllamaChat::Dialog
|
|
|
35
37
|
# @param default [Object, nil] value returned when the timeout expires
|
|
36
38
|
# (defaults to `nil`)
|
|
37
39
|
# @param yes [Object, nil] value that is considered a positive response
|
|
38
|
-
# @param output [IO] the IO object to write the prompt to
|
|
40
|
+
# @param output [IO] the IO object to write the prompt and the answer to
|
|
39
41
|
#
|
|
40
|
-
# @return [Object] the character entered by the user,
|
|
41
|
-
# if a timeout occurs
|
|
42
|
+
# @return [Object] the character entered by the user, the `default` value
|
|
43
|
+
# if a timeout occurs, or `nil` if the read is interrupted (e.g. Ctrl-C)
|
|
42
44
|
def confirm?(prompt:, timeout: nil, default: nil, yes: nil, output: STDOUT)
|
|
43
45
|
return default if timeout&.zero?
|
|
44
46
|
if prompt.include?('%s')
|
|
45
47
|
prompt = prompt % (timeout ? ('timeout in %us' % timeout) : 'no timeout')
|
|
46
48
|
end
|
|
47
|
-
print prompt
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
output.print prompt
|
|
50
|
+
min = 1
|
|
51
|
+
time = 0
|
|
52
|
+
if timeout
|
|
53
|
+
min = 0
|
|
54
|
+
time = timeout
|
|
55
|
+
end
|
|
56
|
+
exceptions = [ (IRB::Abort if defined? IRB), Interrupt ].compact
|
|
57
|
+
begin
|
|
58
|
+
keypress = STDIN.raw(min:, time:, intr: true) do |io|
|
|
59
|
+
io.getc
|
|
60
|
+
end
|
|
61
|
+
rescue *exceptions
|
|
62
|
+
output.puts "\u274C\uFE0F"
|
|
63
|
+
return
|
|
64
|
+
end
|
|
65
|
+
answer = keypress || default
|
|
59
66
|
case
|
|
60
67
|
when yes.nil?
|
|
61
68
|
if keypress
|
|
@@ -68,12 +75,12 @@ module OllamaChat::Dialog
|
|
|
68
75
|
if keypress
|
|
69
76
|
output.puts "\u2705\uFE0F #{answer}"
|
|
70
77
|
else
|
|
71
|
-
output.puts "
|
|
78
|
+
output.puts "\u2611\uFE0F #{answer}"
|
|
72
79
|
end
|
|
73
80
|
answer
|
|
74
81
|
else
|
|
75
82
|
if keypress
|
|
76
|
-
output.puts "
|
|
83
|
+
output.puts "\u{1F6AB} #{answer}"
|
|
77
84
|
else
|
|
78
85
|
output.puts "\u231B\uFE0F #{answer}"
|
|
79
86
|
end
|
|
@@ -111,20 +111,22 @@ module OllamaChat::FileEditing
|
|
|
111
111
|
# @return [Pathname, nil] the validated filename as a Pathname, or nil if the
|
|
112
112
|
# operation was cancelled
|
|
113
113
|
def determine_valid_output_filename(action)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
114
|
+
switch_history(:filename) do
|
|
115
|
+
loop do
|
|
116
|
+
filename_str = ask?(
|
|
117
|
+
prompt: "❓ Enter filename #{action}, C-c ⇒ cancel: "
|
|
118
|
+
)
|
|
119
|
+
if filename_str.nil?
|
|
120
|
+
STDOUT.puts "Cancelled."
|
|
121
|
+
return nil
|
|
122
|
+
end
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
filename = Pathname.new(filename_str)
|
|
125
|
+
if filename.exist?
|
|
126
|
+
STDERR.puts "File #{filename.to_path.inspect} already exists!"
|
|
127
|
+
else
|
|
128
|
+
return filename
|
|
129
|
+
end
|
|
128
130
|
end
|
|
129
131
|
end
|
|
130
132
|
end
|
|
@@ -29,12 +29,19 @@ class OllamaChat::FollowChat
|
|
|
29
29
|
super(output:)
|
|
30
30
|
@chat = chat
|
|
31
31
|
@output.sync = true
|
|
32
|
-
@say = voice ? Handlers::Say.new(voice:) : NOP
|
|
33
32
|
@messages = messages
|
|
34
33
|
@group_uuid = group_uuid
|
|
35
34
|
@sender = nil
|
|
35
|
+
@say = if voice && @chat.voice_handler.respond_to?(:new)
|
|
36
|
+
@chat.voice_handler.new(chat:, voice:)
|
|
37
|
+
else
|
|
38
|
+
NOP
|
|
39
|
+
end
|
|
36
40
|
end
|
|
37
41
|
|
|
42
|
+
# Returns the chat object representing the conversation context.
|
|
43
|
+
#
|
|
44
|
+
# @return [OllamaChat::Chat] The chat object.
|
|
38
45
|
attr_reader :chat
|
|
39
46
|
|
|
40
47
|
# Returns the conversation history (an array of message objects).
|
|
@@ -70,7 +77,7 @@ class OllamaChat::FollowChat
|
|
|
70
77
|
ensure_assistant_response_exists
|
|
71
78
|
update_last_message(response)
|
|
72
79
|
if chat.stream.on?
|
|
73
|
-
display_formatted_terminal_output
|
|
80
|
+
display_formatted_terminal_output(height: Tins::Terminal.lines - 1)
|
|
74
81
|
else
|
|
75
82
|
if display_output
|
|
76
83
|
display_formatted_terminal_output
|
|
@@ -289,9 +296,9 @@ class OllamaChat::FollowChat
|
|
|
289
296
|
def prepare_last_message
|
|
290
297
|
content, thinking = @messages.last.content, @messages.last.thinking
|
|
291
298
|
if chat.markdown.on?
|
|
292
|
-
content = talk_annotate {
|
|
299
|
+
content = talk_annotate { chat.kramdown_ansi_parse(content) }
|
|
293
300
|
if chat.think?
|
|
294
|
-
thinking = think_annotate {
|
|
301
|
+
thinking = think_annotate { chat.kramdown_ansi_parse(thinking) }
|
|
295
302
|
end
|
|
296
303
|
else
|
|
297
304
|
content = talk_annotate { content }
|
|
@@ -300,20 +307,28 @@ class OllamaChat::FollowChat
|
|
|
300
307
|
return content&.chomp, thinking&.chomp
|
|
301
308
|
end
|
|
302
309
|
|
|
303
|
-
# The last_message_with_user method constructs a formatted
|
|
310
|
+
# The last_message_with_user method constructs a formatted string by
|
|
304
311
|
# combining user information, newline characters, thinking annotations, and
|
|
305
312
|
# content for display in the terminal output.
|
|
306
313
|
#
|
|
307
|
-
# @
|
|
308
|
-
#
|
|
309
|
-
#
|
|
310
|
-
|
|
314
|
+
# @param height [Integer] the maximum number of lines to truncate the output
|
|
315
|
+
# to. Defaults to 0 (no truncation).
|
|
316
|
+
#
|
|
317
|
+
# @return [String] the formatted message string containing the user
|
|
318
|
+
# identifier, newline character, thinking annotation (if present), and
|
|
319
|
+
# content formatted for terminal display
|
|
320
|
+
def last_message_with_user(height: 0)
|
|
311
321
|
content, thinking = prepare_last_message
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
322
|
+
content ||= ''
|
|
323
|
+
result = if thinking.present?
|
|
324
|
+
[ @sender + ":\n", thinking, ?\n, content ]
|
|
325
|
+
else
|
|
326
|
+
[ @sender + ":\n", content ]
|
|
327
|
+
end * ''
|
|
328
|
+
if height > 0
|
|
329
|
+
result = truncate_for_terminal(result, max_lines: height)
|
|
316
330
|
end
|
|
331
|
+
result
|
|
317
332
|
end
|
|
318
333
|
|
|
319
334
|
# The display_formatted_terminal_output method formats and outputs the
|
|
@@ -323,9 +338,17 @@ class OllamaChat::FollowChat
|
|
|
323
338
|
# move home commands. The method takes into account whether markdown and
|
|
324
339
|
# thinking modes are enabled to determine how to process and display the
|
|
325
340
|
# content.
|
|
326
|
-
|
|
341
|
+
#
|
|
342
|
+
# @param output [IO, nil] the output stream to print to. Defaults to
|
|
343
|
+
# `@output`.
|
|
344
|
+
# @param height [Integer] the maximum number of lines to truncate the output
|
|
345
|
+
# to. Defaults to 0 (no truncation).
|
|
346
|
+
def display_formatted_terminal_output(output = nil, height: 0)
|
|
327
347
|
output ||= @output
|
|
328
|
-
output.print(
|
|
348
|
+
output.print(
|
|
349
|
+
move_home, erase_in_display(nil),
|
|
350
|
+
last_message_with_user(height:)
|
|
351
|
+
)
|
|
329
352
|
end
|
|
330
353
|
|
|
331
354
|
# The display_output method shows the last message in the conversation.
|
|
@@ -341,7 +364,7 @@ class OllamaChat::FollowChat
|
|
|
341
364
|
if chat.markdown.on?
|
|
342
365
|
display_formatted_terminal_output(output)
|
|
343
366
|
else
|
|
344
|
-
output.print(
|
|
367
|
+
output.print(last_message_with_user)
|
|
345
368
|
end
|
|
346
369
|
end
|
|
347
370
|
end
|
|
@@ -255,7 +255,9 @@ module OllamaChat::ModelHandling
|
|
|
255
255
|
STDOUT.puts "Cancelled."
|
|
256
256
|
return
|
|
257
257
|
when '[NEW]'
|
|
258
|
-
name =
|
|
258
|
+
name = switch_history(:profile_name) do
|
|
259
|
+
ask?(prompt: 'Enter new profile name: ')
|
|
260
|
+
end or return
|
|
259
261
|
if models::ModelOptions.where(model_name:, profile: name).present?
|
|
260
262
|
STDERR.puts "Profile #{name.inspect} already exists!"
|
|
261
263
|
return
|