ollama_chat 0.0.121 → 0.0.122
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 +117 -0
- data/README.md +2 -2
- data/Rakefile +2 -1
- data/lib/ollama_chat/asr.rb +110 -0
- data/lib/ollama_chat/chat.rb +27 -4
- data/lib/ollama_chat/commands.rb +30 -19
- data/lib/ollama_chat/database/models/session.rb +1 -1
- data/lib/ollama_chat/dialog.rb +6 -0
- data/lib/ollama_chat/http_handling.rb +25 -0
- data/lib/ollama_chat/information.rb +7 -1
- data/lib/ollama_chat/invidious.rb +162 -0
- data/lib/ollama_chat/model_handling.rb +23 -2
- data/lib/ollama_chat/oc.rb +53 -0
- data/lib/ollama_chat/ollama_chat_config/default_config.yml +4 -1
- data/lib/ollama_chat/parsing.rb +54 -11
- data/lib/ollama_chat/personae_management.rb +1 -1
- data/lib/ollama_chat/prompt_management.rb +66 -2
- data/lib/ollama_chat/say.rb +4 -0
- data/lib/ollama_chat/source_fetching.rb +20 -11
- data/lib/ollama_chat/speaker.rb +70 -0
- data/lib/ollama_chat/state_selectors.rb +35 -11
- data/lib/ollama_chat/think_control.rb +39 -9
- data/lib/ollama_chat/tools/generate_image.rb +5 -5
- data/lib/ollama_chat/tools/get_url.rb +29 -4
- data/lib/ollama_chat/tools/patch_file.rb +5 -1
- data/lib/ollama_chat/tts.rb +24 -21
- data/lib/ollama_chat/utils/audio_player.rb +1 -1
- data/lib/ollama_chat/utils/fetcher.rb +40 -10
- data/lib/ollama_chat/utils/png_metadata_extractor.rb +1 -0
- data/lib/ollama_chat/utils.rb +7 -7
- data/lib/ollama_chat/version.rb +1 -1
- data/lib/ollama_chat.rb +2 -0
- data/ollama_chat.gemspec +7 -6
- data/spec/assets/linux.oga +0 -0
- data/spec/ollama_chat/asr_spec.rb +89 -0
- data/spec/ollama_chat/commands_spec.rb +120 -5
- data/spec/ollama_chat/http_handling_spec.rb +145 -0
- data/spec/ollama_chat/invidious_spec.rb +238 -0
- data/spec/ollama_chat/message_list_spec.rb +1 -2
- data/spec/ollama_chat/model_handling_spec.rb +42 -0
- data/spec/ollama_chat/parsing_spec.rb +61 -2
- data/spec/ollama_chat/session_management_spec.rb +5 -1
- data/spec/ollama_chat/source_fetching_spec.rb +32 -0
- data/spec/ollama_chat/tools/directory_structure_spec.rb +1 -1
- data/spec/ollama_chat/tools/get_url_spec.rb +58 -19
- data/spec/ollama_chat/tts_spec.rb +9 -11
- data/spec/ollama_chat/utils/audio_player_spec.rb +4 -4
- data/spec/spec_helper.rb +1 -1
- metadata +30 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d77fd2cddf7c921b77712a90d4d1d31b3daf0f80dc4170f71eb209e7ce46390b
|
|
4
|
+
data.tar.gz: af9f30997ca31d67d7ba1914a971b5abb165d1913b2723c0727f07929b4512e4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba243ffe79c5062b0feb5fc2d6389efff95216961567dbe9275f020cbdf811d1f04f238e95972d007e611109c3bcc795057c8a2c4afeeaf7d1b739301b91c0f5
|
|
7
|
+
data.tar.gz: badb4e4ccca4ee006cdb48fd66b107367c81d7bf3ee3717482e4c61f3bc4d6fcda80d4cc54edefb9a0b5af5a6dbcf32173eeb3e0f48f545d32f5c3508d472ba1
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,122 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-09-25 v0.0.122
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
* **Per-model thinking-state support**: Added support for per-model thinking
|
|
8
|
+
states via the `/api/show` endpoint.
|
|
9
|
+
* Replaced the `THINK_MODE_STATES` constant with a `think_mode_states`
|
|
10
|
+
method that reads `@model_metadata&.thinking&.full?(:values)` to return
|
|
11
|
+
a per-model subset, falling back to the full superset for
|
|
12
|
+
pre-**0.34.3** Ollama or non-thinking models.
|
|
13
|
+
* Added `normalize_think_state` helper to canonicalize boolean
|
|
14
|
+
(`true`/`false`) and string (`"low"`/`"high"`/`"max"`) thinking values.
|
|
15
|
+
* Added `:thinking` field to `ModelMetadata` data class.
|
|
16
|
+
* Implemented think-mode clamping in `use_model` to disable thinking for
|
|
17
|
+
non-thinking models or clamp to the model's default when the current
|
|
18
|
+
level is unavailable.
|
|
19
|
+
* Converted `DatabaseStateSelector` `states`/`off` to support `Proc` via
|
|
20
|
+
`ask_and_send_or_self(:call, @chat)`.
|
|
21
|
+
* Converted `think_mode` and `voices` selectors in
|
|
22
|
+
`setup_state_selectors` to lambda-based `states:`.
|
|
23
|
+
* Added `Thinking` display line to `info_model`.
|
|
24
|
+
* **Invidious YouTube captions**: Added `OllamaChat::Invidious` to fetch
|
|
25
|
+
video metadata and transcripts from an Invidious companion instance.
|
|
26
|
+
* Uses `POST /companion/youtubei/v1/player` for `videoDetails` and
|
|
27
|
+
`captionTracks`.
|
|
28
|
+
* Selects the best caption track by priority-ordered language regexes.
|
|
29
|
+
* Fetches timedtext XML and extracts `<text>` elements via `Nokogiri`,
|
|
30
|
+
decoding double-encoded HTML entities.
|
|
31
|
+
* Returns a formatted markdown header + transcript as a text IO.
|
|
32
|
+
* **Unified HTTP handling**: Added `HTTPHandling#request_url_response` to
|
|
33
|
+
consolidate `Excon.new` call sites, merging `http_options` (SSL, proxy)
|
|
34
|
+
with caller-supplied options.
|
|
35
|
+
* Updated `ASR.transcribe`, `TTS.voices`, `TTS#fetch_tts`,
|
|
36
|
+
`GenerateImage#post_url`, `GenerateImage#get_url`, `GetURL#execute`,
|
|
37
|
+
`Invidious.fetch_video_info`, and `SourceFetching#fetch_source` to
|
|
38
|
+
route through this method.
|
|
39
|
+
* Extracted audio/video MIME regex into `HAS_AUDIO` constant shared by
|
|
40
|
+
`parse_source` and the `get_url` tool.
|
|
41
|
+
* Split `Fetcher` `middlewares` into class-level `self.middlewares` and
|
|
42
|
+
instance delegate; added `RedirectFollower` to the default stack.
|
|
43
|
+
* Added `middlewares:` to streaming `excon` requests in `fetcher.rb`.
|
|
44
|
+
* **One-shot voice notification**: Added `OllamaChat::Speaker` concern
|
|
45
|
+
providing `speak(text, background:)` and `cancel_speaking`.
|
|
46
|
+
* `OllamaChat::TTS` and `OllamaChat::Say` now include
|
|
47
|
+
`OllamaChat::Speaker`.
|
|
48
|
+
* Added `Chat#speak` facade to resolve the configured voice handler.
|
|
49
|
+
* `Dialog#readline_prompt` and `Dialog#confirm?` now speak the prompt
|
|
50
|
+
with a 10-second delay.
|
|
51
|
+
* `Tools::PatchFile` announces "You can review the patch for the file …
|
|
52
|
+
now!" before opening `vimdiff`.
|
|
53
|
+
* `AudioPlayer#start` redirects `ffplay` stdout/stderr to `/dev/null`.
|
|
54
|
+
* **ASR transcription pipeline**: Added `OllamaChat::ASR` client for
|
|
55
|
+
audio/video transcription.
|
|
56
|
+
* Uploads audio/video as multipart form-data to the audio.cpp
|
|
57
|
+
`/v1/audio/transcriptions` endpoint.
|
|
58
|
+
* Converts video sources to 16 kHz mono PCM WAV via a configurable
|
|
59
|
+
`ffmpeg` command (`CONVERT_COMMAND`).
|
|
60
|
+
* Added `OC::OLLAMA::CHAT::ASR` config module with `URL`,
|
|
61
|
+
`CONVERT_COMMAND`, and `MODEL` settings.
|
|
62
|
+
* Extended `/input` command with `-l <language>` option.
|
|
63
|
+
* Routed `audio/*`, `video/*`, and specific `application/` MIME types
|
|
64
|
+
into `parse_audio`.
|
|
65
|
+
* Added `Fetcher::ResponseMetadata.as_text(text)` factory for synthetic
|
|
66
|
+
`text/plain` IO responses.
|
|
67
|
+
* Replaced hardcoded timeouts in `lib/ollama_chat/tts.rb` with values
|
|
68
|
+
from `config.timeouts`.
|
|
69
|
+
* **Audio/Video support in `get_url`**: Added `language` parameter to the
|
|
70
|
+
`get_url` tool definition for optional transcription hints.
|
|
71
|
+
* Pre-transcribes `audio/*` and `video/*` sources via `chat.parse_audio`
|
|
72
|
+
before content-type dispatch.
|
|
73
|
+
* Threads `language:` kwarg into `chat.import_source`.
|
|
74
|
+
* **Prompt management enhancements**:
|
|
75
|
+
* Added `reset_prompt_count` and `choose_and_reset_count` to
|
|
76
|
+
`lib/ollama_chat/prompt_management.rb`.
|
|
77
|
+
* Added `/prompt reset count` command.
|
|
78
|
+
* Added `count:` keyword argument to `choose_prompt` to increment
|
|
79
|
+
`metadata['use_count']`.
|
|
80
|
+
* Changed `all_prompts` sort to `use_count` descending, falling back to
|
|
81
|
+
name for tie-breaking.
|
|
82
|
+
* Added `-i` summary instruction flag to `/input` command to prompt for
|
|
83
|
+
an optional instruction.
|
|
84
|
+
* Added `instruction` parameter to `get_url` summarizing, threading it
|
|
85
|
+
through `summarize_source` and `summarize`.
|
|
86
|
+
* **Config-driven default persona**: Added `persona.default_name` to
|
|
87
|
+
`default_config.yml`.
|
|
88
|
+
* Updated `initial_persona_name` in `personae_management.rb` to fall back
|
|
89
|
+
to `config.persona&.default_name`.
|
|
90
|
+
|
|
91
|
+
## Improvements
|
|
92
|
+
|
|
93
|
+
* **CI and Dependencies**:
|
|
94
|
+
* Added `ffmpeg` to the CI Docker image.
|
|
95
|
+
* Added `nokogiri ~> 1.0` dependency.
|
|
96
|
+
* Bumped `tins ~> 1.58`.
|
|
97
|
+
* Added `require 'ollama_chat/utils/utf8_converter'` in
|
|
98
|
+
`png_metadata_extractor.rb`.
|
|
99
|
+
* Reorganized `require` statements in `lib/ollama_chat/utils.rb`
|
|
100
|
+
alphabetically.
|
|
101
|
+
* **Error Handling**:
|
|
102
|
+
* In `lib/ollama_chat/asr.rb`, capture the return value of `system` to
|
|
103
|
+
detect missing executables and print a clear "not found in PATH"
|
|
104
|
+
message.
|
|
105
|
+
* Extract executable name via `Shellwords.split(cmd).first` for error
|
|
106
|
+
messages.
|
|
107
|
+
* **Documentation**:
|
|
108
|
+
* Updated `ollama_chat` description in README to be more concise and
|
|
109
|
+
user-focused.
|
|
110
|
+
* Updated YARD in `session.rb`, `state_selectors.rb`, and
|
|
111
|
+
`think_control.rb` to reference `Chat#think_mode_states`.
|
|
112
|
+
|
|
113
|
+
## Bug Fixes
|
|
114
|
+
|
|
115
|
+
* **Pager Spec**: Changed `expect(Tins::Terminal).to receive(:lines)` to
|
|
116
|
+
`allow(Tins::Terminal).to receive(:lines)` in
|
|
117
|
+
`spec/ollama_chat/message_list_spec.rb` and removed the `skip 'no tty'`
|
|
118
|
+
guard.
|
|
119
|
+
|
|
3
120
|
## 2026-09-18 v0.0.121
|
|
4
121
|
|
|
5
122
|
* Added `**_kwargs` splat to `Say.voices` to prevent `ArgumentError` when the
|
data/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Description
|
|
4
4
|
|
|
5
|
-
**ollama_chat** is a chat client
|
|
6
|
-
server and
|
|
5
|
+
**ollama_chat** is a terminal chat client that connects to an Ollama
|
|
6
|
+
server and lets you converse with the LLMs it provides.
|
|
7
7
|
|
|
8
8
|
## Documentation
|
|
9
9
|
|
data/Rakefile
CHANGED
|
@@ -49,7 +49,7 @@ GemHadar do
|
|
|
49
49
|
dependency 'reverse_markdown', '~> 3.0'
|
|
50
50
|
dependency 'kramdown-ansi', '~> 0.5'
|
|
51
51
|
dependency 'complex_config', '~> 0.22', '>= 0.22.2'
|
|
52
|
-
dependency 'tins', '~> 1.
|
|
52
|
+
dependency 'tins', '~> 1.58'
|
|
53
53
|
dependency 'search_ui', '>= 0.2'
|
|
54
54
|
dependency 'amatch', '~> 0.4'
|
|
55
55
|
dependency 'pdf-reader', '~> 2.0'
|
|
@@ -61,6 +61,7 @@ GemHadar do
|
|
|
61
61
|
dependency 'rubyzip', '~> 3.0'
|
|
62
62
|
dependency 'sequel', '~> 5.0'
|
|
63
63
|
dependency 'file-tail', '~> 1.4'
|
|
64
|
+
dependency 'nokogiri', '~> 1.0'
|
|
64
65
|
development_dependency 'all_images', '~> 0.12'
|
|
65
66
|
development_dependency 'rspec', '~> 3.2'
|
|
66
67
|
development_dependency 'kramdown', '~> 2.0'
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require 'securerandom'
|
|
2
|
+
|
|
3
|
+
# Standalone ASR (Speech-to-Text) transcription client for audio.cpp.
|
|
4
|
+
#
|
|
5
|
+
# Transcribes audio or video sources by uploading them as multipart
|
|
6
|
+
# form-data to the audio.cpp `/v1/audio/transcriptions` endpoint.
|
|
7
|
+
# Video sources are first converted to 16 kHz mono WAV via ffmpeg.
|
|
8
|
+
#
|
|
9
|
+
# @example Transcribing a WAV source
|
|
10
|
+
# text = OllamaChat::ASR.transcribe(source_io, chat:)
|
|
11
|
+
#
|
|
12
|
+
# @example Transcribing an MP4 video (ffmpeg extraction)
|
|
13
|
+
# text = OllamaChat::ASR.transcribe(source_io, chat:)
|
|
14
|
+
module OllamaChat
|
|
15
|
+
class ASR
|
|
16
|
+
class << self
|
|
17
|
+
# Transcribes audio/video content via the audio.cpp ASR endpoint.
|
|
18
|
+
#
|
|
19
|
+
# @param source_io [IO] the audio/video source stream
|
|
20
|
+
# (must respond to #content_type and be seekable)
|
|
21
|
+
# @param language [String, nil] optional language hint
|
|
22
|
+
# (e.g. "en", "de") passed as the `language` form field
|
|
23
|
+
# @param chat [OllamaChat::Chat] the chat instance (for HTTP middleware)
|
|
24
|
+
#
|
|
25
|
+
# @return [String, nil] the transcribed text, or nil on failure
|
|
26
|
+
def transcribe(source_io, chat:, language: nil)
|
|
27
|
+
asr_url = OC::OLLAMA::CHAT::ASR::URL
|
|
28
|
+
asr_model = OC::OLLAMA::CHAT::ASR::MODEL
|
|
29
|
+
input = Tempfile.new(['asr_in'])
|
|
30
|
+
wav = Tempfile.new(['asr', '.wav'])
|
|
31
|
+
begin
|
|
32
|
+
IO.copy_stream(source_io, input)
|
|
33
|
+
input.flush
|
|
34
|
+
|
|
35
|
+
cmd = OC::OLLAMA::CHAT::ASR::CONVERT_COMMAND % {
|
|
36
|
+
input: input.path, output: wav.path
|
|
37
|
+
}
|
|
38
|
+
executable = Shellwords.split(cmd).first
|
|
39
|
+
result = system(cmd, out: File::NULL, err: File::NULL)
|
|
40
|
+
if result.nil?
|
|
41
|
+
STDERR.puts "ASR: #{executable} not found in PATH."
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
unless File.size(wav.path) > 0
|
|
45
|
+
STDERR.puts "ASR: #{executable} produced no output."
|
|
46
|
+
return
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
url = "#{asr_url}/v1/audio/transcriptions"
|
|
50
|
+
form_data = multipart_form_data(wav.path, model: asr_model, language:)
|
|
51
|
+
chat.request_url_response(:post, url, **form_data) do |response|
|
|
52
|
+
return JSON.parse(response.body)['text']
|
|
53
|
+
end
|
|
54
|
+
ensure
|
|
55
|
+
input.close!
|
|
56
|
+
wav&.close!
|
|
57
|
+
end
|
|
58
|
+
rescue JSON::ParserError, Excon::Error => e
|
|
59
|
+
if response = e.ask_and_send(:response)
|
|
60
|
+
status = response.status
|
|
61
|
+
result = JSON.parse(response.body) rescue nil
|
|
62
|
+
end
|
|
63
|
+
chat.log(:error, e, data: { status:, result: })
|
|
64
|
+
STDERR.puts "ASR transcription failed: #{e.message}"
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Builds a multipart/form-data request body for the ASR endpoint.
|
|
71
|
+
#
|
|
72
|
+
# Constructs the wire format manually (boundary, text field parts,
|
|
73
|
+
# file part) so the result can be splatted directly into
|
|
74
|
+
# {HTTPHandling#request_url_response}.
|
|
75
|
+
#
|
|
76
|
+
# @param file_path [String] path to the WAV file to upload
|
|
77
|
+
# @param rest [Hash] additional form fields (e.g. `model:`,
|
|
78
|
+
# `language:`); `nil` values are omitted via `compact`
|
|
79
|
+
#
|
|
80
|
+
# @return [Hash] Excon-compatible options with `:headers`
|
|
81
|
+
# (Content-Type including the generated boundary), `:expects`
|
|
82
|
+
# (200), and `:body` (the assembled multipart string)
|
|
83
|
+
def multipart_form_data(file_path, **rest)
|
|
84
|
+
path = Pathname.new(file_path)
|
|
85
|
+
body = ''
|
|
86
|
+
boundary = SecureRandom.hex(8)
|
|
87
|
+
|
|
88
|
+
rest.compact.each do |field_name, field_value|
|
|
89
|
+
body << "--#{boundary}\r\n"
|
|
90
|
+
body << "Content-Disposition: form-data; name=\"#{field_name}\"\r\n\r\n"
|
|
91
|
+
body << "#{field_value}\r\n"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
body << "--#{boundary}" << Excon::CR_NL
|
|
95
|
+
body << %{Content-Disposition: form-data; name="file"; filename="#{path.basename}"} << Excon::CR_NL
|
|
96
|
+
body << 'Content-Type: audio/wav' << Excon::CR_NL
|
|
97
|
+
body << Excon::CR_NL
|
|
98
|
+
body << path.binread
|
|
99
|
+
body << Excon::CR_NL
|
|
100
|
+
body << "--#{boundary}--" << Excon::CR_NL
|
|
101
|
+
|
|
102
|
+
rest.symbolize_keys_recursive.compact | {
|
|
103
|
+
headers: { 'Content-Type' => %{multipart/form-data; boundary="#{boundary}"} },
|
|
104
|
+
expects: 200,
|
|
105
|
+
body:,
|
|
106
|
+
}
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
data/lib/ollama_chat/chat.rb
CHANGED
|
@@ -162,15 +162,14 @@ class OllamaChat::Chat
|
|
|
162
162
|
# @return [OllamaChat::RedisCache, nil] the cache instance
|
|
163
163
|
attr_reader :cache
|
|
164
164
|
|
|
165
|
-
|
|
166
165
|
# Returns the voice handler class based on the configuration.
|
|
167
166
|
#
|
|
168
167
|
# This method resolves the voice handler class specified in the config.
|
|
169
|
-
# If running within RSpec, it returns
|
|
168
|
+
# If running within RSpec, it returns nil to avoid side effects.
|
|
170
169
|
#
|
|
171
|
-
# @return [Class,
|
|
170
|
+
# @return [Class, NilClass] the voice handler class or nil if in test mode
|
|
172
171
|
def voice_handler
|
|
173
|
-
defined?(RSpec) and return
|
|
172
|
+
defined?(RSpec) and return nil
|
|
174
173
|
case config.voice.handler
|
|
175
174
|
when 'OllamaChat::TTS'
|
|
176
175
|
OllamaChat::TTS
|
|
@@ -295,6 +294,30 @@ class OllamaChat::Chat
|
|
|
295
294
|
content
|
|
296
295
|
end
|
|
297
296
|
|
|
297
|
+
# Synthesizes and plays a short voice notification using the session's
|
|
298
|
+
# current voice.
|
|
299
|
+
#
|
|
300
|
+
# This is the chat-level facade for one-shot speech: it resolves the
|
|
301
|
+
# active voice, instantiates the configured voice handler, and delegates
|
|
302
|
+
# to its `#speak` method. It is a no-op when the voice switch is off,
|
|
303
|
+
# no voice is selected, or the handler lacks `#speak`.
|
|
304
|
+
#
|
|
305
|
+
# @param text [String] the text to speak
|
|
306
|
+
# @param background [Boolean, Integer] pass `true` for an immediate
|
|
307
|
+
# background thread, `false` for inline, or a numeric delay in
|
|
308
|
+
# seconds (e.g. `10`) to spawn a background thread that sleeps
|
|
309
|
+
# before speaking
|
|
310
|
+
#
|
|
311
|
+
# @return [OllamaChat::TTS, OllamaChat::Say, nil] the voice handler
|
|
312
|
+
# instance when a background thread was spawned (so callers can call
|
|
313
|
+
# `cancel_speaking`), or `nil` for inline playback or when voice is off
|
|
314
|
+
def speak(text, background: true)
|
|
315
|
+
voice.on? or return
|
|
316
|
+
voice = session.current_voice.full? or return
|
|
317
|
+
voice_handler.method_defined?(:speak) or return
|
|
318
|
+
voice_handler.new(chat: self, voice:).speak(text, background:)
|
|
319
|
+
end
|
|
320
|
+
|
|
298
321
|
# Returns a human-readable string representation of the Chat object,
|
|
299
322
|
# including the class name and the base URL.
|
|
300
323
|
#
|
data/lib/ollama_chat/commands.rb
CHANGED
|
@@ -433,27 +433,30 @@ module OllamaChat::Commands
|
|
|
433
433
|
|
|
434
434
|
command(
|
|
435
435
|
name: :prompt,
|
|
436
|
-
regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|rename|sync|-e))?(\s+(?:-[ef]|-c\s+(?:\w+|\?)))?(?:\s+([^-].*))?$),
|
|
437
|
-
complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset rename sync ] ],
|
|
436
|
+
regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset count|reset|rename|sync|-e))?(\s+(?:-[ef]|-c\s+(?:\w+|\?)))?(?:\s+([^-].*))?$),
|
|
437
|
+
complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset\ count reset rename sync ] ],
|
|
438
438
|
optional: true,
|
|
439
439
|
options: '[-c CONTEXT|-e|-f]',
|
|
440
440
|
help: <<~EOT,
|
|
441
441
|
📝 Manage prompt templates:
|
|
442
442
|
Subcommands: edit, info, add, delete, list,
|
|
443
|
-
duplicate, import, export, reset,
|
|
444
|
-
sync.
|
|
443
|
+
duplicate, import, export, reset count, reset,
|
|
444
|
+
rename, sync.
|
|
445
445
|
Options: -c [context]
|
|
446
446
|
(? for interactive in /prompt),
|
|
447
447
|
-e (edit next)
|
|
448
448
|
EOT
|
|
449
449
|
) do |subcommand, opts, filename|
|
|
450
450
|
opts = go_command('fc:', opts)
|
|
451
|
-
context =
|
|
451
|
+
context = case subcommand
|
|
452
|
+
when nil, '-e'
|
|
452
453
|
if opts[?c] == ??
|
|
453
454
|
choose_prompt_context
|
|
454
455
|
else
|
|
455
456
|
opts[?c] || 'prompt'
|
|
456
457
|
end
|
|
458
|
+
when 'reset count'
|
|
459
|
+
'prompt'
|
|
457
460
|
else
|
|
458
461
|
opts[?c] || choose_prompt_context
|
|
459
462
|
end
|
|
@@ -481,6 +484,8 @@ module OllamaChat::Commands
|
|
|
481
484
|
export_prompt(context:)
|
|
482
485
|
when 'info'
|
|
483
486
|
info_prompt(context:)
|
|
487
|
+
when 'reset count'
|
|
488
|
+
choose_and_reset_count(context:)
|
|
484
489
|
when 'reset'
|
|
485
490
|
if prompt = choose_prompt(
|
|
486
491
|
default: true,
|
|
@@ -498,8 +503,9 @@ module OllamaChat::Commands
|
|
|
498
503
|
prompt_sync(context:)
|
|
499
504
|
when nil, '-e'
|
|
500
505
|
if prompt = choose_prompt(
|
|
501
|
-
prompt:
|
|
502
|
-
context:
|
|
506
|
+
prompt: 'Which template shall guide the next response? %s',
|
|
507
|
+
context: ,
|
|
508
|
+
count: true
|
|
503
509
|
).full?(&:to_s)
|
|
504
510
|
if subcommand
|
|
505
511
|
prompt = edit_text(prompt)
|
|
@@ -820,38 +826,43 @@ module OllamaChat::Commands
|
|
|
820
826
|
|
|
821
827
|
command(
|
|
822
828
|
name: :input,
|
|
823
|
-
regexp: %r(^/input(?:\s+(path|context|embedding|summary)(?:\s*(?=\z))?)?((?:\s+-(?:[
|
|
829
|
+
regexp: %r(^/input(?:\s+(path|context|embedding|summary)(?:\s*(?=\z))?)?((?:\s+-(?:[apreim]|c\s*#{OllamaChat::COLLECTION_NAME_REGEXP.source}|w\s*\d+|t\s*[-\w\.]+(?:,[-\w\.]+)*|l\s*[-\w]+))*)(?:\s+([^-].*))?$),
|
|
824
830
|
optional: true,
|
|
825
831
|
complete: [ 'input', %w[ path context embedding summary ] ],
|
|
826
832
|
options: <<~EOT,
|
|
827
833
|
[
|
|
828
|
-
|
|
834
|
+
-w|-a|-p|-e|-m|-i|
|
|
829
835
|
-c <collection>|
|
|
830
|
-
-t <tags
|
|
836
|
+
-t <tags>|
|
|
837
|
+
-l <language>
|
|
831
838
|
]
|
|
832
839
|
[arg…]
|
|
833
840
|
EOT
|
|
834
841
|
help: <<~EOT
|
|
835
842
|
📥 Import content (read, summarize, embed, context)
|
|
836
843
|
Subcommands: path, context, embedding, summary
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
844
|
+
Options: -p (pattern), -w [words], -a (all),
|
|
845
|
+
-c [collection], -t [tags], -e (edit),
|
|
846
|
+
-m (monochrome), -i (summary instruction),
|
|
847
|
+
-l [language]
|
|
840
848
|
EOT
|
|
841
849
|
) do |input_mode,opts,arg|
|
|
842
850
|
disable_content_parsing
|
|
843
851
|
case input_mode
|
|
844
852
|
when 'summary'
|
|
845
|
-
opts = go_command('paw:', opts)
|
|
853
|
+
opts = go_command('paw:i', opts)
|
|
854
|
+
instruction = if opts[?i]
|
|
855
|
+
ask?(prompt: 'Summary instruction (Enter to skip): ').full?(:strip)
|
|
856
|
+
end
|
|
846
857
|
if opts[?p]
|
|
847
858
|
words = opts.fetch(?w, 100)
|
|
848
859
|
all = opts.fetch(?a, false)
|
|
849
860
|
patterns = extract_patterns(arg)
|
|
850
|
-
next provide_file_set_content(patterns, all:, skip_blank: true) { summarize(_1, words:) } || :next
|
|
861
|
+
next provide_file_set_content(patterns, all:, skip_blank: true) { summarize(_1, words:, instruction:) } || :next
|
|
851
862
|
elsif arg
|
|
852
863
|
words = opts.fetch(?w, 100)
|
|
853
864
|
source = arg
|
|
854
|
-
next summarize(source, words:) || :next
|
|
865
|
+
next summarize(source, words:, instruction:) || :next
|
|
855
866
|
else
|
|
856
867
|
STDERR.puts "Need a source to summarize for input!"
|
|
857
868
|
next :next
|
|
@@ -915,17 +926,17 @@ module OllamaChat::Commands
|
|
|
915
926
|
next :next
|
|
916
927
|
end
|
|
917
928
|
else
|
|
918
|
-
opts = go_command('
|
|
929
|
+
opts = go_command('paeml:', opts)
|
|
919
930
|
if opts[?p]
|
|
920
931
|
all = opts.fetch(?a, false)
|
|
921
932
|
patterns = extract_patterns(arg)
|
|
922
933
|
next provide_file_set_content(patterns, all:, skip_blank: true) do |src|
|
|
923
|
-
content = import(src)
|
|
934
|
+
content = import(src, language: opts[?l])
|
|
924
935
|
opts[?m] ? OllamaChat::Utils::StripANSI.strip_ansi(content) : content
|
|
925
936
|
end || :next
|
|
926
937
|
elsif arg
|
|
927
938
|
source = arg
|
|
928
|
-
content = import(source) or next :next
|
|
939
|
+
content = import(source, language: opts[?l]) or next :next
|
|
929
940
|
content = OllamaChat::Utils::StripANSI.strip_ansi(content) if opts[?m]
|
|
930
941
|
content = edit_text(content) if opts[?e]
|
|
931
942
|
content
|
|
@@ -89,7 +89,7 @@ class OllamaChat::Database::Models::Session < Sequel::Model(OllamaChat::DB)
|
|
|
89
89
|
# @return [String, nil] A JSON-serialized string containing default settings for tools.
|
|
90
90
|
#
|
|
91
91
|
# @!attribute [v] think_mode
|
|
92
|
-
# @return [String] The current thinking mode (must be one of `
|
|
92
|
+
# @return [String] The current thinking mode (must be one of `Chat#think_mode_states`).
|
|
93
93
|
#
|
|
94
94
|
# @!attribute [v] think_loud_enabled
|
|
95
95
|
# @return [Boolean] Whether thinking annotations are displayed in the output.
|
data/lib/ollama_chat/dialog.rb
CHANGED
|
@@ -19,10 +19,13 @@ module OllamaChat::Dialog
|
|
|
19
19
|
old_pre_input_hook = Reline.pre_input_hook
|
|
20
20
|
Reline.pre_input_hook = -> { Reline.insert_text prefill.to_s }
|
|
21
21
|
end
|
|
22
|
+
speaker = speak(prompt, background: 10)
|
|
22
23
|
Reline.readline(prompt, true)&.chomp
|
|
24
|
+
|
|
23
25
|
rescue Interrupt
|
|
24
26
|
return nil
|
|
25
27
|
ensure
|
|
28
|
+
speaker&.cancel_speaking
|
|
26
29
|
prefill and Reline.pre_input_hook = old_pre_input_hook
|
|
27
30
|
end
|
|
28
31
|
|
|
@@ -46,6 +49,7 @@ module OllamaChat::Dialog
|
|
|
46
49
|
if prompt.include?('%s')
|
|
47
50
|
prompt = prompt % (timeout ? ('timeout in %us' % timeout) : 'no timeout')
|
|
48
51
|
end
|
|
52
|
+
speaker = speak(prompt, background: 10) if timeout.nil?
|
|
49
53
|
output.print prompt
|
|
50
54
|
min = 1
|
|
51
55
|
time = 0
|
|
@@ -86,6 +90,8 @@ module OllamaChat::Dialog
|
|
|
86
90
|
end
|
|
87
91
|
nil
|
|
88
92
|
end
|
|
93
|
+
ensure
|
|
94
|
+
speaker&.cancel_speaking
|
|
89
95
|
end
|
|
90
96
|
|
|
91
97
|
private
|
|
@@ -72,4 +72,29 @@ module OllamaChat::HTTPHandling
|
|
|
72
72
|
&block
|
|
73
73
|
)
|
|
74
74
|
end
|
|
75
|
+
|
|
76
|
+
# Performs an HTTP request with configuration-aware options.
|
|
77
|
+
#
|
|
78
|
+
# Merges the caller-supplied options (body, headers, etc.) with the
|
|
79
|
+
# HTTP options generated by `http_options` (SSL verification, proxy)
|
|
80
|
+
# and sends the request via Excon.
|
|
81
|
+
#
|
|
82
|
+
# @param method [Symbol] the HTTP method (`:get` or `:post`).
|
|
83
|
+
# @param url [String] the target URL.
|
|
84
|
+
# @param options [Hash] request options (e.g. `:body`, `:headers`).
|
|
85
|
+
# @yield [response] yields the Excon response object when a block is
|
|
86
|
+
# given.
|
|
87
|
+
# @return [Excon::Response, Object] the response object, or the block's
|
|
88
|
+
# return value if a block was provided.
|
|
89
|
+
def request_url_response(method, url, **options, &block)
|
|
90
|
+
normalized_url = OllamaChat::Utils::Fetcher.normalize_url(url)
|
|
91
|
+
options = http_options(normalized_url) | options
|
|
92
|
+
excon = Excon.new(url, options)
|
|
93
|
+
response = excon.request(method:)
|
|
94
|
+
if block
|
|
95
|
+
block.(response)
|
|
96
|
+
else
|
|
97
|
+
response
|
|
98
|
+
end
|
|
99
|
+
end
|
|
75
100
|
end
|
|
@@ -97,8 +97,14 @@ module OllamaChat::Information
|
|
|
97
97
|
# (defaults to STDOUT).
|
|
98
98
|
def info_model(output: STDOUT)
|
|
99
99
|
output.puts "🧠 Current chat model is #{bold{@model}}."
|
|
100
|
-
output.puts " Capabilities: #{Array(@model_metadata&.capabilities) * ', '}"
|
|
101
100
|
output.puts " Families: #{Array(@model_metadata&.families) * ', '}"
|
|
101
|
+
output.puts " Capabilities: #{Array(@model_metadata&.capabilities) * ', '}"
|
|
102
|
+
if @model_metadata&.thinking
|
|
103
|
+
thinking = @model_metadata.thinking
|
|
104
|
+
levels = Array(thinking.values).map { normalize_think_state(_1) }
|
|
105
|
+
default = normalize_think_state(thinking.default)
|
|
106
|
+
output.puts " Thinking: #{levels * ', '} (default: #{bold{default}})"
|
|
107
|
+
end
|
|
102
108
|
|
|
103
109
|
profiles = models::ModelOptions.where(model_name: @model).order(:profile).all
|
|
104
110
|
if profiles.full?
|