ollama_chat 0.0.95 → 0.0.97

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +106 -0
  3. data/README.md +115 -2
  4. data/Rakefile +1 -1
  5. data/lib/ollama_chat/chat.rb +34 -17
  6. data/lib/ollama_chat/commands.rb +27 -15
  7. data/lib/ollama_chat/database/migrations/005_add_context_format_to_sessions.rb +5 -0
  8. data/lib/ollama_chat/database/models/model_options.rb +1 -0
  9. data/lib/ollama_chat/database/models/prompt.rb +1 -3
  10. data/lib/ollama_chat/database/models/session.rb +18 -0
  11. data/lib/ollama_chat/follow_chat.rb +4 -5
  12. data/lib/ollama_chat/information.rb +2 -1
  13. data/lib/ollama_chat/input_content.rb +1 -1
  14. data/lib/ollama_chat/message_list.rb +2 -4
  15. data/lib/ollama_chat/ollama_chat_config/default_config.yml +2 -1
  16. data/lib/ollama_chat/parsing.rb +10 -6
  17. data/lib/ollama_chat/personae_management.rb +25 -11
  18. data/lib/ollama_chat/prompt_management.rb +25 -11
  19. data/lib/ollama_chat/session_management.rb +24 -30
  20. data/lib/ollama_chat/source_fetching.rb +1 -1
  21. data/lib/ollama_chat/state_selectors.rb +14 -0
  22. data/lib/ollama_chat/system_prompt_management.rb +21 -0
  23. data/lib/ollama_chat/think_control.rb +0 -1
  24. data/lib/ollama_chat/token_estimator/crude.rb +25 -0
  25. data/lib/ollama_chat/token_estimator.rb +36 -0
  26. data/lib/ollama_chat/tool_calling.rb +1 -1
  27. data/lib/ollama_chat/tools/compute_bmi.rb +6 -4
  28. data/lib/ollama_chat/tools/execute_grep.rb +11 -1
  29. data/lib/ollama_chat/tools/file_context.rb +3 -2
  30. data/lib/ollama_chat/tools/generate_password.rb +4 -1
  31. data/lib/ollama_chat/tools/get_current_weather.rb +11 -0
  32. data/lib/ollama_chat/tools/get_time.rb +13 -2
  33. data/lib/ollama_chat/tools/open_file_in_editor.rb +3 -3
  34. data/lib/ollama_chat/tools/read_file.rb +3 -0
  35. data/lib/ollama_chat/tools/retrieve_document_snippets.rb +18 -11
  36. data/lib/ollama_chat/tools/roll_dice.rb +52 -11
  37. data/lib/ollama_chat/tools/run_tests.rb +23 -11
  38. data/lib/ollama_chat/tools/write_file.rb +2 -6
  39. data/lib/ollama_chat/utils/fetcher.rb +2 -2
  40. data/lib/ollama_chat/utils/png_metadata_extractor.rb +5 -4
  41. data/lib/ollama_chat/utils.rb +0 -1
  42. data/lib/ollama_chat/version.rb +1 -1
  43. data/lib/ollama_chat.rb +1 -0
  44. data/ollama_chat.gemspec +6 -6
  45. data/spec/ollama_chat/database/models/favourite_spec.rb +7 -20
  46. data/spec/ollama_chat/server_socket_spec.rb +11 -20
  47. data/spec/ollama_chat/source_fetching_spec.rb +1 -1
  48. data/spec/ollama_chat/state_selectors_spec.rb +4 -9
  49. data/spec/ollama_chat/switches_spec.rb +1 -1
  50. data/spec/ollama_chat/token_estimator_spec.rb +41 -0
  51. data/spec/ollama_chat/tools/compute_bmi_spec.rb +14 -5
  52. data/spec/ollama_chat/tools/copy_to_clipboard_spec.rb +5 -5
  53. data/spec/ollama_chat/tools/execute_grep_spec.rb +10 -0
  54. data/spec/ollama_chat/tools/generate_image_spec.rb +8 -8
  55. data/spec/ollama_chat/tools/generate_password_spec.rb +13 -0
  56. data/spec/ollama_chat/tools/get_current_weather_spec.rb +4 -0
  57. data/spec/ollama_chat/tools/get_endoflife_spec.rb +3 -3
  58. data/spec/ollama_chat/tools/get_rfc_spec.rb +2 -2
  59. data/spec/ollama_chat/tools/get_time_spec.rb +4 -0
  60. data/spec/ollama_chat/tools/get_url_spec.rb +7 -7
  61. data/spec/ollama_chat/tools/paste_from_clipboard_spec.rb +4 -4
  62. data/spec/ollama_chat/tools/read_file_spec.rb +3 -0
  63. data/spec/ollama_chat/tools/retrieve_document_snippets_spec.rb +38 -5
  64. data/spec/ollama_chat/tools/roll_dice_spec.rb +4 -4
  65. data/spec/ollama_chat/tools/run_tests_spec.rb +5 -5
  66. data/spec/ollama_chat/tools/search_web_spec.rb +3 -3
  67. data/spec/ollama_chat/tools/write_file_spec.rb +2 -0
  68. metadata +11 -5
  69. data/lib/ollama_chat/utils/token_estimator.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5ae8b473c0dfaba1476f7890f8d1e04244db7f19c9dd9d563a845d9a5798a5f
