ollama_chat 0.0.98 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3794355ad924879293253f68420d41eb6e5bc4e766d20fbb9292dea3f9905372
4
- data.tar.gz: 907e863402ec2bb4a225dc21a47751b69bf2f3b72e8776bd9eeba70f275a5f72
3
+ metadata.gz: '0818076703ebd6e8d8babbe5898a6a0069863ce7cebf18658f70e8d4b5592c51'
4
+ data.tar.gz: 7e9f768d41bff3560c2d22ac20ace84da0b580b3b17f8f9d1ffd3522af5d8db1
5
5
  SHA512:
6
- metadata.gz: e8cfbc674eaa28164c9599686db526d70c7aeb1076ce9b2b6d160fb220f712c376a06df3dd41a55e327c26c965c8e06e049ade22dd886bd3c8555967e0658865
7
- data.tar.gz: 42da21a93f2295cd0cdbc01c8ad4bd2a12a93fb017c00e086336f46b2a358f29dea2b87a454298673c1d913404b1cf472682d1c7d56f3c82d961a0f40a8b63ed
6
+ metadata.gz: 4864950b3246106793b51ffe74e3ead6dad4683f1b9fcaf710bf7f672264c646dc90dacdbcef437725b725230b8d0b70938045729ca982c83845ebce41bb773d
7
+ data.tar.gz: 5b99426668efc34bbb89a17faf2dcf016c1a6ada4c69d44882ebb40bcb836907741f96b5fb66ee99ea75b78abbdd34069ddbc186d525acd015302bcea95e55a7
data/CHANGES.md CHANGED
@@ -1,5 +1,46 @@
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
+
3
44
  ## 2026-07-09 v0.0.98
4
45
 
5
46
  ### Added
@@ -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
@@ -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+(\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+(\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,
@@ -564,7 +564,7 @@ module OllamaChat::Commands
564
564
 
565
565
  command(
566
566
  name: :conversation,
567
- regexp: %r(^/conversation\s+(save|load)((?:\s+-(?:[c]))*)\s+(.+)$),
567
+ regexp: %r(^/conversation\s+(save|load)((?:\s+-(?:[c]))*)\s+([^-].*)$),
568
568
  complete: [ 'conversation', %w[ save load ] ],
569
569
  options: '[-c]',
570
570
  help: <<~EOT
@@ -696,7 +696,7 @@ module OllamaChat::Commands
696
696
 
697
697
  command(
698
698
  name: :character,
699
- regexp: %r(^/character(?:\s+(info|load|import))(?:\s+(\S+))?$),
699
+ regexp: %r(^/character(?:\s+(info|load|import))(?:\s+([^-].+))?$),
700
700
  complete: [ 'character', %w[ info load import ] ],
701
701
  help: <<~EOT
702
702
  Display character info, load or import a character
@@ -772,7 +772,7 @@ module OllamaChat::Commands
772
772
 
773
773
  command(
774
774
  name: :input,
775
- 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+([^-].*))?$),
776
776
  optional: true,
777
777
  complete: [ 'input', %w[ path context embedding summary ] ],
778
778
  options: "[\n -w|-a|-p|-e|\n -c <collection>|\n -t <tags>\n]\n[arg…]",
@@ -891,7 +891,7 @@ module OllamaChat::Commands
891
891
 
892
892
  command(
893
893
  name: :pipe,
894
- regexp: %r(^/pipe(\s+-e)?\s+(.+)$),
894
+ regexp: %r(^/pipe(\s+-e)?\s+([^-].*)$),
895
895
  options: 'path',
896
896
  help: <<~EOT
897
897
  Pipe the last response into another command's stdin.
@@ -918,7 +918,7 @@ module OllamaChat::Commands
918
918
 
919
919
  command(
920
920
  name: :output,
921
- regexp: %r(^/output(\s+-e)?\s+(.+)$),
921
+ regexp: %r(^/output(\s+-e)?\s+([^-].*)$),
922
922
  options: '[-e] path',
923
923
  help: <<~EOT
924
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
- Analyze the conversation history above. Generate exactly 3 distinct,
176
- concise follow-up prompts that the user might want to ask next to further
177
- their goal.
175
+ ## Context
178
176
 
179
- Criteria:
180
- 1. One prompt should dive deeper into the current technical implementation.
181
- 2. One prompt should challenge a potential edge case or optimization.
182
- 3. One prompt should suggest a logical next step or a broader architectural question.
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
- Output only the text of these suggestions, separated by an empty line,
185
- without any numbering, bullet points, or introductory text.
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
- Analyze the conversation history and the current narrative state of this
188
- roleplay/story. Generate exactly 3 distinct, concise prompts that the
189
- user could use to respond as their character.
190
-
191
- Criteria:
192
- 1. One prompt should be an active choice or bold action that pushes the
193
- plot forward or creates tension.
194
- 2. One prompt should focus on character interaction, exploring emotional
195
- depth, a social reaction, or a subtle nuance in dialogue.
196
- 3. One prompt should introduce a sudden complication, a risky gamble, or an
197
- unexpected event. This prompt MUST explicitly include a suggested dice roll
198
- (e.g., "Roll 1d20") to determine the success or failure of the action.
199
-
200
- Output only the text of these suggestions, separated by an empty line,
201
- without any numbering, bullet points, or introductory text.
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: 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
@@ -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: chosen, prompt: 'Who else should join the conversation? %s')
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: 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 the last assistant response to the clipboard.
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 (or last assistant reply if
24
- omitted) into the OS clipboard, enabling quick pasting elsewhere. No
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,
@@ -1,6 +1,6 @@
1
1
  module OllamaChat
2
2
  # OllamaChat version
3
- VERSION = '0.0.98'
3
+ VERSION = '0.0.99'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
data/ollama_chat.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama_chat 0.0.98 ruby lib
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.98".freeze
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]
@@ -15,7 +15,7 @@ describe OllamaChat::Chat, protect_env: true do
15
15
  end
16
16
 
17
17
  let :chat do
18
- OllamaChat::Chat.new(argv: argv).expose
18
+ OllamaChat::Chat.new(argv:).expose
19
19
  end
20
20
 
21
21
  describe 'instantiation' do
@@ -4,7 +4,7 @@ describe OllamaChat::History do
4
4
  end
5
5
 
6
6
  let :chat do
7
- OllamaChat::Chat.new(argv: argv).expose
7
+ OllamaChat::Chat.new(argv:).expose
8
8
  end
9
9
 
10
10
  connect_to_ollama_server(instantiate: false)
@@ -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 'can be executed successfully without provided text' do
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 be_nil # No exception was raised
41
- expect(json.success).to be true
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: nil,
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: nil, content: true, edit: true)
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 last response has been successfully copied to the system clipboard.'
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: nil,
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:nil, content: true, edit: false).
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: nil,
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: nil, content: true, edit: false).
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:)
@@ -31,7 +31,7 @@ describe OllamaChat::Tools::ExecuteRI do
31
31
  'ToolCall',
32
32
  function: double(
33
33
  name: 'execute_ri',
34
- arguments: double(topic: topic)
34
+ arguments: double(topic:)
35
35
  )
36
36
  )
37
37
 
@@ -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: url, document_policy: 'ignoring')
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: url, document_policy: 'importing')
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: url, document_policy: 'embedding')
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: url, document_policy: 'summarizing')
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: url, document_policy: 'chaos_mode')
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: url, document_policy: 'ignoring')
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: url, document_policy: 'ignoring')
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:)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ollama_chat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.98
4
+ version: 0.0.99
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank