ollama_chat 0.0.109 → 0.0.111
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 +113 -0
- data/lib/ollama_chat/commands.rb +23 -6
- data/lib/ollama_chat/database/migrations/009_add_app_states_table.rb +11 -0
- data/lib/ollama_chat/database/models/app_state.rb +122 -0
- data/lib/ollama_chat/follow_chat.rb +15 -15
- data/lib/ollama_chat/information.rb +50 -0
- data/lib/ollama_chat/input_content.rb +10 -2
- data/lib/ollama_chat/oc.rb +19 -5
- data/lib/ollama_chat/ollama_chat_config/default_config.yml +5 -1
- data/lib/ollama_chat/prompt_management.rb +124 -0
- data/lib/ollama_chat/session_management.rb +25 -10
- data/lib/ollama_chat/tools/execute_jira_twg.rb +85 -0
- data/lib/ollama_chat/tools/gem_path_lookup.rb +1 -3
- data/lib/ollama_chat/tools/patch_file.rb +1 -1
- data/lib/ollama_chat/tools.rb +1 -0
- data/lib/ollama_chat/version.rb +1 -1
- data/lib/ollama_chat/web_searching.rb +4 -3
- data/lib/ollama_chat.rb +20 -2
- data/ollama_chat.gemspec +5 -5
- data/spec/assets/api_ps.json +23 -0
- data/spec/ollama_chat/commands_spec.rb +12 -0
- data/spec/ollama_chat/parsing_spec.rb +1 -1
- data/spec/ollama_chat/prompt_management_spec.rb +110 -0
- data/spec/ollama_chat/session_management_spec.rb +16 -4
- data/spec/ollama_chat/tools/directory_structure_spec.rb +1 -1
- data/spec/ollama_chat/tools/execute_jira_twg_spec.rb +140 -0
- data/spec/ollama_chat/web_searching_spec.rb +26 -26
- data/spec/spec_helper.rb +3 -0
- metadata +10 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 54b2bf70b7f843ad09b5de5f0881bf810ec78a08c79c5e1cd93d86aa75b68696
|
|
4
|
+
data.tar.gz: 33e475ea5e115609e02ba6b92cdf6a9b1b1dc6eccbfde6978fc80623daa3040b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e7dad8f668a54b9c1e89937a445894eeb9872e1541d54dfc36fcf136d5c759bde7e0c21c9b818ce2e8ffa3bd7274163ada6b18553590dc812f3e89bdae839a27
|
|
7
|
+
data.tar.gz: 8bbd695f1eacc33c6928f8a382acdb8ed68b57348a0ce5199a9203bd7070df59f71a7a37e2862297c6c7dbc12e0d00aa9c46ef90f6392fc7579b87c8de438b7d
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,118 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-08-25 v0.0.111
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
* `OllamaChat::Tools::ExecuteJiraTwg` tool
|
|
8
|
+
(`lib/ollama_chat/tools/execute_jira_twg.rb`) as a `twg` CLI wrapper,
|
|
9
|
+
featuring `Shellwords.shellsplit` argument parsing,
|
|
10
|
+
`OllamaChat::ExecuteError` handling for non-zero exits, and
|
|
11
|
+
`Kramdown::ANSI::Width.truncate` for feedback messages.
|
|
12
|
+
* `OC::OLLAMA::CHAT::TOOLS::JIRA::TWG` configuration constant, defaulting to
|
|
13
|
+
`which 2>/dev/null twg`.
|
|
14
|
+
* `execute_jira_twg` entry in `default_config.yml` with `default: false` and
|
|
15
|
+
`require_confirmation: true`.
|
|
16
|
+
* Two-step tool approval workflow in `FollowChat#handle_tool_calls`,
|
|
17
|
+
replacing single `chat.ask?` with `chat.confirm?` followed by `chat.ask?`
|
|
18
|
+
to allow single-keypress confirmation or free-text instructions.
|
|
19
|
+
* `session_sync` method in `session_management.rb` to persist messages,
|
|
20
|
+
links, history, update `working_directory`, and re-lock the session.
|
|
21
|
+
* `context_usage` method in `lib/ollama_chat/information.rb` to calculate and
|
|
22
|
+
format context usage, including `current_context_length` and
|
|
23
|
+
`context_filled` helpers.
|
|
24
|
+
* Context usage gauge in `info_session` displaying tokens used, total, and
|
|
25
|
+
percentage.
|
|
26
|
+
* `api_ps.json` asset containing a **27.3B** `qwen3.8:27b` model entry with a
|
|
27
|
+
**262144** context length.
|
|
28
|
+
* `spec/ollama_chat/tools/execute_jira_twg_spec.rb` with 8 specs covering
|
|
29
|
+
identity, execution, quoted arguments, nil guards, exception rescue, and
|
|
30
|
+
non-zero exits.
|
|
31
|
+
* `#session_sync` spec verifying ordered calls to
|
|
32
|
+
`store_messages_in_session`, `links.sync`, `save_history`, and
|
|
33
|
+
`session.lock`.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
* `session_close` now delegates to `session_sync` before calling
|
|
38
|
+
`session.unlock`.
|
|
39
|
+
* `change_session` `ensure` block now uses `session_sync` instead of inline
|
|
40
|
+
`session.update(working_directory:)`.
|
|
41
|
+
* `set_new_session` now includes `ensure session.lock` to guarantee locking
|
|
42
|
+
on early return.
|
|
43
|
+
* `context_usage` logic extracted from `dynamic_runtime_information_values`
|
|
44
|
+
for improved maintainability.
|
|
45
|
+
* `session_management.rb` now logs the formatted `context_usage` string
|
|
46
|
+
instead of raw token counts and byte sizes.
|
|
47
|
+
* `self.register_name` in `lib/ollama_chat/tools/gem_path_lookup.rb`
|
|
48
|
+
simplified to use endless method definition syntax.
|
|
49
|
+
* Context ingestion logic now guards against empty file sets, emitting a
|
|
50
|
+
`STDERR` message and canceling if no files match the given patterns.
|
|
51
|
+
* `parsing_spec.rb` and `directory_structure_spec.rb` expected file counts
|
|
52
|
+
updated from **23** to **24**.
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
* `current_context_length` now uses safe navigation operators to guard
|
|
57
|
+
against missing `ollama.ps` or `models`.
|
|
58
|
+
* `context_usage` body wrapped in a guard to return `nil` when context length
|
|
59
|
+
is unavailable.
|
|
60
|
+
* `which` stderr silenced in all OC config defaults using `2>/dev/null`.
|
|
61
|
+
|
|
62
|
+
## 2026-08-20 v0.0.110
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
* **App State Support**: Introduced `009_add_app_states_table.rb` migration
|
|
67
|
+
and `app_state.rb` model.
|
|
68
|
+
* **Prompt Synchronization**: Added `/prompt sync` subcommand with unified
|
|
69
|
+
diffs and boot check capabilities.
|
|
70
|
+
* Implemented `AppState` model with `get`/`set` helpers and a `seed` boot
|
|
71
|
+
check using per-prompt `Digest::SHA256` hashes XOR-folded into a
|
|
72
|
+
fingerprint.
|
|
73
|
+
* Boot check prints `~`/`+`/`-` detail report on drift and prompts user
|
|
74
|
+
to acknowledge; declining retains old fingerprint for repeated notice
|
|
75
|
+
on next boot.
|
|
76
|
+
* Added `OC::DIFF_COMMAND` setting (`diff -u --color=always`) to `oc.rb`
|
|
77
|
+
for unified diff display, decoded via `Shellwords.split`.
|
|
78
|
+
* Implemented `prompt_sync` and `show_prompt_diff` methods in
|
|
79
|
+
`prompt_management.rb` to compare DB prompts against shipped defaults,
|
|
80
|
+
show unified diff, offer `OC::DIFF_TOOL` resolution, and clean up
|
|
81
|
+
orphaned default prompts.
|
|
82
|
+
* Wired `/prompt sync` subcommand into `commands.rb` (regexp, completion,
|
|
83
|
+
dispatch).
|
|
84
|
+
* **Test Mode**: Added `OllamaChat.test_mode` flag in `ollama_chat.rb` to
|
|
85
|
+
suppress interactive prompts during specs; set in `spec_helper.rb`.
|
|
86
|
+
* **Documentation**: Added YARD `@!attribute` docs for `test_mode` and
|
|
87
|
+
`test_mode?`.
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
|
|
91
|
+
* **Web Searching**: Updated `web_searching.rb` to store `search_engine` in
|
|
92
|
+
local `engine` to avoid double method call.
|
|
93
|
+
* **Session Management**: Added call to `session_close` after
|
|
94
|
+
`clean_messages!` succeeds in `lib/ollama_chat/commands.rb`.
|
|
95
|
+
* **Error Messages**: Simplified stale context error message in `PatchFile`
|
|
96
|
+
when a checksum mismatch occurs.
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
|
|
100
|
+
* **Spec Expectations**: Updated `web_searching_spec.rb` to replace `allow`
|
|
101
|
+
with `expect` for message expectations.
|
|
102
|
+
* Changed `log` expectations to `.at_least(:once)` since `links.add` also
|
|
103
|
+
triggers `log` internally.
|
|
104
|
+
* Removed unused `before { allow(chat).to receive(:log) }` from the
|
|
105
|
+
`#web` describe block.
|
|
106
|
+
* Added `.at_least(:once)` to `document_policy.selected` as it is called
|
|
107
|
+
in both `if` and `elsif` branches.
|
|
108
|
+
* Changed `fetch_source`, `summarize`, and `import` expectations to
|
|
109
|
+
`.twice` since they are called once per URL (2 URLs in test data).
|
|
110
|
+
|
|
111
|
+
### Tests
|
|
112
|
+
|
|
113
|
+
* Added 7 new spec examples for `prompt_sync` and `show_prompt_diff` in
|
|
114
|
+
`prompt_management_spec.rb`.
|
|
115
|
+
|
|
3
116
|
## 2026-08-18 v0.0.109
|
|
4
117
|
|
|
5
118
|
### Added
|
data/lib/ollama_chat/commands.rb
CHANGED
|
@@ -484,14 +484,15 @@ module OllamaChat::Commands
|
|
|
484
484
|
|
|
485
485
|
command(
|
|
486
486
|
name: :prompt,
|
|
487
|
-
regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|rename|-e))?(\s+(?:-[ef]|-c\s+(?:\w+|\?)))?(?:\s+([^-].*))?$),
|
|
488
|
-
complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset rename ] ],
|
|
487
|
+
regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|rename|sync|-e))?(\s+(?:-[ef]|-c\s+(?:\w+|\?)))?(?:\s+([^-].*))?$),
|
|
488
|
+
complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset rename sync ] ],
|
|
489
489
|
optional: true,
|
|
490
490
|
options: '[-c CONTEXT|-e|-f]',
|
|
491
491
|
help: <<~EOT,
|
|
492
492
|
📝 Manage prompt templates:
|
|
493
|
-
|
|
494
|
-
|
|
493
|
+
Subcommands: edit, info, add, delete, list,
|
|
494
|
+
duplicate, import, export, reset, rename,
|
|
495
|
+
sync.
|
|
495
496
|
Options: -c [context]
|
|
496
497
|
(? for interactive in /prompt),
|
|
497
498
|
-e (edit next)
|
|
@@ -544,6 +545,8 @@ module OllamaChat::Commands
|
|
|
544
545
|
STDOUT.puts "No default value found for prompt #{bold{prompt.name}}."
|
|
545
546
|
end
|
|
546
547
|
end
|
|
548
|
+
when 'sync'
|
|
549
|
+
prompt_sync(context:)
|
|
547
550
|
when nil, '-e'
|
|
548
551
|
if prompt = choose_prompt(
|
|
549
552
|
prompt: 'Which template shall guide the next response? %s',
|
|
@@ -591,14 +594,15 @@ module OllamaChat::Commands
|
|
|
591
594
|
|
|
592
595
|
command(
|
|
593
596
|
name: :conversation,
|
|
594
|
-
regexp: %r(^/conversation\s+(clean|save|load)(\s+-c)?(?:\s+([^-].*\.jsonl?))?$),
|
|
595
|
-
complete: [ 'conversation', %w[ save load clean ] ],
|
|
597
|
+
regexp: %r(^/conversation\s+(clean|compact|save|load)(\s+-c)?(?:\s+([^-].*\.jsonl?))?$),
|
|
598
|
+
complete: [ 'conversation', %w[ save load clean compact ] ],
|
|
596
599
|
options: '[-c] [FILENAME]',
|
|
597
600
|
help: <<~EOT
|
|
598
601
|
💾 Save/Load conversation state
|
|
599
602
|
(-c to clean before saving)
|
|
600
603
|
FILENAME ends with .json or .jsonl
|
|
601
604
|
Or clean inplace (removes tool content, images, and thinking)
|
|
605
|
+
Or compact (summarize old messages, keep recent)
|
|
602
606
|
EOT
|
|
603
607
|
) do |subcommand,opts,path|
|
|
604
608
|
if %w[ save load ].include?(subcommand) && path.blank?
|
|
@@ -619,10 +623,23 @@ module OllamaChat::Commands
|
|
|
619
623
|
)
|
|
620
624
|
then
|
|
621
625
|
messages.clean_messages!
|
|
626
|
+
session_sync
|
|
622
627
|
STDOUT.puts "Conversation cleaned."
|
|
623
628
|
else
|
|
624
629
|
STDOUT.puts 'Cancelled.'
|
|
625
630
|
end
|
|
631
|
+
when 'compact'
|
|
632
|
+
if confirm?(
|
|
633
|
+
prompt: '🔔 Compact conversation? Old messages will be summarized. (y/n) ',
|
|
634
|
+
yes: /\Ay/i
|
|
635
|
+
)
|
|
636
|
+
then
|
|
637
|
+
messages.compact!
|
|
638
|
+
session_sync
|
|
639
|
+
STDOUT.puts "Conversation compacted."
|
|
640
|
+
else
|
|
641
|
+
STDOUT.puts 'Cancelled.'
|
|
642
|
+
end
|
|
626
643
|
end
|
|
627
644
|
:next
|
|
628
645
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Sequel.migration do
|
|
2
|
+
change do
|
|
3
|
+
create_table :app_states do
|
|
4
|
+
primary_key :id
|
|
5
|
+
String :key, null: false, unique: true
|
|
6
|
+
Text :value # JSON-encoded payload
|
|
7
|
+
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP, null: false
|
|
8
|
+
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP, null: false
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
|
|
3
|
+
# Represents a key/value pair of persistent application state stored in
|
|
4
|
+
# the database.
|
|
5
|
+
#
|
|
6
|
+
# This model provides a generic, JSON-backed store for small bits of
|
|
7
|
+
# runtime state that need to survive across sessions—such as the
|
|
8
|
+
# XOR-SHA256 fingerprint of the shipped default prompts used for boot
|
|
9
|
+
# drift detection.
|
|
10
|
+
class OllamaChat::Database::Models::AppState < Sequel::Model(OllamaChat::DB)
|
|
11
|
+
plugin :timestamps, update_on_create: true
|
|
12
|
+
plugin :serialization, :json, :value
|
|
13
|
+
plugin :validation_helpers
|
|
14
|
+
|
|
15
|
+
# Validates the application state entry.
|
|
16
|
+
#
|
|
17
|
+
# Ensures that the `key` is present and unique.
|
|
18
|
+
def validate
|
|
19
|
+
super
|
|
20
|
+
validates_presence :key
|
|
21
|
+
validates_unique :key
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Retrieves the JSON payload stored under the given key.
|
|
25
|
+
#
|
|
26
|
+
# @param key [String, Symbol] the state key to look up
|
|
27
|
+
# @return [Hash, nil] the deserialized JSON payload, or nil if no
|
|
28
|
+
# record exists for the key
|
|
29
|
+
def self.get(key)
|
|
30
|
+
where(key: key.to_s).first&.value
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Creates or updates the JSON payload stored under the given key.
|
|
34
|
+
#
|
|
35
|
+
# @param key [String, Symbol] the state key to write
|
|
36
|
+
# @param value [Hash, nil] the JSON-serializable payload to store
|
|
37
|
+
# @return [OllamaChat::Database::Models::AppState] the saved record
|
|
38
|
+
def self.set(key, value)
|
|
39
|
+
if found = where(key: key.to_s).first
|
|
40
|
+
found.update(value:)
|
|
41
|
+
found
|
|
42
|
+
else
|
|
43
|
+
create(key: key.to_s, value:)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Computes per-prompt SHA256 hashes and an XOR fingerprint of all shipped
|
|
48
|
+
# default prompts.
|
|
49
|
+
#
|
|
50
|
+
# At boot, if the stored fingerprint differs from the current one, a detail
|
|
51
|
+
# report (modified / added / removed) is printed to STDERR and the user is
|
|
52
|
+
# prompted to accept the new fingerprint. If declined, the old fingerprint
|
|
53
|
+
# is retained and the notice will reappear on the next boot.
|
|
54
|
+
#
|
|
55
|
+
# This method is invoked automatically by the model seeding loop in
|
|
56
|
+
# `OllamaChat::Database.setup_models`.
|
|
57
|
+
#
|
|
58
|
+
# @param chat [OllamaChat::Chat] the chat instance providing the config
|
|
59
|
+
# and `confirm?`
|
|
60
|
+
def self.seed(chat)
|
|
61
|
+
stored = get(:prompt_defaults_fingerprint)
|
|
62
|
+
|
|
63
|
+
stored_fingerprint = stored&.dig('fingerprint')
|
|
64
|
+
|
|
65
|
+
hashes = {}
|
|
66
|
+
chat.config.prompts.to_h.each do |context, prompts|
|
|
67
|
+
prompts.each do |name, content|
|
|
68
|
+
hashes["#{context}/#{name}"] =
|
|
69
|
+
Digest::SHA256.hexdigest(content.to_s)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
fingerprint = hashes.values.reduce(0) do |sum, hex|
|
|
74
|
+
sum ^ hex.to_i(16)
|
|
75
|
+
end.to_s(16)
|
|
76
|
+
|
|
77
|
+
return true if fingerprint == stored_fingerprint
|
|
78
|
+
|
|
79
|
+
if stored&.dig('hashes')
|
|
80
|
+
old = stored['hashes']
|
|
81
|
+
added = hashes.keys - old.keys
|
|
82
|
+
removed = old.keys - hashes.keys
|
|
83
|
+
changed = hashes.select { |k, v| old[k] && old[k] != v }.keys
|
|
84
|
+
|
|
85
|
+
STDERR.puts "⚠️ Shipped prompts changed since last boot:"
|
|
86
|
+
changed.each { |k| STDERR.puts " ~ #{k} (modified)" }
|
|
87
|
+
added.each { |k| STDERR.puts " + #{k} (new)" }
|
|
88
|
+
removed.each { |k| STDERR.puts " - #{k} (removed)" }
|
|
89
|
+
elsif !OllamaChat.test_mode?
|
|
90
|
+
STDERR.puts '⚠️ First run — storing prompt fingerprints.'
|
|
91
|
+
set(:prompt_defaults_fingerprint,
|
|
92
|
+
{ fingerprint:, hashes:, computed_at: Time.now.iso8601 })
|
|
93
|
+
return true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
prompt = <<~EOT.chomp << ' '
|
|
97
|
+
Keep local prompts (stop nagging)?
|
|
98
|
+
Consider adopting via /prompt sync or /prompt reset. (y/n) %s
|
|
99
|
+
EOT
|
|
100
|
+
if OllamaChat.test_mode? || chat.confirm?(prompt:, yes: /\Ay/i, timeout: 5)
|
|
101
|
+
set(:prompt_defaults_fingerprint,
|
|
102
|
+
{ fingerprint:, hashes:, computed_at: Time.now.iso8601 })
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
true
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @!attribute [v] id
|
|
109
|
+
# @return [Integer] The primary key for the state entry.
|
|
110
|
+
#
|
|
111
|
+
# @!attribute [v] key
|
|
112
|
+
# @return [String] The unique state key.
|
|
113
|
+
#
|
|
114
|
+
# @!attribute [v] value
|
|
115
|
+
# @return [Hash, nil] The JSON-serialized state payload.
|
|
116
|
+
#
|
|
117
|
+
# @!attribute [v] created_at
|
|
118
|
+
# @return [Time, nil] The timestamp when the state was created.
|
|
119
|
+
#
|
|
120
|
+
# @!attribute [v] updated_at
|
|
121
|
+
# @return [Time, nil] The timestamp of the last update to the state.
|
|
122
|
+
end
|
|
@@ -137,18 +137,25 @@ class OllamaChat::FollowChat
|
|
|
137
137
|
end
|
|
138
138
|
STDOUT.puts
|
|
139
139
|
confirmed = :implicit
|
|
140
|
-
|
|
140
|
+
resolve = nil
|
|
141
|
+
function = JSON.pretty_generate(tool_call.function)
|
|
141
142
|
chat.log(:info, "Tool call received", data: { tool: name, function: })
|
|
142
143
|
if chat.tool_function(name).require_confirmation?
|
|
143
|
-
|
|
144
|
-
bold { name },
|
|
145
|
-
italic { function },
|
|
144
|
+
STDOUT.puts "🔔 I want to execute tool %s\n%s\n" % [
|
|
145
|
+
bold { name }, italic { function },
|
|
146
146
|
]
|
|
147
|
-
prompt
|
|
148
|
-
|
|
147
|
+
prompt = '❓ Allow ✅[y]es / ⛔️[n]o / 📝[i]nstruct? '
|
|
148
|
+
answer = chat.confirm?(prompt:)&.to_s&.downcase
|
|
149
|
+
resolve = 'You **MUST** ask the user for instructions on how to proceed!!!'
|
|
150
|
+
case answer
|
|
151
|
+
when 'y'
|
|
149
152
|
confirmed = :explicit
|
|
153
|
+
when 'n'
|
|
154
|
+
confirmed = :denied
|
|
150
155
|
else
|
|
151
156
|
confirmed = :denied
|
|
157
|
+
instr = chat.ask?(prompt: '📝 Instructions: ')&.strip
|
|
158
|
+
resolve = instr.full? || resolve
|
|
152
159
|
end
|
|
153
160
|
else
|
|
154
161
|
STDOUT.puts "Executing tool %s\n%s" % [
|
|
@@ -160,10 +167,7 @@ class OllamaChat::FollowChat
|
|
|
160
167
|
result = nil
|
|
161
168
|
case confirmed
|
|
162
169
|
when :denied
|
|
163
|
-
result = JSON(
|
|
164
|
-
message: 'User denied confirmation!',
|
|
165
|
-
resolve: 'You **MUST** ask the user for instructions on how to proceed!!!',
|
|
166
|
-
)
|
|
170
|
+
result = JSON(message: 'User denied confirmation!', resolve:)
|
|
167
171
|
STDOUT.printf(
|
|
168
172
|
"\n%s Execution of tool %s denied by user.\n\n", ?🚫, bold { name }
|
|
169
173
|
)
|
|
@@ -174,11 +178,7 @@ class OllamaChat::FollowChat
|
|
|
174
178
|
"\n%s Execution of tool %s confirmed.\n\n", symbol, bold { name }
|
|
175
179
|
)
|
|
176
180
|
result = OllamaChat::Tools.registered[name].execute(tool_call, chat:)
|
|
177
|
-
|
|
178
|
-
chat.log(:info, "Tool execution confirmed", data: { tool: name, confirmed: })
|
|
179
|
-
else
|
|
180
|
-
chat.log(:info, "Tool execution confirmed", data: { tool: name, confirmed: })
|
|
181
|
-
end
|
|
181
|
+
chat.log(:info, "Tool execution confirmed", data: { tool: name, confirmed: })
|
|
182
182
|
end
|
|
183
183
|
|
|
184
184
|
chat.tool_call_results[name] << result
|
|
@@ -132,6 +132,12 @@ module OllamaChat::Information
|
|
|
132
132
|
output.puts " No persona selected."
|
|
133
133
|
end
|
|
134
134
|
output.puts "🧠 Current chat model is #{bold{@model}}."
|
|
135
|
+
context_usage = '%s of %s (%s)' % [
|
|
136
|
+
session.estimate_tokens.tokens_formatted,
|
|
137
|
+
format_tokens(current_context_length),
|
|
138
|
+
bold { '%.1f%%' % (100 * context_filled)},
|
|
139
|
+
]
|
|
140
|
+
output.puts " Context Length: #{context_usage}"
|
|
135
141
|
output.print ' '; think_mode.show(output:)
|
|
136
142
|
output.print ' '; think_loud.show(output:)
|
|
137
143
|
output.print ' '; think_strip.show(output:)
|
|
@@ -360,6 +366,7 @@ module OllamaChat::Information
|
|
|
360
366
|
tools_support: tools_support.on? ? 'enabled' : 'disabled',
|
|
361
367
|
voice: voice.on? ? 'enabled' : 'disabled',
|
|
362
368
|
weekday: now.strftime('%A'),
|
|
369
|
+
context_usage: ,
|
|
363
370
|
}
|
|
364
371
|
end
|
|
365
372
|
|
|
@@ -373,4 +380,47 @@ module OllamaChat::Information
|
|
|
373
380
|
def dynamic_runtime_information
|
|
374
381
|
prompt(:dynamic_runtime_info).to_s % dynamic_runtime_information_values
|
|
375
382
|
end
|
|
383
|
+
|
|
384
|
+
# Resolves the effective context window size for the current model.
|
|
385
|
+
#
|
|
386
|
+
# Prefers the session-level `num_ctx` override (set via `/model_options`
|
|
387
|
+
# or the stored model options profile). Falls back to the model's native
|
|
388
|
+
# `context_length` in the default profile or as reported by the Ollama server
|
|
389
|
+
# if possible.
|
|
390
|
+
#
|
|
391
|
+
# @return [Integer, NilClass] the context length in tokens or nil
|
|
392
|
+
def current_context_length
|
|
393
|
+
get_session_model_options[:num_ctx] ||
|
|
394
|
+
get_stored_model_options(@model)[:num_ctx] ||
|
|
395
|
+
ollama.ps&.models&.find { _1.name == @model }&.context_length
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Computes the fraction of the context window currently in use.
|
|
399
|
+
#
|
|
400
|
+
# Divides the estimated token count of the session's messages by the
|
|
401
|
+
# effective context length, clamped to the range 0.0–1.0.
|
|
402
|
+
#
|
|
403
|
+
# @return [Float] the ratio of used context (e.g. `0.73` for 73%)
|
|
404
|
+
def context_filled
|
|
405
|
+
es = session.estimate_tokens
|
|
406
|
+
(es.tokens.to_f / current_context_length).clamp(0..1).to_f
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Formats the current context usage as a human-readable string.
|
|
410
|
+
#
|
|
411
|
+
# Returns a string like `"167.7 KT of 262.1 KT (64.0%)"` combining
|
|
412
|
+
# the estimated tokens in use, the effective context length, and
|
|
413
|
+
# the percentage (via +context_filled+).
|
|
414
|
+
#
|
|
415
|
+
# @return [String, nil] the formatted usage string, or +nil+ if
|
|
416
|
+
# the context length cannot be determined.
|
|
417
|
+
def context_usage
|
|
418
|
+
if cl = current_context_length
|
|
419
|
+
'%s of %s (%s)' % [
|
|
420
|
+
session.estimate_tokens.tokens_formatted,
|
|
421
|
+
format_tokens(current_context_length),
|
|
422
|
+
'%.1f%%' % (100 * context_filled),
|
|
423
|
+
]
|
|
424
|
+
end
|
|
425
|
+
end
|
|
376
426
|
end
|
|
@@ -95,14 +95,22 @@ module OllamaChat::InputContent
|
|
|
95
95
|
format = @context_format.selected
|
|
96
96
|
myself = self
|
|
97
97
|
if patterns
|
|
98
|
-
|
|
98
|
+
count = 0
|
|
99
|
+
ctx = ContextSpook::generate_context(verbose: true, format:) do |context|
|
|
99
100
|
context do
|
|
100
101
|
myself.file_set_each(patterns, all:) do |filename|
|
|
101
102
|
filename.file? or next
|
|
102
103
|
file filename.to_path
|
|
104
|
+
count += 1
|
|
103
105
|
end
|
|
104
106
|
end
|
|
105
|
-
end
|
|
107
|
+
end
|
|
108
|
+
if count > 0
|
|
109
|
+
ctx.send("to_#{format.downcase}")
|
|
110
|
+
STDOUT.puts "✅ Ingesting context now."
|
|
111
|
+
else
|
|
112
|
+
STDERR.puts "❌ No files in context. ⇨ Cancelled."
|
|
113
|
+
end
|
|
106
114
|
else
|
|
107
115
|
if context_filename = choose_filename('.contexts/*.rb')
|
|
108
116
|
ContextSpook.generate_context(context_filename, verbose: true, format:).
|
data/lib/ollama_chat/oc.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'const_conf'
|
|
2
2
|
require 'pathname'
|
|
3
|
+
require 'shellwords'
|
|
3
4
|
|
|
4
5
|
# Environment configuration module for OllamaChat
|
|
5
6
|
#
|
|
@@ -38,7 +39,7 @@ module OC
|
|
|
38
39
|
description 'Pager command to use in case terminal lines are exceeded by output'
|
|
39
40
|
|
|
40
41
|
default do
|
|
41
|
-
if fallback_pager = `which less`.full?(:chomp) || `which more`.full?(:chomp)
|
|
42
|
+
if fallback_pager = `which 2>/dev/null less`.full?(:chomp) || `which 2>/dev/null more`.full?(:chomp)
|
|
42
43
|
fallback_pager << ' -r'
|
|
43
44
|
end
|
|
44
45
|
end
|
|
@@ -48,7 +49,7 @@ module OC
|
|
|
48
49
|
description 'Editor to use'
|
|
49
50
|
|
|
50
51
|
default do
|
|
51
|
-
if editor = %w[ vim vi ].find { `which #{_1}`.full?(:chomp) }
|
|
52
|
+
if editor = %w[ vim vi ].find { `which 2>/dev/null #{_1}`.full?(:chomp) }
|
|
52
53
|
editor
|
|
53
54
|
else
|
|
54
55
|
warn 'Need an editor command configured via env var "EDITOR"'
|
|
@@ -60,15 +61,23 @@ module OC
|
|
|
60
61
|
description 'Browser to use'
|
|
61
62
|
|
|
62
63
|
default do
|
|
63
|
-
%w[ open xdg-open ].find { `which #{_1}` }.full?(:chomp)
|
|
64
|
+
%w[ open xdg-open ].find { `which 2>/dev/null #{_1}` }.full?(:chomp)
|
|
64
65
|
end
|
|
65
66
|
end
|
|
66
67
|
|
|
68
|
+
DIFF_COMMAND = set do
|
|
69
|
+
description 'Command (string) to generate a unified diff with'
|
|
70
|
+
|
|
71
|
+
required true
|
|
72
|
+
default { 'diff -u --color=always' }
|
|
73
|
+
decode { Shellwords.split(_1) if _1.present? }
|
|
74
|
+
end
|
|
75
|
+
|
|
67
76
|
DIFF_TOOL = set do
|
|
68
77
|
description 'Diff tool to apply changes with'
|
|
69
78
|
|
|
70
79
|
default do
|
|
71
|
-
if diff = `which vimdiff`.full?(:chomp)
|
|
80
|
+
if diff = `which 2>/dev/null vimdiff`.full?(:chomp)
|
|
72
81
|
diff
|
|
73
82
|
else
|
|
74
83
|
warn 'Need a diff tool configured via env var "DIFF_TOOL"'
|
|
@@ -184,7 +193,7 @@ module OC
|
|
|
184
193
|
|
|
185
194
|
CTAGS_TOOL = set do
|
|
186
195
|
description 'Tools ctags path'
|
|
187
|
-
default { `which ctags`.full?(:chomp) }
|
|
196
|
+
default { `which 2>/dev/null ctags`.full?(:chomp) }
|
|
188
197
|
check { value.blank? || File.exist?(value) }
|
|
189
198
|
end
|
|
190
199
|
|
|
@@ -231,6 +240,11 @@ module OC
|
|
|
231
240
|
sensitive true
|
|
232
241
|
required { OC::OLLAMA::CHAT::TOOLS::JIRA::URL? }
|
|
233
242
|
end
|
|
243
|
+
|
|
244
|
+
TWG = set do
|
|
245
|
+
description 'Path to the twg CLI binary'
|
|
246
|
+
default { `which 2>/dev/null twg`.full?(:chomp) }
|
|
247
|
+
end
|
|
234
248
|
end
|
|
235
249
|
|
|
236
250
|
module IMAGE_GENERATOR
|
|
@@ -73,6 +73,7 @@ prompts:
|
|
|
73
73
|
80%% of this width in your output of responses, especially when using
|
|
74
74
|
markdown tables.
|
|
75
75
|
- **Session**: %{session_name}
|
|
76
|
+
- Context usage is %{context_usage}.
|
|
76
77
|
- Markdown output is %{markdown}. **Never** output markdown as your
|
|
77
78
|
responses if it is disabled.
|
|
78
79
|
- Voice output is %{voice}. Speak naturally and short if voice
|
|
@@ -240,7 +241,6 @@ prompts:
|
|
|
240
241
|
How does this implementation handle malformed JSON or missing keys in the
|
|
241
242
|
middle of a 2GB file without crashing the process?
|
|
242
243
|
|
|
243
|
-
|
|
244
244
|
What is the time complexity of the current insert logic, and can we implement
|
|
245
245
|
bulk inserts to reduce database round trips?
|
|
246
246
|
|
|
@@ -508,6 +508,10 @@ tools:
|
|
|
508
508
|
result_display_timeout: 3
|
|
509
509
|
default: false
|
|
510
510
|
require_confirmation: true
|
|
511
|
+
execute_jira_twg:
|
|
512
|
+
result_display_timeout: 3
|
|
513
|
+
default: false
|
|
514
|
+
require_confirmation: true
|
|
511
515
|
get_rfc:
|
|
512
516
|
url: "https://www.rfc-editor.org/rfc/rfc%{rfc_id}.txt"
|
|
513
517
|
result_display_timeout: 3
|