4
- data.tar.gz: 6e0f053ce8c5f9bbe97d5e04d3819fbee2ef9857f6b4c9d6bc4d76fe7e12dffa
3
+ metadata.gz: 83e62a371bea41c14f117fc546d3acf8e8a22d6c7f3be71a06f5dbb9f76a0201
4
+ data.tar.gz: 1348fecdb529bd12784e3843fcee09228d422db9f82a06554e6cf1923d318971
5
5
  SHA512:
6
- metadata.gz: 9e514fe65a9e3ccddef894cfe92fdff0e17a1c9ca639994ab54edcd573b219cca6c50774640cd966e8adc4d3232071abacc193c8dd795f9ed6be6460109b62df
7
- data.tar.gz: 681c439ba46ee38cce5201696e40dda4dc6f6d2291c505e420c7fa2d63899ffd0e82068e94c6f8509f170e6843480924c969730c328a3c6a92b00702115de994
6
+ metadata.gz: f53884d1050759f8d32731f872e3746f92fc3b9747ecfdb12015bafc39cd98dd1e3fc977c831f116a7b95ece70fd8fc2cbfc760803d06207a2cf49584138a6de
7
+ data.tar.gz: 78144c20b98504852ce74ce33709b9a5592865d9fc75c49edc5cb23ce93a767275d5f090c38d77450be6e503b5b37cdbe895650b25f0e07f91cb118d8a7f59dc
data/CHANGES.md CHANGED
@@ -1,5 +1,111 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-07-06 v0.0.97
4
+
5
+ ### New Features
6
+
7
+ - **Session-Specific Context Formatting**:
8
+ - Introduced the `/context_format` command to manage context formats per
9
+ session.
10
+ - Added a database migration `005_add_context_format_to_sessions.rb` to
11
+ include the `context_format` column in the `sessions` table.
12
+ - Updated `OllamaChat::Database::Models::Session` with presence validations
13
+ and initialization for `context_format`.
14
+ - Implemented a `context_format` state selector using
15
+ `DatabaseStateSelector` in `lib/ollama_chat/state_selectors.rb`.
16
+ - Integrated session-based format selection into
17
+ `OllamaChat::InputContent#context_spook` and
18
+ `OllamaChat::Tools::FileContext#execute`.
19
+ - **Persona Management**:
20
+ - Added a `copy` subcommand to the `/persona` command.
21
+ - Updated `select_persona_path` in `lib/ollama_chat/personae_management.rb`
22
+ to support a `no_prefill` keyword argument, affecting `@prefill_prompt`
23
+ assignment logic.
24
+
25
+ ### Improvements & Refactorings
26
+
27
+ - **Generation API**:
28
+ - Refactored `OllamaChat::Chat#generate` to utilize `ollama.chat`,
29
+ returning a string directly and supporting an optional `system` prompt.
30
+ - Implemented `OllamaChat::SystemPromptManagement#raw_system_prompt` for
31
+ resolving active system prompts.
32
+ - Enhanced `OllamaChat::PromptManagement#suggest_prompts` with an `edit:`
33
+ parameter and improved formatting.
34
+ - **Embedding & Import Logic**:
35
+ - Updated the `embed` prompt template in `default_config.yml` to include
36
+ `%{source}`.
37
+ - Modified `#embed` in `lib/ollama_chat/source_fetching.rb` to pass the
38
+ `source` variable during interpolation.
39
+ - Added `disable_content_parsing` to `'update'` and `'embedding'` command
40
+ paths in `lib/ollama_chat/commands.rb` to prevent recursive imports of
41
+ system status messages.
42
+ - **Documentation & UX**:
43
+ - Expanded `README.md` with detailed guides for `/input` and a new "Power
44
+ User Features" section (covering `/character`, `/prompt suggest`,
45
+ `/model` profiles, and `/session summarize`).
46
+ - Corrected the help text flag in `lib/ollama_chat/information.rb` from
47
+ `-s` to `-l`.
48
+
49
+ ### Bug Fixes & Maintenance
50
+
51
+ - Added `validates_presence :profile` to the `validate` method in
52
+ `lib/ollama_chat/database/models/model_options.rb`.
53
+ - Ensured character names are cast to strings in `lib/ollama_chat/parsing.rb`
54
+ for type consistency.
55
+ - Updated `/prompt` command regex and argument logic in
56
+ `lib/ollama_chat/commands.rb`.
57
+
58
+ ## 2026-07-03 v0.0.96
59
+
60
+ ### New Features
61
+
62
+ - Added reroll capability to the `roll_dice` tool, including a new `reroll`
63
+ configuration option in `default_config.yml` and implementation of a reroll
64
+ loop using `chat.confirm?`.
65
+ - Enhanced the `roll_dice` tool output to include `min` and `max` bounds in the
66
+ JSON response and result range in the summary message.
67
+ - Improved character importing by implementing `import_persona_from_json` in
68
+ `PersonaeManagement`, removing temporary file dependencies.
69
+ - Added the ability to offer new sessions during session selection via
70
+ `offer_new_session: true` in `SessionManagement`.
71
+
72
+ ### Improvements
73
+
74
+ - **Tool Messaging**: Added or enhanced human-readable summary `message` fields
75
+ for several tools to improve user experience:
76
+ - `GetTime`: Now includes time-of-day greetings and formatted weekdays.
77
+ - `GetCurrentWeather`: Now incorporates temperature, conditions, and daily
78
+ forecasts.
79
+ - `ReadFile` & `WriteFile`: Integrated `OllamaChat::TokenEstimator` for
80
+ formatted byte and token counts.
81
+ - `GeneratePassword`: Added summaries covering various alphabet types and
82
+ errors.
83
+ - `RetrieveDocumentSnippets`: Added counts and collection details.
84
+ - `ExecuteGrep`: Added logic to distinguish between found matches and blank
85
+ results.
86
+ - `RunTests`: Added emoji-based result summaries and removed reliance on
87
+ the global `$?` variable.
88
+ - `ComputeBMI`: Enhanced summary messages and simplified calculation logic.
89
+ - **Character Personalization**:
90
+ - Updated `convert_json_character_to_markdown` to replace `{{char}}`
91
+ placeholders with actual names.
92
+ - Enhanced `personalize_character_profile` using case-insensitive regexes
93
+ for `{{user}} and {{char}}`.
94
+ - Refactored `PNGMetadataExtractor.decode_character` to return a tuple of
95
+ the decoded string and parsed JSON data.
96
+ - **Token Estimation**:
97
+ - Relocated `TokenEstimator` to a primary domain module and introduced an
98
+ `Estimate` struct for formatted bytes and tokens.
99
+ - Implemented `OllamaChat::TokenEstimator::Crude` as the estimation engine.
100
+
101
+ ### Refactoring & Maintenance
102
+
103
+ - Updated Ruby syntax across the project to use shorthand hash syntax (e.g., in
104
+ `.where`, object initialization, and method calls).
105
+ - Improved compatibility by replacing the `it` parameter with `_1` in the
106
+ `tools` method within `ToolCalling`.
107
+ - Bumped `context_spook` dependency from **1.5** to **1.6**.
108
+
3
109
  ## 2026-06-29 v0.0.95
4
110
 
5
111
  ### New Features & Enhancements
data/README.md CHANGED
@@ -103,7 +103,120 @@ connect can be configured in the environment variable `OLLAMA_CHAT_MODEL`.
103
103
  The YAML config file is stored in `$XDG_CONFIG_HOME/ollama_chat/config.yml` and
104
104
  you can use it for more complex settings.
105
105
 
106
- ### Example: Using a Persona
106
+ ### The `/input` Command: Advanced Context Injection
107
+
108
+ The `/input` command is a versatile tool designed to bring external data into
109
+ your conversation context. Instead of manually copying and pasting, `/input`
110
+ allows you to target files, URLs, or even live system output using powerful
111
+ filters.
112
+
113
+ #### 1. Understanding "Sources"
114
+
115
+ One of the most powerful aspects of `/input` is its definition of a **source**.
116
+ A source isn't just a filename; it can be any of the following:
117
+
118
+ * **Local Files**: Standard paths (e.g., `lib/ollama_chat.rb`) or quoted paths
119
+ for those with spaces.
120
+ * **Remote URLs**: Any `http://` or `https://` link. The command will fetch the
121
+ web content and parse it before importing.
122
+ * **Shell Commands (`!`)**: Any source starting with an exclamation mark is
123
+ executed as a shell command, and its **STDOUT** is imported as the content.
124
+ * *Example*: `/input !ls -la` imports the current directory listing.
125
+ * *Example*: `/input !git branch` tells the AI which branch you are on.
126
+
127
+ #### 2. Subcommands
128
+
129
+ Depending on how you want the AI to "see" the data, you can use different
130
+ subcommands:
131
+
132
+ * **`import` (Default)**: The standard way to bring content in. It reads the
133
+ source and formats it for the chat.
134
+ * **`path`**: Specifically targets local files to read their raw content.
135
+ * **`summary`**: Instead of importing the whole file, this uses the LLM to
136
+ generate a concise summary first. Use `-w <words>` to control the length
137
+ (default: 100).
138
+ * **`embedding`**: Sends the content directly into your RAG vector database
139
+ rather than the immediate chat window. Use `-c <collection>` for specific
140
+ collections and `-t <tags>` to categorize the data.
141
+ * **`context`**: Uses `ContextSpook` to generate a comprehensive structural dump
142
+ of your project, providing both the architectural skeleton and full file
143
+ contents in a high-density format.
144
+
145
+ #### 3. The Power of Patterns (`-p`)
146
+
147
+ By adding the `-p` flag, you can use Unix-style globs to select multiple files
148
+ at once.
149
+
150
+ * **Interactive Selection**: By default, when using patterns, `ollama_chat` will
151
+ present an interactive menu allowing you to pick exactly which matching
152
+ files you want to import.
153
+ * **Bulk Import (`-a`)**: Use the `-a` (all) flag alongside `-p` to bypass the
154
+ menu and import every file that matches the pattern automatically.
155
+
156
+ #### 4. Refining the Input
157
+
158
+ * **Editing on the Fly (`-e`)**: For the `import` and `path` subcommands, you
159
+ can use the `-e` flag. This opens the content in your default editor,
160
+ allowing you to trim or modify the text before it is sent to the LLM.
161
+ * **Custom Tags**: When embedding, tags allow you to create "slices" of
162
+ knowledge that can be retrieved later based on specific categories.
163
+
164
+ #### Examples
165
+
166
+ | Goal | Command |
167
+ | :--- | :--- |
168
+ | **Import a single file** | `/input path lib/ollama_chat.rb` |
169
+ | **Input the output of a command** | `/input !env` |
170
+ | **Summarize multiple files (Interactive)** | `/input summary -p "spec/**/*_spec.rb"` |
171
+ | **Bulk import all Ruby files in `lib/`** | `/input -p "lib/**/*.rb" -a` |
172
+ | **Embed a doc into a specific collection** | `/input embedding -c documentation-v1 "docs/api.md"` |
173
+ | **Import and edit a URL's content** | `/input -e https://example.com/guide` |
174
+ | **Generate project context for the AI** | `/input context` |
175
+
176
+ ### 🚀 Power User Features
177
+
178
+ Beyond basic chat, **ollama_chat** includes several advanced systems designed
179
+ for high-end AI orchestration and personalization.
180
+
181
+ #### 1. The Character & Persona Engine
182
+ The bot supports a sophisticated roleplay system that goes beyond simple text prompts.
183
+
184
+ * **SillyTavern Compatibility (`/character`)**: You can import character
185
+ cards directly from PNG or JSON files. This supports the SillyTavern
186
+ standard, allowing you to load entire personalities—including metadata and
187
+ example dialogues—embedded within an image file.
188
+ * *Example*: `/character import fluffy.png`
189
+ * **Persona Management (`/persona`)**: Create, duplicate, and export complex
190
+ persona profiles. You can use `/persona play` to activate a personality as
191
+ your default for the session.
192
+
193
+ #### 2. Intelligent Prompt Engineering (`/prompt suggest`)
194
+
195
+ Instead of guessing the right prompt, you can let the AI help you write one.
196
+ The `suggest` subcommand analyzes your current session history and utilizes
197
+ specialized strategies (like `suggest_coding` or `suggest_roleplaying`) to
198
+ generate a tailored prompt template for you to refine.
199
+
200
+ #### 3. Model Profile Orchestration (`/model`)
201
+
202
+ Avoid the tedious task of re-configuring model parameters every time you
203
+ switch. The profile system allows you to manage "Live" vs "Saved" settings:
204
+ * **Saved -> Live**: Apply a pre-configured profile's settings (temperature,
205
+ top\_p, etc.) to your current session using `options to session`.
206
+ * **Live -> Saved**: After fine-tuning your model's behavior during a chat,
207
+ "bake" those live settings into a permanent profile using `options from
208
+ session`.
209
+
210
+ #### 4. Conversation Archiving (`/session summarize`)
211
+
212
+ Turn your long-form brainstorming sessions into structured documents. The
213
+ `/session summarize` command can distill an entire conversation into a concise
214
+ summary.
215
+ * **Quick View**: Use `-s` for a one-sentence executive summary.
216
+ * **Archiving**: Use `-f <filename>` to dump the full summary directly into a
217
+ Markdown file, creating an automatic journal of your project's evolution.
218
+
219
+ ### Using a Persona
107
220
 
