ollama_chat 0.0.97 → 0.0.99
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 +66 -0
- data/lib/ollama_chat/chat.rb +29 -3
- data/lib/ollama_chat/commands.rb +17 -12
- data/lib/ollama_chat/message_list.rb +2 -2
- data/lib/ollama_chat/ollama_chat_config/default_config.yml +156 -24
- data/lib/ollama_chat/personae_management.rb +3 -3
- data/lib/ollama_chat/prompt_management.rb +3 -3
- data/lib/ollama_chat/tools/copy_to_clipboard.rb +6 -15
- data/lib/ollama_chat/tools/directory_structure.rb +3 -1
- data/lib/ollama_chat/version.rb +1 -1
- data/ollama_chat.gemspec +2 -2
- data/spec/ollama_chat/chat_spec.rb +1 -1
- data/spec/ollama_chat/history_spec.rb +1 -1
- data/spec/ollama_chat/tools/copy_to_clipboard_spec.rb +13 -14
- data/spec/ollama_chat/tools/execute_ri_spec.rb +1 -1
- data/spec/ollama_chat/tools/get_url_spec.rb +7 -7
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0818076703ebd6e8d8babbe5898a6a0069863ce7cebf18658f70e8d4b5592c51'
|
|
4
|
+
data.tar.gz: 7e9f768d41bff3560c2d22ac20ace84da0b580b3b17f8f9d1ffd3522af5d8db1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4864950b3246106793b51ffe74e3ead6dad4683f1b9fcaf710bf7f672264c646dc90dacdbcef437725b725230b8d0b70938045729ca982c83845ebce41bb773d
|
|
7
|
+
data.tar.gz: 5b99426668efc34bbb89a17faf2dcf016c1a6ada4c69d44882ebb40bcb836907741f96b5fb66ee99ea75b78abbdd34069ddbc186d525acd015302bcea95e55a7
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,71 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-07-13 v0.0.99
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Command Handling & UX**
|
|
8
|
+
- Updated regexes for `/system`, `/session`, `/prompt`, `/conversation`,
|
|
9
|
+
`/character`, `/input`, `/pipe`, and `/output` in
|
|
10
|
+
`lib/ollama_chat/commands.rb` to prevent positional arguments from
|
|
11
|
+
starting with a hyphen.
|
|
12
|
+
- Enhanced warning messages in `lib/ollama_chat/message_list.rb` to include
|
|
13
|
+
explicit suggestions for `/drop` and `/regenerate`.
|
|
14
|
+
|
|
15
|
+
- **LLM & Prompt Management**
|
|
16
|
+
- Updated the `generate` method in `lib/ollama_chat/chat.rb` to capture
|
|
17
|
+
output in `content` and implemented conditional logging ( `:warn` for
|
|
18
|
+
empty results, `:info` otherwise).
|
|
19
|
+
- Added `.full? or return` guard clauses to `edit_text('')` and
|
|
20
|
+
`generate(prompt: full_prompt)` within
|
|
21
|
+
`lib/ollama_chat/prompt_management.rb`.
|
|
22
|
+
- Overhauled the `suggest_coding` prompt in `default_config.yml` to adopt a
|
|
23
|
+
Senior Software Architect and Technical Auditor persona.
|
|
24
|
+
- Overhauled the `suggest_roleplaying` prompt in `default_config.yml` to
|
|
25
|
+
adopt a Narrative Consultant and Game Master's Assistant persona.
|
|
26
|
+
|
|
27
|
+
- **Tools**
|
|
28
|
+
- Modified `OllamaChat::Tools::CopyToClipboard` to make the `text`
|
|
29
|
+
parameter required.
|
|
30
|
+
- Updated `lib/ollama_chat/tools/copy_to_clipboard.rb` to raise
|
|
31
|
+
`OllamaChat::ToolFunctionArgumentError` if `text` is missing and
|
|
32
|
+
simplified the success message.
|
|
33
|
+
|
|
34
|
+
- **Refactoring & Internal Improvements**
|
|
35
|
+
- Adopted Ruby shorthand for keyword arguments in:
|
|
36
|
+
- `set_default_persona_name` and `load_personae`
|
|
37
|
+
(`lib/ollama_chat/personae_management.rb`).
|
|
38
|
+
- `choose_prompt` via the `default` parameter
|
|
39
|
+
(`lib/ollama_chat/prompt_management.rb`).
|
|
40
|
+
- `OllamaChat::Chat` instantiation using `argv` in specs.
|
|
41
|
+
- The `topic` parameter in `spec/ollama_chat/tools/execute_ri_spec.rb`.
|
|
42
|
+
- The `url` parameter in `spec/ollama_chat/tools/get_url_spec.rb`.
|
|
43
|
+
|
|
44
|
+
## 2026-07-09 v0.0.98
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- Implemented `prefill_prompt` getter and setter in `lib/ollama_chat/chat.rb`
|
|
49
|
+
with accompanying YARD documentation.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- Updated `lib/ollama_chat/commands.rb` to persist the result of
|
|
54
|
+
`suggest_prompts` as `self.prefill_prompt`, allowing prefilled prompts to be
|
|
55
|
+
stored in history for reuse and modification.
|
|
56
|
+
- Refactored call sites in `lib/ollama_chat/chat.rb`,
|
|
57
|
+
`lib/ollama_chat/commands.rb`, and `lib/ollama_chat/personae_management.rb`
|
|
58
|
+
to use the `self.prefill_prompt` interface instead of the `@prefill_prompt`
|
|
59
|
+
instance variable.
|
|
60
|
+
- Wrapped the output of the `info` subcommand in `use_pager` within
|
|
61
|
+
`lib/ollama_chat/commands.rb`.
|
|
62
|
+
- Added a confirmation message to the `import` subcommand in
|
|
63
|
+
`lib/ollama_chat/commands.rb`.
|
|
64
|
+
- Refined the description of the `directory_structure` tool in
|
|
65
|
+
`lib/ollama_chat/tools/directory_structure.rb` to include warnings regarding
|
|
66
|
+
token usage and the exponential growth of nodes relative to the `max_depth`
|
|
67
|
+
parameter.
|
|
68
|
+
|
|
3
69
|
## 2026-07-06 v0.0.97
|
|
4
70
|
|
|
5
71
|
### New Features
|
data/lib/ollama_chat/chat.rb
CHANGED
|
@@ -255,7 +255,7 @@ class OllamaChat::Chat
|
|
|
255
255
|
),
|
|
256
256
|
]
|
|
257
257
|
|
|
258
|
-
ollama.chat(
|
|
258
|
+
content = ollama.chat(
|
|
259
259
|
model: @model,
|
|
260
260
|
messages: ,
|
|
261
261
|
options: model_options,
|
|
@@ -263,6 +263,20 @@ class OllamaChat::Chat
|
|
|
263
263
|
think: false,
|
|
264
264
|
tools:
|
|
265
265
|
)&.message&.content.to_s
|
|
266
|
+
|
|
267
|
+
if content.empty?
|
|
268
|
+
log(
|
|
269
|
+
:warn,
|
|
270
|
+
"Sent #{prompt.inspect} to LLM for generation, received no content!"
|
|
271
|
+
)
|
|
272
|
+
else
|
|
273
|
+
log(
|
|
274
|
+
:info,
|
|
275
|
+
"Sent #{prompt.inspect} to LLM for generation, received #{content.inspect}"
|
|
276
|
+
)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
content
|
|
266
280
|
end
|
|
267
281
|
|
|
268
282
|
private
|
|
@@ -302,6 +316,18 @@ class OllamaChat::Chat
|
|
|
302
316
|
!!@parse_content
|
|
303
317
|
end
|
|
304
318
|
|
|
319
|
+
# Returns whether there is a prompt waiting to be prefilled into the input buffer.
|
|
320
|
+
#
|
|
321
|
+
# @return [Boolean] true if a prefill prompt exists and is not empty, false otherwise
|
|
322
|
+
def prefill_prompt
|
|
323
|
+
@prefill_prompt.full?
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Sets the content to be prefilled into the next user input prompt.
|
|
327
|
+
#
|
|
328
|
+
# @param prefill_prompt [String, nil] The text to prefill, or nil to clear it
|
|
329
|
+
attr_writer :prefill_prompt
|
|
330
|
+
|
|
305
331
|
# Handles user input commands and processes chat interactions.
|
|
306
332
|
#
|
|
307
333
|
# @param content [String] The input content to process
|
|
@@ -388,10 +414,10 @@ class OllamaChat::Chat
|
|
|
388
414
|
tools_were_called and type = :tool_input
|
|
389
415
|
unless tools_were_called
|
|
390
416
|
content = enable_command_completion do
|
|
391
|
-
if prefill_prompt
|
|
417
|
+
if prefill_prompt
|
|
392
418
|
Reline.pre_input_hook = -> {
|
|
393
419
|
Reline.insert_text prefill_prompt.gsub(/\n*\z/, '')
|
|
394
|
-
|
|
420
|
+
self.prefill_prompt = nil
|
|
395
421
|
}
|
|
396
422
|
else
|
|
397
423
|
Reline.pre_input_hook = nil
|
data/lib/ollama_chat/commands.rb
CHANGED
|
@@ -170,7 +170,7 @@ module OllamaChat::Commands
|
|
|
170
170
|
|
|
171
171
|
command(
|
|
172
172
|
name: :system,
|
|
173
|
-
regexp: %r(^/system(?:\s+(change|info|edit|add|delete|list|duplicate|export|import|reset))?(?:\s+(
|
|
173
|
+
regexp: %r(^/system(?:\s+(change|info|edit|add|delete|list|duplicate|export|import|reset))?(?:\s+([^-].*))?$),
|
|
174
174
|
complete: [ 'system', %w[ change info edit add delete list duplicate export import reset ] ],
|
|
175
175
|
optional: true,
|
|
176
176
|
help: <<~EOT
|
|
@@ -285,7 +285,7 @@ module OllamaChat::Commands
|
|
|
285
285
|
|
|
286
286
|
command(
|
|
287
287
|
name: :session,
|
|
288
|
-
regexp: %r(^/session(?:\s+(change|previous|list|new|duplicate|rename|summarize|delete|model options change|model options))?((?:\s+-(?:[sf]|p\s*\w+))*)(?:\s+(
|
|
288
|
+
regexp: %r(^/session(?:\s+(change|previous|list|new|duplicate|rename|summarize|delete|model options change|model options))?((?:\s+-(?:[sf]|p\s*\w+))*)(?:\s+([^-].*))?$),
|
|
289
289
|
complete: [ 'session', %w[ change previous list new duplicate rename summarize delete model\ options\ change model\ options ] ],
|
|
290
290
|
optional: true,
|
|
291
291
|
options: "[-s|-f|-p profile]\n[name]",
|
|
@@ -490,7 +490,7 @@ module OllamaChat::Commands
|
|
|
490
490
|
|
|
491
491
|
command(
|
|
492
492
|
name: :prompt,
|
|
493
|
-
regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|suggest|-e))?(\s+-e)?(?:\s+(
|
|
493
|
+
regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|suggest|-e))?(\s+-e)?(?:\s+([^-].*))?$),
|
|
494
494
|
complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset suggest ] ],
|
|
495
495
|
optional: true,
|
|
496
496
|
help: <<~EOT,
|
|
@@ -536,14 +536,16 @@ module OllamaChat::Commands
|
|
|
536
536
|
end
|
|
537
537
|
when 'suggest'
|
|
538
538
|
opts = go_command('e', opts)
|
|
539
|
-
prompt = suggest_prompts(edit: opts[?e])
|
|
539
|
+
if prompt = suggest_prompts(edit: opts[?e])
|
|
540
|
+
self.prefill_prompt = prompt
|
|
541
|
+
end
|
|
540
542
|
when nil, '-e'
|
|
541
543
|
if prompt = choose_prompt(prompt: 'Which template shall guide the next response? %s').full?(&:to_s)
|
|
542
544
|
if subcommand
|
|
543
545
|
prompt = edit_text(prompt)
|
|
544
546
|
next prompt
|
|
545
547
|
else
|
|
546
|
-
|
|
548
|
+
self.prefill_prompt = prompt
|
|
547
549
|
end
|
|
548
550
|
end
|
|
549
551
|
end
|
|
@@ -562,7 +564,7 @@ module OllamaChat::Commands
|
|
|
562
564
|
|
|
563
565
|
command(
|
|
564
566
|
name: :conversation,
|
|
565
|
-
regexp: %r(^/conversation\s+(save|load)((?:\s+-(?:[c]))*)\s+(
|
|
567
|
+
regexp: %r(^/conversation\s+(save|load)((?:\s+-(?:[c]))*)\s+([^-].*)$),
|
|
566
568
|
complete: [ 'conversation', %w[ save load ] ],
|
|
567
569
|
options: '[-c]',
|
|
568
570
|
help: <<~EOT
|
|
@@ -694,7 +696,7 @@ module OllamaChat::Commands
|
|
|
694
696
|
|
|
695
697
|
command(
|
|
696
698
|
name: :character,
|
|
697
|
-
regexp: %r(^/character(?:\s+(info|load|import))(?:\s+(
|
|
699
|
+
regexp: %r(^/character(?:\s+(info|load|import))(?:\s+([^-].+))?$),
|
|
698
700
|
complete: [ 'character', %w[ info load import ] ],
|
|
699
701
|
help: <<~EOT
|
|
700
702
|
Display character info, load or import a character
|
|
@@ -734,13 +736,16 @@ module OllamaChat::Commands
|
|
|
734
736
|
}
|
|
735
737
|
case subcommand
|
|
736
738
|
when 'info'
|
|
737
|
-
|
|
739
|
+
use_pager do |output|
|
|
740
|
+
output.puts json_to_yaml.(data)
|
|
741
|
+
end
|
|
738
742
|
:next
|
|
739
743
|
when 'load'
|
|
740
744
|
disable_content_parsing
|
|
741
745
|
data
|
|
742
746
|
when 'import'
|
|
743
|
-
import_persona_from_json(data)
|
|
747
|
+
persona_name = import_persona_from_json(data)
|
|
748
|
+
STDOUT.puts "Imported character as persona %s." % persona_name.to_s.inspect
|
|
744
749
|
:next
|
|
745
750
|
end
|
|
746
751
|
end
|
|
@@ -767,7 +772,7 @@ module OllamaChat::Commands
|
|
|
767
772
|
|
|
768
773
|
command(
|
|
769
774
|
name: :input,
|
|
770
|
-
regexp: %r(^/input(?:\s+(path|context|embedding|summary)(?:\s*(?=\z))?)?((?:\s+-(?:[apre]|c\s*\w+|w\s*\d+|t\s*[-\w\.]+(?:,[-\w\.]+)*))*)(?:\s+(
|
|
775
|
+
regexp: %r(^/input(?:\s+(path|context|embedding|summary)(?:\s*(?=\z))?)?((?:\s+-(?:[apre]|c\s*\w+|w\s*\d+|t\s*[-\w\.]+(?:,[-\w\.]+)*))*)(?:\s+([^-].*))?$),
|
|
771
776
|
optional: true,
|
|
772
777
|
complete: [ 'input', %w[ path context embedding summary ] ],
|
|
773
778
|
options: "[\n -w|-a|-p|-e|\n -c <collection>|\n -t <tags>\n]\n[arg…]",
|
|
@@ -886,7 +891,7 @@ module OllamaChat::Commands
|
|
|
886
891
|
|
|
887
892
|
command(
|
|
888
893
|
name: :pipe,
|
|
889
|
-
regexp: %r(^/pipe(\s+-e)?\s+(
|
|
894
|
+
regexp: %r(^/pipe(\s+-e)?\s+([^-].*)$),
|
|
890
895
|
options: 'path',
|
|
891
896
|
help: <<~EOT
|
|
892
897
|
Pipe the last response into another command's stdin.
|
|
@@ -913,7 +918,7 @@ module OllamaChat::Commands
|
|
|
913
918
|
|
|
914
919
|
command(
|
|
915
920
|
name: :output,
|
|
916
|
-
regexp: %r(^/output(\s+-e)?\s+(
|
|
921
|
+
regexp: %r(^/output(\s+-e)?\s+([^-].*)$),
|
|
917
922
|
options: '[-e] path',
|
|
918
923
|
help: <<~EOT
|
|
919
924
|
Save the last response to a file.
|
|
@@ -277,11 +277,11 @@ class OllamaChat::MessageList
|
|
|
277
277
|
)
|
|
278
278
|
msg = <<~EOT
|
|
279
279
|
|
|
280
|
-
⚠️ Last message is actually #{bold{'user message'}}, see:
|
|
280
|
+
⚠️ Last message is actually a #{bold{'user message'}}, see:
|
|
281
281
|
|
|
282
282
|
#{message_content}
|
|
283
283
|
|
|
284
|
-
You might want to drop it.
|
|
284
|
+
You might want to /drop it or /regenerate it.
|
|
285
285
|
EOT
|
|
286
286
|
output.puts msg
|
|
287
287
|
end
|
|
@@ -172,33 +172,165 @@ prompts:
|
|
|
172
172
|
|
|
173
173
|
%{content}
|
|
174
174
|
suggest_coding: |
|
|
175
|
-
|
|
176
|
-
concise follow-up prompts that the user might want to ask next to further
|
|
177
|
-
their goal.
|
|
175
|
+
## Context
|
|
178
176
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
You are acting as a Senior Software Architect and Technical Auditor. Your goal
|
|
178
|
+
is to analyze the conversation history between a developer and an AI to
|
|
179
|
+
identify technical blind spots, architectural risks, and optimization
|
|
180
|
+
opportunities. You do not just summarize; you challenge the current
|
|
181
|
+
implementation to ensure it is production-ready, scalable, and maintainable.
|
|
183
182
|
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
## Task
|
|
184
|
+
|
|
185
|
+
Analyze the provided Conversation History. Generate exactly 8 distinct, concise
|
|
186
|
+
follow-up prompts that the user could ask next to further their goal from a
|
|
187
|
+
professional engineering perspective.
|
|
188
|
+
|
|
189
|
+
Each prompt must map strictly to one of these categories:
|
|
190
|
+
|
|
191
|
+
1. **Technical Deep-Dive**: A tactical inquiry into the immediate
|
|
192
|
+
implementation details or logic flow.
|
|
193
|
+
2. **Edge Case/Optimization**: A stress test identifying where the logic
|
|
194
|
+
breaks, fails silently, or degrades in performance.
|
|
195
|
+
3. **Complexity & Scale**: An analysis of Big O, memory pressure, or
|
|
196
|
+
concurrency (e.g., "How does this behave under 100x load?").
|
|
197
|
+
4. **Maintainability & Debt**: An evaluation of clean code principles and
|
|
198
|
+
whether the current approach introduces future technical debt.
|
|
199
|
+
5. **Alternative Trade-offs**: A "Plan B" challenge suggesting a different
|
|
200
|
+
design pattern, library, or architectural approach.
|
|
201
|
+
6. **Verification & Testing**: A request for a programmatic verification
|
|
202
|
+
strategy (e.g., boundary value analysis or mocking dependencies).
|
|
203
|
+
7. **Knowledge Transfer & Documentation**: An inquiry about updating API
|
|
204
|
+
contracts, READMEs, or creating an Architectural Decision Record (ADR).
|
|
205
|
+
8. **Strategic Next Step**: A high-level roadmap question connecting this
|
|
206
|
+
tactical win to the broader system architecture.
|
|
207
|
+
|
|
208
|
+
## Constraints
|
|
209
|
+
|
|
210
|
+
- **Quantity**: Exactly 8 suggestions.
|
|
211
|
+
- **Tone**: Rigorous, auditing, and senior. Avoid passive phrasing ("Maybe you
|
|
212
|
+
could...") in favor of critical analysis ("This approach risks X; how do we
|
|
213
|
+
resolve it?").
|
|
214
|
+
- **Negative Constraints**:
|
|
215
|
+
- DO NOT use numbering or bullet points.
|
|
216
|
+
- DO NOT include introductory or concluding text.
|
|
217
|
+
- DO NOT suggest generic, superficial questions (e.g., "How does this
|
|
218
|
+
work?").
|
|
219
|
+
- **Conciseness**: Each prompt must be a single, punchy sentence.
|
|
220
|
+
|
|
221
|
+
## Few-Shot Examples
|
|
222
|
+
|
|
223
|
+
**Input:** [User and AI just implemented a Ruby method to parse a large JSON
|
|
224
|
+
file into a database]
|
|
225
|
+
|
|
226
|
+
**Output:**
|
|
227
|
+
|
|
228
|
+
Can we refactor the parser to use a stream-based approach instead of loading
|
|
229
|
+
the entire JSON into memory?
|
|
230
|
+
|
|
231
|
+
How does this implementation handle malformed JSON or missing keys in the
|
|
232
|
+
middle of a 2GB file without crashing the process?
|
|
233
|
+
|
|
234
|
+
What is the time complexity of the current insert logic, and can we implement
|
|
235
|
+
bulk inserts to reduce database round trips?
|
|
236
|
+
|
|
237
|
+
Does the current naming convention for these helper methods align with our
|
|
238
|
+
project's style guide, or are we introducing cognitive load for future
|
|
239
|
+
maintainers?
|
|
240
|
+
|
|
241
|
+
Would using a dedicated library like Oj provide significant performance gains
|
|
242
|
+
over the standard JSON implementation here?
|
|
243
|
+
|
|
244
|
+
What specific boundary cases should be covered in the RSpec suite to ensure the
|
|
245
|
+
parser doesn't truncate data on trailing whitespace?
|
|
246
|
+
|
|
247
|
+
Which sections of the API documentation need to be updated to reflect this
|
|
248
|
+
change in the data ingestion pipeline?
|
|
249
|
+
|
|
250
|
+
Now that parsing is solved, how does this integrate into our overall background
|
|
251
|
+
job queue and retry logic?
|
|
252
|
+
|
|
253
|
+
## Output Format
|
|
254
|
+
|
|
255
|
+
Output only the 8 suggestions as plain text, each separated by one empty line.
|
|
256
|
+
No other characters or formatting.
|
|
186
257
|
suggest_roleplaying: |
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
258
|
+
## Context
|
|
259
|
+
|
|
260
|
+
You are an expert Narrative Consultant and Game Master's Assistant. Your
|
|
261
|
+
purpose is to analyze a roleplay conversation history and provide high-value,
|
|
262
|
+
diverse action prompts that help a player overcome writer's block while
|
|
263
|
+
deepening the story.
|
|
264
|
+
|
|
265
|
+
## Task
|
|
266
|
+
|
|
267
|
+
Analyze the provided Conversation History and current narrative state. Generate
|
|
268
|
+
exactly 9 distinct, concise prompt suggestions that the user could use as their
|
|
269
|
+
character's next response.
|
|
270
|
+
|
|
271
|
+
Each suggestion must map to one of these specific categories:
|
|
272
|
+
|
|
273
|
+
1. Plot Driver: A bold action or choice that advances the plot or increases
|
|
274
|
+
tension.
|
|
275
|
+
2. Emotional Core: An interaction focusing on emotional depth, social nuance,
|
|
276
|
+
or psychological reaction.
|
|
277
|
+
3. World Insight: Investigating an object or detail in the environment. Include
|
|
278
|
+
a suggested dice roll (e.g., "Roll 1d20") if there is an inherent risk of
|
|
279
|
+
failure or a need for high skill.
|
|
280
|
+
4. Hidden Agenda: An action involving secrecy, deception, or private motives.
|
|
281
|
+
Include a suggested dice roll (e.g., "Roll 1d20") if the action risks
|
|
282
|
+
discovery.
|
|
283
|
+
5. Social Catalyst: A prompt that engages all characters currently present in
|
|
284
|
+
the scene to force group interaction.
|
|
285
|
+
6. High Stakes: A risky gamble or sudden complication. This MUST explicitly
|
|
286
|
+
include a suggested dice roll (e.g., "Roll 1d20").
|
|
287
|
+
7. Character Quirk: A response rooted in the character's specific flaws,
|
|
288
|
+
habits, or obsessions.
|
|
289
|
+
8. Meta-Pivot: An abrupt shift in scene direction via a revelation, surprising
|
|
290
|
+
admission, or sudden change of heart.
|
|
291
|
+
9. The Curveball: An unexpected external event or realization that forces an
|
|
292
|
+
immediate reaction.
|
|
293
|
+
|
|
294
|
+
## Constraints
|
|
295
|
+
|
|
296
|
+
- Generate exactly 9 suggestions.
|
|
297
|
+
- Each suggestion must be concise and punchy.
|
|
298
|
+
- Negative Constraints: Do not use numbering, bullet points, or
|
|
299
|
+
introductory/concluding text. Avoid generic clichés (e.g., "I can't believe
|
|
300
|
+
this" or "What do we do now?").
|
|
301
|
+
- Ensure the tone of the suggestions aligns with the established voice of the
|
|
302
|
+
characters in the history.
|
|
303
|
+
|
|
304
|
+
## Variables
|
|
305
|
+
|
|
306
|
+
- Conversation History: The provided log of messages between players and AI.
|
|
307
|
+
|
|
308
|
+
## Few-Shot Examples
|
|
309
|
+
|
|
310
|
+
Input: [Scene where a party is arguing in a rainy forest while being hunted]
|
|
311
|
+
Output:
|
|
312
|
+
Demand that everyone stop shouting and listen for the sound of footsteps in the brush.
|
|
313
|
+
|
|
314
|
+
Quietly admit to the party leader that you are terrified, leaning into their shoulder for support.
|
|
315
|
+
|
|
316
|
+
Examine the muddy tracks near the campfire to see if the hunters are human or something else. Roll 1d20.
|
|
317
|
+
|
|
318
|
+
Secretly slip the map out of the leader's pocket while they are distracted by the argument. Roll 1d20.
|
|
319
|
+
|
|
320
|
+
Raise your voice and challenge every single person present to state their true loyalty right now.
|
|
321
|
+
|
|
322
|
+
Attempt to leap across the ravine to flank the hunters, risking a fall into the rapids. Roll 1d20.
|
|
323
|
+
|
|
324
|
+
Obsessively polish your sword with a rag, ignoring the argument entirely until it is perfectly mirrored.
|
|
325
|
+
|
|
326
|
+
Suddenly reveal that you were actually sent here by the enemy as a double agent.
|
|
327
|
+
|
|
328
|
+
A sudden lightning strike hits a nearby tree, knocking everyone off balance and creating a deafening blast.
|
|
329
|
+
|
|
330
|
+
## Output Format
|
|
331
|
+
|
|
332
|
+
Output only the 9 suggestions as plain text, each separated by one empty line.
|
|
333
|
+
No other text.
|
|
202
334
|
system_prompts:
|
|
203
335
|
default: <%= OC::OLLAMA::CHAT::SYSTEM || "%{persona}\n\n%{runtime_info}".inspect %>
|
|
204
336
|
persona: |
|
|
@@ -73,7 +73,7 @@ module OllamaChat::PersonaeManagement
|
|
|
73
73
|
def set_default_persona_name(persona_name)
|
|
74
74
|
if persona_name.present? && persona_name != :none
|
|
75
75
|
@default_persona_name = Pathname.new(persona_name).basename.sub_ext('').to_path
|
|
76
|
-
@session.update(default_persona_name:
|
|
76
|
+
@session.update(default_persona_name:)
|
|
77
77
|
else
|
|
78
78
|
@session.update(default_persona_name: nil)
|
|
79
79
|
@default_persona_name = nil
|
|
@@ -269,7 +269,7 @@ module OllamaChat::PersonaeManagement
|
|
|
269
269
|
persona = choose_persona(prompt: "Which persona's path do you need? %s") or return
|
|
270
270
|
path = persona_name_to_pathname(persona).to_s
|
|
271
271
|
perform_copy_to_clipboard(text: path, edit: false)
|
|
272
|
-
no_prefill or
|
|
272
|
+
no_prefill or self.prefill_prompt = path
|
|
273
273
|
path
|
|
274
274
|
end
|
|
275
275
|
|
|
@@ -403,7 +403,7 @@ module OllamaChat::PersonaeManagement
|
|
|
403
403
|
def load_personae
|
|
404
404
|
chosen = Set[]
|
|
405
405
|
choose_with_state do
|
|
406
|
-
while persona = choose_persona(chosen
|
|
406
|
+
while persona = choose_persona(chosen:, prompt: 'Who else should join the conversation? %s')
|
|
407
407
|
persona == :none and next
|
|
408
408
|
chosen << persona
|
|
409
409
|
end
|
|
@@ -31,7 +31,7 @@ module OllamaChat::PromptManagement
|
|
|
31
31
|
# @return [OllamaChat::Database::Models::Prompt, nil] the selected prompt
|
|
32
32
|
# model, or nil if the user chooses '[EXIT]' or cancels the selection.
|
|
33
33
|
def choose_prompt(default: nil, prompt: 'Select a prompt template: %s')
|
|
34
|
-
prompts = all_prompts(default:
|
|
34
|
+
prompts = all_prompts(default:)
|
|
35
35
|
prompts.unshift('[EXIT]')
|
|
36
36
|
case chosen = choose_entry(prompts, prompt:)
|
|
37
37
|
when '[EXIT]', nil
|
|
@@ -246,7 +246,7 @@ module OllamaChat::PromptManagement
|
|
|
246
246
|
instruction = nil
|
|
247
247
|
if edit
|
|
248
248
|
# Let the user write a suggestion instruction on the fly
|
|
249
|
-
instruction = edit_text('')
|
|
249
|
+
instruction = edit_text('').full? or return
|
|
250
250
|
else
|
|
251
251
|
# Let the user pick a prompt template (e.g., suggest_coding, suggest_roleplaying)
|
|
252
252
|
instruction = choose_prompt(prompt: 'Which suggestion strategy shall we employ? %s') or return
|
|
@@ -263,7 +263,7 @@ module OllamaChat::PromptManagement
|
|
|
263
263
|
EOT
|
|
264
264
|
|
|
265
265
|
# Execute a silent chat oneshot call (doesn't add to history)
|
|
266
|
-
suggestions = generate(prompt: full_prompt)
|
|
266
|
+
suggestions = generate(prompt: full_prompt).full? or return
|
|
267
267
|
|
|
268
268
|
# Pass the AI's suggestions through the editor for final refinement
|
|
269
269
|
edit_text(suggestions)
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
# A tool for copying
|
|
1
|
+
# A tool for copying a text to the clipboard.
|
|
2
2
|
#
|
|
3
3
|
# The executable used for the copy operation is read from the configuration
|
|
4
4
|
# under `copy.executable`. The default configuration ships with
|
|
5
5
|
# `ctc` – a small CLI that copies stdin to the system clipboard.
|
|
6
|
-
#
|
|
7
|
-
# The tool has no parameters; it simply grabs the most recent response
|
|
8
|
-
# from the chat instance and streams it to the executable.
|
|
9
6
|
class OllamaChat::Tools::CopyToClipboard
|
|
10
7
|
include OllamaChat::Tools::Concern
|
|
11
8
|
|
|
@@ -20,9 +17,8 @@ class OllamaChat::Tools::CopyToClipboard
|
|
|
20
17
|
function: Tool::Function.new(
|
|
21
18
|
name:,
|
|
22
19
|
description: <<~EOT,
|
|
23
|
-
Clipboard helper – Copies supplied string
|
|
24
|
-
|
|
25
|
-
output.
|
|
20
|
+
Clipboard helper – Copies supplied string into the OS clipboard,
|
|
21
|
+
enabling quick pasting elsewhere. No output.
|
|
26
22
|
EOT
|
|
27
23
|
parameters: Tool::Function::Parameters.new(
|
|
28
24
|
type: 'object',
|
|
@@ -36,7 +32,7 @@ class OllamaChat::Tools::CopyToClipboard
|
|
|
36
32
|
description: 'True if the copied text should be edited by the user, (default: false)'
|
|
37
33
|
)
|
|
38
34
|
},
|
|
39
|
-
required: []
|
|
35
|
+
required: %w[ text ]
|
|
40
36
|
)
|
|
41
37
|
)
|
|
42
38
|
)
|
|
@@ -51,17 +47,12 @@ class OllamaChat::Tools::CopyToClipboard
|
|
|
51
47
|
def execute(tool_call, **opts)
|
|
52
48
|
args = tool_call.function.arguments
|
|
53
49
|
edit = !!args.edit
|
|
54
|
-
text = args.text.full?
|
|
50
|
+
text = args.text.full? or raise OllamaChat::ToolFunctionArgumentError, 'no text given'
|
|
55
51
|
|
|
56
52
|
chat = opts[:chat]
|
|
57
53
|
chat.perform_copy_to_clipboard(text:, content: true, edit:)
|
|
58
54
|
|
|
59
|
-
message =
|
|
60
|
-
if text.nil?
|
|
61
|
-
"The last response has been successfully copied to the system clipboard."
|
|
62
|
-
else
|
|
63
|
-
"The provided text has been successfully copied to the system clipboard."
|
|
64
|
-
end
|
|
55
|
+
message = "The provided text has been successfully copied to the system clipboard."
|
|
65
56
|
|
|
66
57
|
{
|
|
67
58
|
success: true,
|
|
@@ -29,7 +29,9 @@ class OllamaChat::Tools::DirectoryStructure
|
|
|
29
29
|
Tree viewer – Returns JSON describing files/folders under path up to
|
|
30
30
|
max_depth (<= height of the tree), optionally only files ending with
|
|
31
31
|
suffix, e. g. rb for ruby files. Handy for locating resources or
|
|
32
|
-
presenting a project layout.
|
|
32
|
+
presenting a project layout. Limit the required tokens by using the
|
|
33
|
+
max_depth parameter if possible, because the number of nodes in a
|
|
34
|
+
tree can grow exponentially with its height.
|
|
33
35
|
EOT
|
|
34
36
|
parameters: Tool::Function::Parameters.new(
|
|
35
37
|
type: 'object',
|
data/lib/ollama_chat/version.rb
CHANGED
data/ollama_chat.gemspec
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: ollama_chat 0.0.
|
|
2
|
+
# stub: ollama_chat 0.0.99 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "ollama_chat".freeze
|
|
6
|
-
s.version = "0.0.
|
|
6
|
+
s.version = "0.0.99".freeze
|
|
7
7
|
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
|
9
9
|
s.require_paths = ["lib".freeze]
|
|
@@ -17,7 +17,7 @@ describe OllamaChat::Tools::CopyToClipboard do
|
|
|
17
17
|
expect(described_class.new.to_hash).to be_a Hash
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
it '
|
|
20
|
+
it 'raises an error if no text is provided' do
|
|
21
21
|
tool_call = double(
|
|
22
22
|
'ToolCall',
|
|
23
23
|
function: double(
|
|
@@ -29,17 +29,13 @@ describe OllamaChat::Tools::CopyToClipboard do
|
|
|
29
29
|
)
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
-
# Test that perform_copy_to_clipboard is called with content: true
|
|
33
|
-
expect(chat).to receive(:perform_copy_to_clipboard).with(text: nil, content: true, edit: false)
|
|
34
|
-
|
|
35
32
|
result = described_class.new.execute(tool_call, chat:)
|
|
36
33
|
|
|
37
34
|
# Should return valid JSON
|
|
38
35
|
expect(result).to be_a(String)
|
|
39
36
|
json = json_object(result)
|
|
40
|
-
expect(json.error).to
|
|
41
|
-
expect(json.
|
|
42
|
-
expect(json.message).to eq 'The last response has been successfully copied to the system clipboard.'
|
|
37
|
+
expect(json.error).to eq 'OllamaChat::ToolFunctionArgumentError'
|
|
38
|
+
expect(json.message).to eq 'no text given'
|
|
43
39
|
end
|
|
44
40
|
|
|
45
41
|
it 'can copy custom text to the clipboard' do
|
|
@@ -70,19 +66,20 @@ describe OllamaChat::Tools::CopyToClipboard do
|
|
|
70
66
|
end
|
|
71
67
|
|
|
72
68
|
it 'can be executed with edit option' do
|
|
69
|
+
text = 'Text to edit'
|
|
73
70
|
tool_call = double(
|
|
74
71
|
'ToolCall',
|
|
75
72
|
function: double(
|
|
76
73
|
name: 'copy_to_clipboard',
|
|
77
74
|
arguments: double(
|
|
78
|
-
text:
|
|
75
|
+
text: ,
|
|
79
76
|
edit: true
|
|
80
77
|
)
|
|
81
78
|
)
|
|
82
79
|
)
|
|
83
80
|
|
|
84
81
|
# Test that perform_copy_to_clipboard is called with edit: true
|
|
85
|
-
expect(chat).to receive(:perform_copy_to_clipboard).with(text
|
|
82
|
+
expect(chat).to receive(:perform_copy_to_clipboard).with(text:, content: true, edit: true)
|
|
86
83
|
|
|
87
84
|
result = described_class.new.execute(tool_call, chat:)
|
|
88
85
|
|
|
@@ -91,23 +88,24 @@ describe OllamaChat::Tools::CopyToClipboard do
|
|
|
91
88
|
json = json_object(result)
|
|
92
89
|
expect(json.error).to be_nil
|
|
93
90
|
expect(json.success).to be true
|
|
94
|
-
expect(json.message).to eq 'The
|
|
91
|
+
expect(json.message).to eq 'The provided text has been successfully copied to the system clipboard.'
|
|
95
92
|
end
|
|
96
93
|
|
|
97
94
|
it 'can handle execution errors gracefully' do
|
|
95
|
+
text = 'Error triggering text'
|
|
98
96
|
tool_call = double(
|
|
99
97
|
'ToolCall',
|
|
100
98
|
function: double(
|
|
101
99
|
name: 'copy_to_clipboard',
|
|
102
100
|
arguments: double(
|
|
103
|
-
text:
|
|
101
|
+
text: ,
|
|
104
102
|
edit: nil
|
|
105
103
|
)
|
|
106
104
|
)
|
|
107
105
|
)
|
|
108
106
|
|
|
109
107
|
# Test that perform_copy_to_clipboard raises an error
|
|
110
|
-
expect(chat).to receive(:perform_copy_to_clipboard).with(text
|
|
108
|
+
expect(chat).to receive(:perform_copy_to_clipboard).with(text:, content: true, edit: false).
|
|
111
109
|
and_raise(OllamaChat::OllamaChatError, 'No response available to copy to the system clipboard.')
|
|
112
110
|
|
|
113
111
|
result = described_class.new.execute(tool_call, chat:)
|
|
@@ -120,19 +118,20 @@ describe OllamaChat::Tools::CopyToClipboard do
|
|
|
120
118
|
end
|
|
121
119
|
|
|
122
120
|
it 'can handle execution exceptions gracefully' do
|
|
121
|
+
text = 'Exception triggering text'
|
|
123
122
|
tool_call = double(
|
|
124
123
|
'ToolCall',
|
|
125
124
|
function: double(
|
|
126
125
|
name: 'copy_to_clipboard',
|
|
127
126
|
arguments: double(
|
|
128
|
-
text:
|
|
127
|
+
text: ,
|
|
129
128
|
edit: nil
|
|
130
129
|
)
|
|
131
130
|
)
|
|
132
131
|
)
|
|
133
132
|
|
|
134
133
|
# Test that perform_copy_to_clipboard raises an exception
|
|
135
|
-
expect(chat).to receive(:perform_copy_to_clipboard).with(text
|
|
134
|
+
expect(chat).to receive(:perform_copy_to_clipboard).with(text:, content: true, edit: false).
|
|
136
135
|
and_raise(RuntimeError, 'some kind of exception')
|
|
137
136
|
|
|
138
137
|
result = described_class.new.execute(tool_call, chat:)
|
|
@@ -104,7 +104,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
it 'handles the "ignoring" policy' do
|
|
107
|
-
args = double(url
|
|
107
|
+
args = double(url:, document_policy: 'ignoring')
|
|
108
108
|
tool_call = double(function: double(arguments: args))
|
|
109
109
|
|
|
110
110
|
result = described_class.new.execute(tool_call, chat:)
|
|
@@ -113,7 +113,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
it 'handles the "importing" policy' do
|
|
116
|
-
args = double(url
|
|
116
|
+
args = double(url:, document_policy: 'importing')
|
|
117
117
|
tool_call = double(function: double(arguments: args))
|
|
118
118
|
expect(chat).to receive(:import_source).with(source_io, URI.parse(url)).and_return('imported content')
|
|
119
119
|
|
|
@@ -123,7 +123,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
it 'handles the "embedding" policy' do
|
|
126
|
-
args = double(url
|
|
126
|
+
args = double(url:, document_policy: 'embedding')
|
|
127
127
|
tool_call = double(function: double(arguments: args))
|
|
128
128
|
expect(chat).to receive(:embed_source).with(source_io, URI.parse(url)).and_return('embedded content')
|
|
129
129
|
|
|
@@ -133,7 +133,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
it 'handles the "summarizing" policy' do
|
|
136
|
-
args = double(url
|
|
136
|
+
args = double(url:, document_policy: 'summarizing')
|
|
137
137
|
tool_call = double(function: double(arguments: args))
|
|
138
138
|
expect(chat).to receive(:summarize_source).with(source_io, URI.parse(url)).and_return('summarized content')
|
|
139
139
|
|
|
@@ -143,7 +143,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
it 'handles an invalid policy' do
|
|
146
|
-
args = double(url
|
|
146
|
+
args = double(url:, document_policy: 'chaos_mode')
|
|
147
147
|
tool_call = double(function: double(arguments: args))
|
|
148
148
|
|
|
149
149
|
result = described_class.new.execute(tool_call, chat:)
|
|
@@ -160,7 +160,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
160
160
|
allow(chat).to receive(:fetch_source).and_yield(source_io)
|
|
161
161
|
expect(chat).to receive(:add_image).with(chat.images, source_io, URI.parse(url))
|
|
162
162
|
|
|
163
|
-
args = double(url
|
|
163
|
+
args = double(url:, document_policy: 'ignoring')
|
|
164
164
|
tool_call = double(function: double(arguments: args))
|
|
165
165
|
|
|
166
166
|
result = described_class.new.execute(tool_call, chat:)
|
|
@@ -172,7 +172,7 @@ describe OllamaChat::Tools::GetURL do
|
|
|
172
172
|
source_io = double('SourceIO', content_type: double(media_type: 'video'))
|
|
173
173
|
allow(chat).to receive(:fetch_source).and_yield(source_io)
|
|
174
174
|
|
|
175
|
-
args = double(url
|
|
175
|
+
args = double(url:, document_policy: 'ignoring')
|
|
176
176
|
tool_call = double(function: double(arguments: args))
|
|
177
177
|
|
|
178
178
|
result = described_class.new.execute(tool_call, chat:)
|