ollama_chat 0.0.121 → 0.0.123

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +211 -0
  3. data/README.md +67 -11
  4. data/Rakefile +2 -2
  5. data/lib/ollama_chat/asr.rb +110 -0
  6. data/lib/ollama_chat/chat.rb +27 -4
  7. data/lib/ollama_chat/commands.rb +36 -26
  8. data/lib/ollama_chat/conversation.rb +3 -2
  9. data/lib/ollama_chat/database/models/session.rb +1 -1
  10. data/lib/ollama_chat/dialog.rb +6 -0
  11. data/lib/ollama_chat/http_handling.rb +25 -0
  12. data/lib/ollama_chat/information.rb +7 -1
  13. data/lib/ollama_chat/invidious.rb +162 -0
  14. data/lib/ollama_chat/kramdown_ansi.rb +22 -11
  15. data/lib/ollama_chat/model_handling.rb +23 -2
  16. data/lib/ollama_chat/oc.rb +53 -0
  17. data/lib/ollama_chat/ollama_chat_config/default_config.yml +4 -1
  18. data/lib/ollama_chat/parsing.rb +54 -11
  19. data/lib/ollama_chat/personae_management.rb +1 -1
  20. data/lib/ollama_chat/prompt_management.rb +66 -2
  21. data/lib/ollama_chat/redis_cache.rb +1 -1
  22. data/lib/ollama_chat/say.rb +4 -0
  23. data/lib/ollama_chat/session_management.rb +28 -39
  24. data/lib/ollama_chat/source_fetching.rb +20 -11
  25. data/lib/ollama_chat/speaker.rb +70 -0
  26. data/lib/ollama_chat/state_selectors.rb +35 -11
  27. data/lib/ollama_chat/think_control.rb +39 -9
  28. data/lib/ollama_chat/tools/compute_bmi.rb +2 -2
  29. data/lib/ollama_chat/tools/execute_grep.rb +4 -0
  30. data/lib/ollama_chat/tools/generate_image.rb +5 -5
  31. data/lib/ollama_chat/tools/get_url.rb +29 -4
  32. data/lib/ollama_chat/tools/patch_file.rb +5 -1
  33. data/lib/ollama_chat/tts.rb +25 -22
  34. data/lib/ollama_chat/utils/audio_player.rb +1 -1
  35. data/lib/ollama_chat/utils/fetcher.rb +40 -10
  36. data/lib/ollama_chat/utils/png_metadata_extractor.rb +1 -0
  37. data/lib/ollama_chat/utils.rb +7 -7
  38. data/lib/ollama_chat/version.rb +1 -1
  39. data/lib/ollama_chat.rb +2 -0
  40. data/ollama_chat.gemspec +7 -7
  41. data/spec/assets/linux.oga +0 -0
  42. data/spec/ollama_chat/asr_spec.rb +89 -0
  43. data/spec/ollama_chat/commands_spec.rb +122 -20
  44. data/spec/ollama_chat/conversation_spec.rb +1 -1
  45. data/spec/ollama_chat/http_handling_spec.rb +145 -0
  46. data/spec/ollama_chat/invidious_spec.rb +238 -0
  47. data/spec/ollama_chat/kramdown_ansi_spec.rb +60 -0
  48. data/spec/ollama_chat/message_list_spec.rb +1 -2
  49. data/spec/ollama_chat/model_handling_spec.rb +42 -0
  50. data/spec/ollama_chat/parsing_spec.rb +61 -2
  51. data/spec/ollama_chat/session_management_spec.rb +26 -15
  52. data/spec/ollama_chat/source_fetching_spec.rb +32 -0
  53. data/spec/ollama_chat/tools/directory_structure_spec.rb +1 -1
  54. data/spec/ollama_chat/tools/get_url_spec.rb +58 -19
  55. data/spec/ollama_chat/tts_spec.rb +11 -13
  56. data/spec/ollama_chat/utils/audio_player_spec.rb +4 -4
  57. data/spec/spec_helper.rb +1 -1
  58. metadata +30 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e457507428f39cb5f1641783d067f1424a1dca8908cc30e8331d05e141c68f2e
4
- data.tar.gz: 9c14299158dec444c3debd7ba133678c04324a6985f26110ce779da37bdf295a
3
+ metadata.gz: f2d0c5676e03433f245a21795872a9d7bd64357306ec691de3012195b5403bef
4
+ data.tar.gz: 933dca2b53647c523b9849d958cc1625106c97500caf2814bc731458a0e683f4
5
5
  SHA512:
6
- metadata.gz: eafc7e3075b48d10dc9ad7ac1654cdb5af4e2c7b235cf2ff4e8ad4240518c77afc02167689c831987b25c9ac07d68f2a758d6d85d9bc4a1bad4537decc01ef26
7
- data.tar.gz: 9492a500da2006ef57212e0f1a68444e35d94be81ad2a5abbd041daf73d9a3087af9873d36264182c77a7b76d3825697eaea45189dd69b227178df569c558c23
6
+ metadata.gz: 4589b5f1cf689bb35bdf26c288e59f40b87ef926eaf7f91e187c3fa729958c9d042f784eb3bf9a4b6e64966c201312713c2e20607291c6aabc10d306cf4cf272
7
+ data.tar.gz: 19102c3d01a2ebb4bb14b8ea617b09a7e168d0175225024a490dc85e23b41ce3c7eba249d82cc671f5530da0090e945f8f6358d17ca7f6b7587a413002e27dc2
data/CHANGES.md CHANGED
@@ -1,5 +1,216 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-09-25 v0.0.123
4
+
5
+ ### Added
6
+
7
+ * README section 5 documenting `/conversation compact`: token-aware
8
+ keep-recent budget, safe & retryable behavior, visible before/after
9
+ metrics, and iterative re-compaction.
10
+ * Three-step context escalation ladder in the README: `/conversation
11
+ compact` first, then `/conversation report` + `/clear` + `/input path`
12
+ as the preferred second step, and `/conversation clean` as the emergency
13
+ exit.
14
+ * `execute_shell` tool to the Filesystem row in the README, noted as running
15
+ shell commands as a last resort.
16
+ * `execute_jira_twg` tool to the Web/External row in the README for running
17
+ Jira CLI queries via `twg`.
18
+ * `lookup_group` tool to the Knowledge row in the README for retrieving prior
19
+ conversation groups by UUID.
20
+ * Speech Services (ASR) section documenting `OLLAMA_CHAT_ASR_URL`,
21
+ `OLLAMA_CHAT_ASR_CONVERT_COMMAND`, and `OLLAMA_CHAT_ASR_MODEL`.
22
+ * Audio Playback section documenting `OLLAMA_CHAT_AUDIO_PLAYER_CONFIG`.
23
+ * Invidious (YouTube Proxy) section documenting `OLLAMA_CHAT_INVIDIOUS_URL`,
24
+ `OLLAMA_CHAT_INVIDIOUS_COMPANION_KEY`, and
25
+ `OLLAMA_CHAT_INVIDIOUS_CAPTION_LANGUAGES`.
26
+ * `markdown:` keyword argument (default `true`) to
27
+ `kramdown_markdown_remove`; when `false`, skips the `kramdown_ansi_parse`
28
+ step while still removing emoji, ANSI, and box-drawing characters.
29
+ * Explanatory comment block to `eval_template` in
30
+ `lib/ollama_chat/tools/execute_grep.rb` clarifying that the `cmd` config
31
+ value is a Ruby string template whose `#{...}` interpolations are resolved
32
+ against the method's local parameters via `eval`.
33
+ * `cleaned` string prefix in `save_conversation` so the `STDOUT`/`STDERR`
34
+ confirmation messages read "Saved cleaned conversation to…" when `clean:
35
+ true` is passed.
36
+ * "Cancelled." message when the user skips saving in conversation report and
37
+ summarize flows.
38
+
39
+ ### Changed
40
+
41
+ * Renamed `report_session` to `generate_conversation_report` to clarify it
42
+ only builds the report text.
43
+ * Reworked `report_conversation` to always display in the pager first, then
44
+ optionally save to a file; removed the `save:` keyword argument.
45
+ * Reworked `summarize_conversation` in `session_management.rb` to always
46
+ render via `use_pager` first, then optionally save via `ask_for_filename?`
47
+ and `should_overwrite?`.
48
+ * Removed the `save:` keyword argument from `summarize_conversation` and the
49
+ `-f` flag from the `/conversation` command dispatch in `commands.rb`.
50
+ * Tightened the `/conversation` regex from `(?:[sf]|c)` to `[sc]` and updated
51
+ the `options:` help text to drop `-f save`.
52
+ * Changed "Nothing to summarize!" and "Nothing to report!" output from
53
+ `STDERR` to `STDOUT`.
54
+ * `kramdown_markdown_remove` now strips `\p{Extended_Pictographic}` emoji
55
+ before any further processing, preventing TTS engines from vocalizing them.
56
+ * `kramdown_markdown_remove` in `lib/ollama_chat/kramdown_ansi.rb` now strips
57
+ Unicode box-drawing characters (`U+2500`..`U+257F`) that kramdown renders
58
+ as table borders, preventing TTS engines from stammering on them.
59
+ * Collapses resulting multi-space runs and strips leading/trailing whitespace
60
+ in `kramdown_markdown_remove`.
61
+ * Moved the `kramdown_markdown_remove` call from `flush_buffer` into
62
+ `enqueue_tts` in `lib/ollama_chat/tts.rb` so all enqueue paths are covered,
63
+ passing `markdown: @chat.markdown.on?`.
64
+ * `multipart_form_data` in `lib/ollama_chat/asr.rb` now returns a hash
65
+ containing only `headers:`, `expects:`, and `body:`, which are the actual
66
+ Excon request options, instead of merging `rest` (`model:`, `language:`)
67
+ into the returned options hash.
68
+ * Updated YARD documentation to reflect the new parameter and emoji stripping
69
+ behavior.
70
+ * Updated `spec/ollama_chat/asr_spec.rb` expectations from
71
+ `hash_including(model: ...)` / `hash_including(language: ...)` to
72
+ `hash_including(body: kind_of(String))` to match the new option shape.
73
+ * Updated `commands_spec.rb` to remove the obsolete `/conversation summarize
74
+ -f` and `/conversation report -f` specs and drop `save:` from expectations.
75
+ * Updated `session_management_spec.rb` for the new show-then-save flow,
76
+ `STDOUT` stream, and `Cancelled.` expectation.
77
+ * Updated the corresponding expectation in `conversation_spec.rb` to match
78
+ the new message text.
79
+ * Renamed former section 5 "Session Reports" to section 6 "Conversation
80
+ Reports" to match the actual command name.
81
+ * Updated Conversation Archiving section from `/session summarize` to
82
+ `/conversation summarize`, removed obsolete `-f` flag reference, and
83
+ described the show-then-save flow.
84
+
85
+ ### Fixed
86
+ * Fixed `TTS_URL` to `OLLAMA_CHAT_TTS_URL` to match the actual env var name
87
+ in `oc.rb`.
88
+ * Wrapped `@prefix` with `Regexp.quote()` in `RedisCache#unpre` to ensure the
89
+ prefix is always matched as a literal string.
90
+ * Fixed typos in height validation error message.
91
+
92
+ ### Removed
93
+ * `context_spook` from development dependencies in `Rakefile` and
94
+ `ollama_chat.gemspec`.
95
+ * The `-f` flag from the `/report` command in `commands.rb`.
96
+
97
+ ## 2026-09-25 v0.0.122
98
+
99
+ ## Features
100
+
101
+ * **Per-model thinking-state support**: Added support for per-model thinking
102
+ states via the `/api/show` endpoint.
103
+ * Replaced the `THINK_MODE_STATES` constant with a `think_mode_states`
104
+ method that reads `@model_metadata&.thinking&.full?(:values)` to return
105
+ a per-model subset, falling back to the full superset for
106
+ pre-**0.34.3** Ollama or non-thinking models.
107
+ * Added `normalize_think_state` helper to canonicalize boolean
108
+ (`true`/`false`) and string (`"low"`/`"high"`/`"max"`) thinking values.
109
+ * Added `:thinking` field to `ModelMetadata` data class.
110
+ * Implemented think-mode clamping in `use_model` to disable thinking for
111
+ non-thinking models or clamp to the model's default when the current
112
+ level is unavailable.
113
+ * Converted `DatabaseStateSelector` `states`/`off` to support `Proc` via
114
+ `ask_and_send_or_self(:call, @chat)`.
115
+ * Converted `think_mode` and `voices` selectors in
116
+ `setup_state_selectors` to lambda-based `states:`.
117
+ * Added `Thinking` display line to `info_model`.
118
+ * **Invidious YouTube captions**: Added `OllamaChat::Invidious` to fetch
119
+ video metadata and transcripts from an Invidious companion instance.
120
+ * Uses `POST /companion/youtubei/v1/player` for `videoDetails` and
121
+ `captionTracks`.
122
+ * Selects the best caption track by priority-ordered language regexes.
123
+ * Fetches timedtext XML and extracts `<text>` elements via `Nokogiri`,
124
+ decoding double-encoded HTML entities.
125
+ * Returns a formatted markdown header + transcript as a text IO.
126
+ * **Unified HTTP handling**: Added `HTTPHandling#request_url_response` to
127
+ consolidate `Excon.new` call sites, merging `http_options` (SSL, proxy)
128
+ with caller-supplied options.
129
+ * Updated `ASR.transcribe`, `TTS.voices`, `TTS#fetch_tts`,
130
+ `GenerateImage#post_url`, `GenerateImage#get_url`, `GetURL#execute`,
131
+ `Invidious.fetch_video_info`, and `SourceFetching#fetch_source` to
132
+ route through this method.
133
+ * Extracted audio/video MIME regex into `HAS_AUDIO` constant shared by
134
+ `parse_source` and the `get_url` tool.
135
+ * Split `Fetcher` `middlewares` into class-level `self.middlewares` and
136
+ instance delegate; added `RedirectFollower` to the default stack.
137
+ * Added `middlewares:` to streaming `excon` requests in `fetcher.rb`.
138
+ * **One-shot voice notification**: Added `OllamaChat::Speaker` concern
139
+ providing `speak(text, background:)` and `cancel_speaking`.
140
+ * `OllamaChat::TTS` and `OllamaChat::Say` now include
141
+ `OllamaChat::Speaker`.
142
+ * Added `Chat#speak` facade to resolve the configured voice handler.
143
+ * `Dialog#readline_prompt` and `Dialog#confirm?` now speak the prompt
144
+ with a 10-second delay.
145
+ * `Tools::PatchFile` announces "You can review the patch for the file …
146
+ now!" before opening `vimdiff`.
147
+ * `AudioPlayer#start` redirects `ffplay` stdout/stderr to `/dev/null`.
148
+ * **ASR transcription pipeline**: Added `OllamaChat::ASR` client for
149
+ audio/video transcription.
150
+ * Uploads audio/video as multipart form-data to the audio.cpp
151
+ `/v1/audio/transcriptions` endpoint.
152
+ * Converts video sources to 16 kHz mono PCM WAV via a configurable
153
+ `ffmpeg` command (`CONVERT_COMMAND`).
154
+ * Added `OC::OLLAMA::CHAT::ASR` config module with `URL`,
155
+ `CONVERT_COMMAND`, and `MODEL` settings.
156
+ * Extended `/input` command with `-l <language>` option.
157
+ * Routed `audio/*`, `video/*`, and specific `application/` MIME types
158
+ into `parse_audio`.
159
+ * Added `Fetcher::ResponseMetadata.as_text(text)` factory for synthetic
160
+ `text/plain` IO responses.
161
+ * Replaced hardcoded timeouts in `lib/ollama_chat/tts.rb` with values
162
+ from `config.timeouts`.
163
+ * **Audio/Video support in `get_url`**: Added `language` parameter to the
164
+ `get_url` tool definition for optional transcription hints.
165
+ * Pre-transcribes `audio/*` and `video/*` sources via `chat.parse_audio`
166
+ before content-type dispatch.
167
+ * Threads `language:` kwarg into `chat.import_source`.
168
+ * **Prompt management enhancements**:
169
+ * Added `reset_prompt_count` and `choose_and_reset_count` to
170
+ `lib/ollama_chat/prompt_management.rb`.
171
+ * Added `/prompt reset count` command.
172
+ * Added `count:` keyword argument to `choose_prompt` to increment
173
+ `metadata['use_count']`.
174
+ * Changed `all_prompts` sort to `use_count` descending, falling back to
175
+ name for tie-breaking.
176
+ * Added `-i` summary instruction flag to `/input` command to prompt for
177
+ an optional instruction.
178
+ * Added `instruction` parameter to `get_url` summarizing, threading it
179
+ through `summarize_source` and `summarize`.
180
+ * **Config-driven default persona**: Added `persona.default_name` to
181
+ `default_config.yml`.
182
+ * Updated `initial_persona_name` in `personae_management.rb` to fall back
183
+ to `config.persona&.default_name`.
184
+
185
+ ## Improvements
186
+
187
+ * **CI and Dependencies**:
188
+ * Added `ffmpeg` to the CI Docker image.
189
+ * Added `nokogiri ~> 1.0` dependency.
190
+ * Bumped `tins ~> 1.58`.
191
+ * Added `require 'ollama_chat/utils/utf8_converter'` in
192
+ `png_metadata_extractor.rb`.
193
+ * Reorganized `require` statements in `lib/ollama_chat/utils.rb`
194
+ alphabetically.
195
+ * **Error Handling**:
196
+ * In `lib/ollama_chat/asr.rb`, capture the return value of `system` to
197
+ detect missing executables and print a clear "not found in PATH"
198
+ message.
199
+ * Extract executable name via `Shellwords.split(cmd).first` for error
200
+ messages.
201
+ * **Documentation**:
202
+ * Updated `ollama_chat` description in README to be more concise and
203
+ user-focused.
204
+ * Updated YARD in `session.rb`, `state_selectors.rb`, and
205
+ `think_control.rb` to reference `Chat#think_mode_states`.
206
+
207
+ ## Bug Fixes
208
+
209
+ * **Pager Spec**: Changed `expect(Tins::Terminal).to receive(:lines)` to
210
+ `allow(Tins::Terminal).to receive(:lines)` in
211
+ `spec/ollama_chat/message_list_spec.rb` and removed the `skip 'no tty'`
212
+ guard.
213
+
3
214
  ## 2026-09-18 v0.0.121