108
221
  You can import a persona profile from a Markdown file and load it into the
109
222
  current session. The persona's content is then interpolated into the system
@@ -140,7 +253,7 @@ seems to be the problem that has brought you to our humble abode at 221B Baker
140
253
  Street?
141
254
  ```
142
255
 
143
- ### Example: Using a multimodal model
256
+ ### Using a multimodal model
144
257
 
145
258
  This example shows how an image like this can be sent to the LLM for multimodal
146
259
  analysis:
data/Rakefile CHANGED
@@ -56,7 +56,7 @@ GemHadar do
56
56
  dependency 'bigdecimal', '~> 3.1'
57
57
  dependency 'csv', '~> 3.0'
58
58
  dependency 'const_conf', '~> 0.3'
59
- dependency 'context_spook', '~> 1.5'
59
+ dependency 'context_spook', '~> 1.6'
60
60
  dependency 'infobar', '>= 0.13.1'
61
61
  dependency 'rubyzip', '~> 3.0'
62
62
  dependency 'sequel', '~> 5.0'
@@ -33,6 +33,7 @@ require 'context_spook'
33
33
  class OllamaChat::Chat
34
34
  include Tins::GO
35
35
  include Term::ANSIColor
36
+ include OllamaChat::TokenEstimator
36
37
  include OllamaChat::HTTPHandling
37
38
  include OllamaChat::Commands
38
39
  include OllamaChat::Logging
@@ -229,23 +230,39 @@ class OllamaChat::Chat
229
230
  @messages.system_name
230
231
  end
231
232
 
232
- # The generate method sends a prompt to the Ollama model and returns the
233
- # result.
234
- #
235
- # @param prompt [ String ] the prompt to send to the model
236
- #
237
- # @return [ Ollama::Response ] the response from the Ollama model
238
- def generate(prompt:)
239
- prepare_model(@model)
240
- msg = "Using prompt #{prompt.inspect} for generation to #{@model.inspect}."
241
- log(:info, msg)
242
- ollama.generate(
243
- model: @model,
244
- prompt:,
245
- options: model_options,
246
- stream: false,
247
- think: false,
248
- )
233
+ # Sends a structured chat request to the Ollama model and returns the
234
+ # response content.
235
+ #
236
+ # This method creates a minimal conversation consisting of a system message
237
+ # and a user message, executing it as a one-shot chat interaction.
238
+ #
239
+ # @param system [String] the system prompt to guide the model's behavior
240
+ # (defaults to current raw_system_prompt)
241
+ #
242
+ # @param prompt [String] the user prompt to send to the model
243
+ #
244
+ # @return [String] the content of the resulting response message
245
+ def generate(system: raw_system_prompt, prompt:)
246
+ messages = [
247
+ OllamaChat::Message.new(
248
+ role: 'system',
249
+ content: system,
250
+ ),
251
+
252
+ OllamaChat::Message.new(
253
+ role: 'user',
254
+ content: prompt
255
+ ),
256
+ ]
257
+
258
+ ollama.chat(
259
+ model: @model,
260
+ messages: ,
261
+ options: model_options,
262
+ stream: false,
263
+ think: false,
264
+ tools:
265
+ )&.message&.content.to_s
249
266
  end
250
267
 
251
268
  private
@@ -234,6 +234,15 @@ module OllamaChat::Commands
234
234
  :next
235
235
  end
236
236
 
237
+ command(
238
+ name: :context_format,
239
+ regexp: %r(^/context_format$),
240
+ help: 'Configure the context_format for context_spook JSON|TOON',
241
+ ) do
242
+ context_format.choose
243
+ :next
244
+ end
245
+
237
246
  command(
238
247
  name: :tools,
239
248
  regexp: %r(^/tools(?:\s+(on|off|enable|disable))?),
@@ -481,7 +490,7 @@ module OllamaChat::Commands
481
490
 
482
491
  command(
483
492
  name: :prompt,
484
- regexp: %r(^/prompt(\s+-e)?(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|suggest))?(?:\s+(\S+))?$),
493
+ regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|suggest|-e))?(\s+-e)?(?:\s+(\S+))?$),
485
494
  complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset suggest ] ],
486
495
  optional: true,
487
496
  help: <<~EOT,
@@ -495,7 +504,7 @@ module OllamaChat::Commands
495
504
  Options: -e to edit the next prompt
496
505
  instead of prefilling
497
506
  EOT
498
- ) do |opts, subcommand, filename|
507
+ ) do |subcommand, opts, filename|
499
508
  case subcommand
500
509
  when 'add'
501
510
  add_new_prompt
@@ -526,11 +535,11 @@ module OllamaChat::Commands
526
535
  end
527
536
  end
528
537
  when 'suggest'
529
- prompt = suggest_prompts and next prompt
530
- when nil
531
- opts = go_command('e', opts)
538
+ opts = go_command('e', opts)
539
+ prompt = suggest_prompts(edit: opts[?e]) and next prompt
540
+ when nil, '-e'
532
541
  if prompt = choose_prompt(prompt: 'Which template shall guide the next response? %s').full?(&:to_s)
533
- if opts[?e]
542
+ if subcommand
534
543
  prompt = edit_text(prompt)
535
544
  next prompt
536
545
  else
@@ -595,7 +604,10 @@ module OllamaChat::Commands
595
604
  when 'rename'
596
605
  rename_collection(collection)
597
606
  when 'update'
598
- results = update_collection and next results
607
+ if results = update_collection
608
+ disable_content_parsing
609
+ next results
610
+ end
599
611
  when nil
600
612
  collection_stats
601
613
  end
@@ -606,8 +618,8 @@ module OllamaChat::Commands
606
618
 
607
619
  command(
608
620
  name: :persona,
609
- regexp: %r(^/persona(?:\s+(play|load|edit|info|list|add|delete|backup|import|export|duplicate))?$),
610
- complete: [ 'persona', %w[ play load edit info list add delete backup import export duplicate ] ],
621
+ regexp: %r(^/persona(?:\s+(play|load|edit|info|list|add|delete|backup|import|export|duplicate|copy))?$),
622
+ complete: [ 'persona', %w[ play load edit info list add delete backup import export duplicate copy ] ],
611
623
  optional: true,
612
624
  help: <<~EOT,
613
625
  Manage and activate personae for roleplay:
@@ -616,6 +628,7 @@ module OllamaChat::Commands
616
628
  and start roleplaying
617
629
  - load: Load a specific persona
618
630
  into session
631
+ - copy: persona pathname to clipboard
619
632
  - Management:
620
633
  - add: Create a new persona
621
634
  - edit: Modify an existing persona
@@ -670,6 +683,9 @@ module OllamaChat::Commands
670
683
  when 'play'
671
684
  set_default_persona
672
685
  :next
686
+ when 'copy'
687
+ select_persona_path(no_prefill: true)
688
+ :next
673
689
  else
674
690
  select_persona_path
675
691
  :next
@@ -724,12 +740,7 @@ module OllamaChat::Commands
724
740
  disable_content_parsing
725
741
  data
726
742
  when 'import'
727
- markdown = convert_json_character_to_markdown(data)
728
- Tempfile.create('character.md') do |tmp|
729
- tmp.puts markdown
730
- tmp.flush
731
- import_persona(Pathname.new(tmp.path))
732
- end
743
+ import_persona_from_json(data)
733
744
  :next
734
745
  end
735
746
  end
@@ -810,6 +821,7 @@ module OllamaChat::Commands
810
821
  next context_spook(nil) || :next
811
822
  end
812
823
  when 'embedding'
824
+ disable_content_parsing
813
825
  opts = go_command('pac:t:', opts)
814
826
  switch_collection(opts[?c]) do |other_collection|
815
827
  if collection == other_collection and !confirm?(
@@ -0,0 +1,5 @@
1
+ Sequel.migration do
2
+ change do
3
+ add_column :sessions, :context_format, String, null: false, default: 'JSON'
4
+ end
5
+ end
@@ -12,6 +12,7 @@ class OllamaChat::Database::Models::ModelOptions < Sequel::Model(OllamaChat::DB)
12
12
  super
13
13
  validates_presence :model_name
14
14
  validates_unique [ :model_name, :profile ]
15
+ validates_presence :profile
15
16
  end
16
17
 
17
18
  # @!attribute [v] id
@@ -52,9 +52,7 @@ class OllamaChat::Database::Models::Prompt < Sequel::Model(OllamaChat::DB)
52
52
  # database when the underlying prompt is removed.
53
53
  def after_destroy
54
54
  super
55
- OllamaChat::Database::Models::Favourite.
56
- where(context: context, name: name).
57
- destroy
55
+ OllamaChat::Database::Models::Favourite.where(context:, name:).destroy
58
56
  end
59
57
 
60
58
  # Seeds the prompt table from the provided chat configuration.
@@ -25,6 +25,20 @@ class OllamaChat::Database::Models::Session < Sequel::Model(OllamaChat::DB)
25
25
  validates_unique :name
26
26
  validates_presence :updated_at
27
27
  validates_presence :created_at
28
+
29
+ validates_presence :context_format
30
+ validates_presence :current_voice
31
+ validates_presence :document_policy
32
+ validates_presence :embedding_enabled
33
+ validates_presence :location_enabled
34
+ validates_presence :markdown_enabled
35
+ validates_presence :runtime_info_enabled
36
+ validates_presence :stream_enabled
37
+ validates_presence :think_loud_enabled
38
+ validates_presence :think_mode
39
+ validates_presence :think_strip_enabled
40
+ validates_presence :tools_enabled
41
+ validates_presence :voice_enabled
28
42
  end
29
43
 
30
44
  # Calculates the age of the session based on the last update timestamp.
@@ -93,6 +107,9 @@ class OllamaChat::Database::Models::Session < Sequel::Model(OllamaChat::DB)
93
107
  # @!attribute [v] current_voice
94
108
  # @return [String] The name of the voice currently in use.
95
109
  #
110
+ # @!attribute [v] context_format
111
+ # @return [String] The format used for project context generation (e.g., 'JSON', 'TOON').
112
+ #
96
113
  # @!attribute [v] working_directory
97
114
  # @return [String] The directory used as the working context for this session.
98
115
  #
@@ -144,6 +161,7 @@ class OllamaChat::Database::Models::Session < Sequel::Model(OllamaChat::DB)
144
161
  voice_enabled: chat.config.voice.enabled,
145
162
  model_options: ,
146
163
  current_voice: chat.config.voice.default,
164
+ context_format: chat.config.context.format,
147
165
  working_directory: Dir.pwd,
148
166
  messages: '',
149
167
  }
@@ -165,7 +165,7 @@ class OllamaChat::FollowChat
165
165
  STDOUT.printf(
166
166
  "\n%s Execution of tool %s confirmed.\n\n", symbol, bold { name }
167
167
  )
168
- result = OllamaChat::Tools.registered[name].execute(tool_call, chat: chat)
168
+ result = OllamaChat::Tools.registered[name].execute(tool_call, chat:)
169
169
  if confirmed == :explicit
170
170
  chat.log(:info, "Execution of tool %s was explicitly confirmed." % name)
171
171
  else
@@ -191,14 +191,13 @@ class OllamaChat::FollowChat
191
191
  false
192
192
  end
193
193
 
194
- size_bytes = result.to_s.size
195
- tokens = OllamaChat::Utils::TokenEstimator.estimate(size_bytes)
194
+ es = OllamaChat::TokenEstimator.estimate(result.to_s)
196
195
 
197
196
  tools_used[name] = {
198
197
  message:,
199
198
  warn: ,
200
- size: format_bytes(size_bytes),
201
- tokens: format_tokens(tokens),
199
+ size: es.bytes_formatted,
200
+ tokens: es.tokens_formatted,
202
201
  duration: Tins::Duration.new(Time.now - start).to_s,
203
202
  }
204
203
  end
@@ -142,6 +142,7 @@ module OllamaChat::Information
142
142
  output.print ' '; voices.show(output:)
143
143
  end
144
144
  output.print ' '; location.show(output:)
145
+ output.print ' '; context_format.show(output:)
145
146
  end
146
147
 
147
148
  # Displays the current runtime environment details, split into static
@@ -232,7 +233,7 @@ module OllamaChat::Information
232
233
  -V display the current version number and quit
233
234
  -h this help
234
235
 
235
- Use `?selector` with `-m` or `-s` to filter options. Multiple matches
236
+ Use `?selector` with `-m` or `-l` to filter options. Multiple matches
236
237
  will open a chooser dialog.
237
238
  EOT
238
239
  0
@@ -92,7 +92,7 @@ module OllamaChat::InputContent
92
92
  # @example Load default context
93
93
  # context_spook(nil)
94
94
  def context_spook(patterns, all: false)
95
- format = config.context.format
95
+ format = @context_format.selected
96
96
  myself = self
97
97
  if patterns
98
98
  ContextSpook::generate_context(verbose: true, format:) do |context|
@@ -380,9 +380,7 @@ class OllamaChat::MessageList
380
380
  def show_system_prompt
381
381
  current_system = system.to_s
382
382
  size_bytes = current_system.size
383
- size = format_bytes(size_bytes)
384
- tokens = OllamaChat::Utils::TokenEstimator.estimate(size_bytes)
385
- tokens_size = format_tokens(tokens)
383
+ es = OllamaChat::TokenEstimator.estimate(size_bytes)
386
384
  system_prompt = @chat.kramdown_ansi_parse(current_system).
387
385
  gsub(/\n+\z/, '').full?
388
386
  if system_prompt.blank?
@@ -398,7 +396,7 @@ class OllamaChat::MessageList
398
396
  #{system_prompt}
399
397
 
400
398
  System prompt name: #{bold{system_name}}
401
- System prompt length: 👾#{size} 🧩#{tokens_size}
399
+ System prompt length: 👾#{es.bytes_formatted} 🧩#{es.tokens_formatted}
402
400
  EOT
403
401
  end
404
402
  self
@@ -24,7 +24,7 @@ infobar:
24
24
  :frames: :braille181
25
25
  :message: ✓
26
26
  prompts:
27
- embed: This source has been added to or updated in collection "%{collection}".
27
+ embed: The source %{source} has been added to or updated in collection "%{collection}".
28
28
  rerank: |
29
29
  Query: %{query}
30
30
  Candidates:
@@ -402,6 +402,7 @@ tools:
402
402
  timeout_duration: 66
403
403
  roll_dice:
404
404
  default: true
405
+ reroll: false
405
406
  result_display_timeout: 10
406
407
  get_ghr:
407
408
  default: true
@@ -76,9 +76,10 @@ module OllamaChat::Parsing
76
76
  results = []
77
77
 
78
78
  if data = metadata.delete('chara') and
79
- char = OllamaChat::Utils::PNGMetadataExtractor.decode_character(data)
79
+ (char, char_data = OllamaChat::Utils::PNGMetadataExtractor.decode_character(data))
80
80
  then
81
- results << "Character Profile:\n\n#{personalize_character_profile(char)}"
81
+ name = char_data.dig('data', 'name').full? || Pathname.new(source_io.path).basename.sub_ext('').to_s
82
+ results << "Character Profile:\n\n#{personalize_character_profile(char, name:)}"
82
83
  end
83
84
 
84
85
  if data = metadata.delete('parameters') and
@@ -222,13 +223,16 @@ module OllamaChat::Parsing
222
223
  )
223
224
  end
224
225
 
225
- # Personalizes a character profile by replacing the {{user}} placeholder.
226
+ # Personalizes a character profile by replacing placeholders with actual names.
226
227
  #
227
228
  # @param char [String] The raw character JSON string.
229
+ # @param name [String] The name of the character to replace {{char}} with.
228
230
  # @return [String] The personalized character profile.
229
- def personalize_character_profile(char)
230
- name = user_name || 'the user'
231
- char.gsub('{{user}}', name)
231
+ def personalize_character_profile(char, name:)
232
+ my_user_name = user_name || 'the user'
233
+ char = char.gsub(/{{user}}/i, my_user_name)
234
+ name.present? and char = char.gsub(/{{char}}/i, name)
235
+ char
232
236
  end
233
237
 
234
238
  # Regular expression to scan content for url/file references