4
215
 
5
216
  * 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, that can be used to connect to an ollama
6
- server and enter chat conversations with the LLMs provided by it.
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
 
@@ -62,7 +62,7 @@ The following environment variables can be used to configure behavior:
62
62
  - `OLLAMA_URL` - Base URL for Ollama server (default: `http://localhost:11434`)
63
63
  - `OLLAMA_HOST` - Base hostname for Ollama server (default: `localhost:11434`)
64
64
  - `OLLAMA_SEARXNG_URL` - SearxNG search endpoint URL
65
- - `TTS_URL` - Base URL for the [audio.cpp](https://github.com/0xshug0/audio.cpp) TTS server (default: `http://localhost:8880`)
65
+ - `OLLAMA_CHAT_TTS_URL` - Base URL for the [audio.cpp](https://github.com/0xshug0/audio.cpp) TTS server (default: `http://localhost:8880`)
66
66
 
67
67
  #### Chat Settings
68
68
  - `OLLAMA_CHAT_MODEL` - Default model to use (e.g., `llama3.1`)
@@ -75,6 +75,19 @@ The following environment variables can be used to configure behavior:
75
75
  - `OLLAMA_CHAT_LOG_DATABASE` - Database log file path (default: `$XDG_STATE_HOME/database.log`)
76
76
  - `OLLAMA_CHAT_LOG_TAIL_LINES` - Max lines to retain in log files (default: `10000`)
77
77
 
78
+ #### Speech Services (ASR)
79
+ - `OLLAMA_CHAT_ASR_URL` - Base URL for the ASR (Speech-to-Text) service (default: `http://localhost:8880`)
80
+ - `OLLAMA_CHAT_ASR_CONVERT_COMMAND` - Shell command template to convert video to 16 kHz mono PCM WAV; use `%{input}` / `%{output}` as placeholders (default: `ffmpeg -y -i %{input} -vn -acodec pcm_s16le -ar 16000 -ac 1 %{output}`)
81
+ - `OLLAMA_CHAT_ASR_MODEL` - ASR model identifier (default: `qwen3-asr-1.7b`)
82
+
83
+ #### Audio Playback
84
+ - `OLLAMA_CHAT_AUDIO_PLAYER_CONFIG` - JSON object with `command`, `frequency`, `bits`, `pause` keys controlling the ffplay-based TTS audio player
85
+
86
+ #### Invidious (YouTube Proxy)
87
+ - `OLLAMA_CHAT_INVIDIOUS_URL` - Base URL for an Invidious instance
88
+ - `OLLAMA_CHAT_INVIDIOUS_COMPANION_KEY` - Bearer token for the Invidious companion player API (required when `URL` is set)
89
+ - `OLLAMA_CHAT_INVIDIOUS_CAPTION_LANGUAGES` - JSON array of language-code regexes for caption selection priority (default: `["\\Aen\\z","\\Aen-","\\Ade\\z","\\Ade-"]`)
90
+
78
91
  #### Tool Configuration
79
92
  - `OLLAMA_CHAT_TOOLS_TEST_RUNNER` - Configured test runner for `run_tests` (default: `rspec`)
80
93
  - `OLLAMA_CHAT_TOOLS_CTAGS_TOOL` - Path to the ctags tool
@@ -243,14 +256,57 @@ switch. The profile system allows you to manage "Live" vs "Saved" settings:
243
256
  "bake" those live settings into a permanent profile using `options from
244
257
  session`.
245
258
 
246
- #### 4. Conversation Archiving (`/session summarize`)
259
+ #### 4. Conversation Archiving (`/conversation summarize`)
247
260
 
248
261
  Turn your long-form brainstorming sessions into structured documents. The
249
- `/session summarize` command can distill an entire conversation into a concise
250
- summary.
262
+ `/conversation summarize` command distills an entire conversation into a
263
+ concise summary, displayed in the pager first, then optionally saved.
251
264
  * **Quick View**: Use `-s` for a one-sentence executive summary.
252
- * **Archiving**: Use `-f <filename>` to dump the full summary directly into a
253
- Markdown file, creating an automatic journal of your project's evolution.
265
+ * **Archiving**: After the summary is displayed, you are prompted for a
266
+ filename to save it as a Markdown file, creating an automatic journal
267
+ of your project's evolution.
268
+
269
+ #### 5. Context Compaction (`/conversation compact`)
270
+
271
+ Long sessions inevitably fill up the model's context window. The
272
+ `/conversation compact` command summarizes older messages into a single
273
+ compact summary while preserving recent exchanges, freeing up space for
274
+ continued work.
275
+
276
+ * **Token-aware**: The keep-recent budget is derived from the active
277
+ model's context length, so it adapts automatically.
278
+ * **Safe & retryable**: If the LLM summarization fails, the message list
279
+ is left untouched and you can simply retry.
280
+ * **Visible metrics**: After compaction, a report shows summarized
281
+ message count, summary size, and context usage before/after.
282
+ * **Iterative**: Repeated compactions merge into the existing summary
283
+ rather than stacking multiple summary messages.
284
+
285
+ Run it whenever context usage climbs into the red zone (visible in the
286
+ runtime info gauge) — the model stays sharp and the conversation keeps
287
+ flowing.
288
+
289
+ If compaction alone isn't enough, a practical escalation ladder:
290
+
291
+ 1. **`/conversation compact`** — summarize old messages, keep recent tail.
292
+ Try this first; it's the least destructive.
293
+ 2. **`/conversation report`** → **`/clear`** → **`/input path
294
+ standup.md`** — generate a standup-style report, save it, clear
295
+ the messages, then feed the report back as context. Do this
296
+ before `clean`, because `clean` strips the tool output and
297
+ thinking that make a good report.
298
+ 3. **`/conversation clean`** — strips tool results, images, and
299
+ thinking content from all messages. More radical, but the
300
+ conversation structure and text remain intact. The emergency
301
+ exit: if context is so stuffed that even a report can't fit,
302
+ `clean` carves out enough room for anything to work again.
303
+
304
+ #### 6. Conversation Reports (`/conversation report`)
305
+
306
+ Generate a structured report from your conversation using configurable
307
+ prompt templates. The report is displayed in the pager first, then
308
+ optionally saved to a file. Particularly useful for daily standup summaries
309
+ — run it at the end of the day and save the output for your team sync.
254
310
 
255
311
  ### Using a Persona
256
312
 
@@ -467,12 +523,12 @@ context, manipulate files, and retrieve external information.
467
523
 
468
524
  | Category | Tools | Description |
469
525
  | :--- | :--- | :--- |
470
- | **Filesystem** | `read_file`, `write_file`, `patch_file`, `delete_file`, `move_file`, `directory_structure`, `execute_grep` | Read, write, and search files within allowed directories. |
526
+ | **Filesystem** | `read_file`, `write_file`, `patch_file`, `delete_file`, `move_file`, `directory_structure`, `execute_grep`, `execute_shell` | Read, write, and search files within allowed directories; run shell commands as a last resort. |
471
527
  | **Ruby/Dev** | `resolve_tag`, `execute_ri`, `gem_path_lookup`, `run_tests`, `eval_ruby` | Introspect Ruby code, check documentation, and run test suites. |
472
- | **Web/External** | `search_web`, `get_url`, `browse`, `get_rfc`, `get_cve`, `get_endoflife`, `get_ghr`, `get_jira_issue` | Access the internet, fetch specific URLs, and look up technical standards. |
528
+ | **Web/External** | `search_web`, `get_url`, `browse`, `get_rfc`, `get_cve`, `get_endoflife`, `get_ghr`, `get_jira_issue`, `execute_jira_twg` | Access the internet, fetch specific URLs, and look up technical standards; run Jira CLI queries via `twg`. |
473
529
  | **System/Util** | `get_time`, `get_location`, `get_current_weather`, `generate_password`, `compute_bmi`, `roll_dice` | General utility functions for time, location, and simple calculations. |
474
530
  | **Editor/Clip** | `copy_to_clipboard`, `paste_from_clipboard`, `paste_into_editor`, `open_file_in_editor` | Bridge the gap between the chat and the system clipboard or editor. |
475
- | **Knowledge** | `search_knowledge`, `file_context` | Semantic search for specific snippets vs. broad retrieval of structured project context. |
531
+ | **Knowledge** | `search_knowledge`, `file_context`, `lookup_group` | Semantic search for specific snippets, broad retrieval of structured project context, or retrieving prior message groups by UUID. |
476
532
  | **Multimodal** | `generate_image` | Generate images via a local ComfyUI server. |
477
533
 
478
534
  ***Note on Knowledge Tools**: Use `search_knowledge` for precise,
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.57'
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,13 +61,13 @@ 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'
67
68
  development_dependency 'webmock'
68
69
  development_dependency 'debug'
69
70
  development_dependency 'simplecov'
70
- development_dependency 'context_spook'
71
71
  development_dependency 'utils'
72
72
 
73
73
  licenses << 'MIT'
@@ -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
+ {
103
+ headers: { 'Content-Type' => %{multipart/form-data; boundary="#{boundary}"} },
104
+ expects: 200,
105
+ body: ,
106
+ }
107
+ end
108
+ end
109
+ end
110
+ end
@@ -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 an empty array to avoid side effects.
168
+ # If running within RSpec, it returns nil to avoid side effects.
170
169
  #
171
- # @return [Class, Array] the voice handler class or an empty array if in test mode
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
  #