rubik 0.3.2 → 0.3.4
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/CHANGELOG.md +77 -0
- data/lib/rubik/buffer/manager.rb +21 -3
- data/lib/rubik/config/defaults.rb +19 -26
- data/lib/rubik/config/store.rb +1 -1
- data/lib/rubik/config.rb +0 -6
- data/lib/rubik/llm/client.rb +102 -72
- data/lib/rubik/orchestrator/goal_ledger/patch.rb +48 -30
- data/lib/rubik/orchestrator/subagent/budget.rb +17 -10
- data/lib/rubik/orchestrator/subagent/instance.rb +14 -2
- data/lib/rubik/orchestrator/subagent/journal.rb +56 -13
- data/lib/rubik/orchestrator/subagent/reuse.rb +3 -3
- data/lib/rubik/orchestrator/subagent/run_cycle.rb +8 -5
- data/lib/rubik/orchestrator/subagent/supervisor.rb +7 -1
- data/lib/rubik/platform/process.rb +7 -7
- data/lib/rubik/platform/sanitize.rb +41 -20
- data/lib/rubik/platform.rb +2 -2
- data/lib/rubik/prompt/doctrine.rb +4 -0
- data/lib/rubik/repl/commands/agent_execution.rb +100 -87
- data/lib/rubik/repl/commands/agent_providers.rb +190 -156
- data/lib/rubik/repl/commands/lsp.rb +212 -179
- data/lib/rubik/repl/commands/subagents.rb +12 -11
- data/lib/rubik/repl/commands/workspace.rb +156 -152
- data/lib/rubik/repl/interrupt_handler.rb +10 -10
- data/lib/rubik/runtime/overlay_storage.rb +59 -2
- data/lib/rubik/sandbox/driver/docker.rb +2 -2
- data/lib/rubik/session/replayer.rb +1 -2
- data/lib/rubik/storage/store.rb +3 -2
- data/lib/rubik/tools/builtins/subagent.rb +7 -2
- data/lib/rubik/tools/fuzzy_matcher.rb +27 -11
- data/lib/rubik/version.rb +1 -1
- metadata +1 -1
|
@@ -34,173 +34,207 @@ module Rubik
|
|
|
34
34
|
when "list", "ls", "status"
|
|
35
35
|
render_provider_catalog(ctx)
|
|
36
36
|
when "add"
|
|
37
|
-
|
|
38
|
-
arg2 = clean_args[2]
|
|
39
|
-
arg3 = clean_args[3]
|
|
40
|
-
|
|
41
|
-
p_type = "openai_compatible"
|
|
42
|
-
p_endpoint = nil
|
|
43
|
-
|
|
44
|
-
if arg2 && (arg2.start_with?("http://") || arg2.start_with?("https://"))
|
|
45
|
-
p_endpoint = arg2
|
|
46
|
-
elsif arg2 && arg3
|
|
47
|
-
p_type = arg2.downcase
|
|
48
|
-
p_endpoint = arg3
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
if p_name.nil? || p_name.empty? || p_endpoint.nil? || p_endpoint.empty?
|
|
52
|
-
ctx.out.puts "Usage: !provider add <name> [type] <endpoint_url> " \
|
|
53
|
-
"[--key=API_KEY | --key-env=ENV_VAR] [--model=NAME] [--context=LIMIT] [--project|--global]"
|
|
54
|
-
elsif !p_endpoint.start_with?("http://") && !p_endpoint.start_with?("https://")
|
|
55
|
-
ctx.out.puts "Error: Endpoint must be a valid HTTP/HTTPS URL (starting with http:// or https://). " \
|
|
56
|
-
"Received: '#{p_endpoint}'\nUsage: !provider add <name> [type] <endpoint_url> " \
|
|
57
|
-
"[--key=API_KEY | --key-env=ENV_VAR] [--model=NAME] [--context=LIMIT] [--project|--global]"
|
|
58
|
-
else
|
|
59
|
-
key_arg = clean_args.find { |a| a.start_with?("--key=") || a.start_with?("--api-key=") }
|
|
60
|
-
key_env_arg = clean_args.find { |a| a.start_with?("--key-env=") }
|
|
61
|
-
model_arg = clean_args.find { |a| a.start_with?("--model=") }
|
|
62
|
-
context_arg = clean_args.find { |a| a.start_with?("--context=") }
|
|
63
|
-
|
|
64
|
-
api_key = key_arg&.split("=", 2)&.last
|
|
65
|
-
key_env = key_env_arg&.split("=", 2)&.last
|
|
66
|
-
m_name = model_arg ? model_arg.split("=", 2).last : "default"
|
|
67
|
-
c_limit = context_arg ? context_arg.split("=", 2).last.to_i : 32_768
|
|
68
|
-
|
|
69
|
-
normalized_endpoint = if ["openai_compatible", "openai"].include?(p_type)
|
|
70
|
-
Rubik::LLM::OpenAICompatibleProvider.normalize_endpoint(p_endpoint)
|
|
71
|
-
else
|
|
72
|
-
p_endpoint
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
p_spec = {
|
|
76
|
-
"type" => p_type,
|
|
77
|
-
"endpoint" => normalized_endpoint,
|
|
78
|
-
"api_key" => api_key,
|
|
79
|
-
"api_key_env" => key_env,
|
|
80
|
-
"models" => [
|
|
81
|
-
{ "name" => m_name, "context_limit" => c_limit }
|
|
82
|
-
]
|
|
83
|
-
}.compact
|
|
84
|
-
|
|
85
|
-
res = ctx.config.set("providers.#{p_name}", p_spec, persist: scope)
|
|
86
|
-
ctx.refresh_llm_registry!
|
|
87
|
-
next if Base.report_persist_failure(ctx, res, scope)
|
|
88
|
-
|
|
89
|
-
ctx.out.puts "Successfully added provider '#{p_name}' (#{p_type} -> #{normalized_endpoint}) " \
|
|
90
|
-
"with model '#{m_name}' [#{Base.scope_label(scope)}]."
|
|
91
|
-
end
|
|
37
|
+
handle_add_provider(clean_args, ctx, scope)
|
|
92
38
|
when "add-model", "model-add"
|
|
93
|
-
|
|
94
|
-
m_name = clean_args[2]
|
|
95
|
-
if p_name.nil? || p_name.empty? || m_name.nil? || m_name.empty?
|
|
96
|
-
ctx.out.puts "Usage: !provider add-model <provider_name> <model_name> [--context=LIMIT] [--project|--global]"
|
|
97
|
-
else
|
|
98
|
-
context_arg = clean_args.find { |a| a.start_with?("--context=") }
|
|
99
|
-
c_limit = context_arg ? context_arg.split("=", 2).last.to_i : 32_768
|
|
100
|
-
|
|
101
|
-
res = ctx.config.upsert_provider_model(p_name, m_name, context_limit: c_limit, persist: scope)
|
|
102
|
-
ctx.refresh_llm_registry!
|
|
103
|
-
if res[:ok]
|
|
104
|
-
ctx.out.puts "Added model '#{m_name}' (#{c_limit} tokens) to provider '#{p_name}' [#{Base.scope_label(scope)}]."
|
|
105
|
-
elsif res[:reason] == :unknown_provider
|
|
106
|
-
ctx.out.puts "Provider '#{p_name}' not found. Add it first via !provider add."
|
|
107
|
-
else
|
|
108
|
-
Base.report_persist_failure(ctx, res, scope)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
39
|
+
handle_add_provider_model(clean_args, ctx, scope)
|
|
111
40
|
when "remove-model", "model-remove", "model-rm"
|
|
112
|
-
|
|
113
|
-
m_name = clean_args[2]
|
|
114
|
-
if p_name.nil? || p_name.empty? || m_name.nil? || m_name.empty?
|
|
115
|
-
ctx.out.puts "Usage: !provider remove-model <provider_name> <model_name> [--project|--global]"
|
|
116
|
-
else
|
|
117
|
-
res = ctx.config.remove_provider_model(p_name, m_name, persist: scope)
|
|
118
|
-
ctx.refresh_llm_registry!
|
|
119
|
-
if res[:ok]
|
|
120
|
-
ctx.out.puts "Removed model '#{m_name}' from provider '#{p_name}' [#{Base.scope_label(scope)}]."
|
|
121
|
-
elsif res[:reason] == :unknown_provider
|
|
122
|
-
ctx.out.puts "Provider '#{p_name}' not found."
|
|
123
|
-
else
|
|
124
|
-
Base.report_persist_failure(ctx, res, scope)
|
|
125
|
-
end
|
|
126
|
-
end
|
|
41
|
+
handle_remove_provider_model(clean_args, ctx, scope)
|
|
127
42
|
when "set"
|
|
128
|
-
|
|
129
|
-
field = clean_args[2]&.downcase
|
|
130
|
-
val = clean_args[3]
|
|
131
|
-
if p_name.nil? || p_name.empty? || field.nil? || val.nil?
|
|
132
|
-
ctx.out.puts "Usage: !provider set <name> <endpoint|key|key-env|type|stream|stream_timeout|open_timeout|tool_native|num_ctx> <value> [--project|--global]"
|
|
133
|
-
else
|
|
134
|
-
p_spec = ctx.config.provider_spec(p_name)
|
|
135
|
-
if p_spec.nil?
|
|
136
|
-
ctx.out.puts "Provider '#{p_name}' not found."
|
|
137
|
-
else
|
|
138
|
-
case field
|
|
139
|
-
when "key", "api_key"
|
|
140
|
-
p_spec["api_key"] = val
|
|
141
|
-
when "key-env", "key_env", "api_key_env"
|
|
142
|
-
p_spec["api_key_env"] = val
|
|
143
|
-
when "endpoint", "url", "host"
|
|
144
|
-
if !val.start_with?("http://") && !val.start_with?("https://")
|
|
145
|
-
ctx.out.puts "Error: Endpoint must be a valid HTTP/HTTPS URL (starting with http:// or https://). Received: '#{val}'"
|
|
146
|
-
next
|
|
147
|
-
end
|
|
148
|
-
p_spec["endpoint"] = val
|
|
149
|
-
when "type"
|
|
150
|
-
p_spec["type"] = val
|
|
151
|
-
else
|
|
152
|
-
# Free-form fields (stream, tool_native, num_ctx, ...) are
|
|
153
|
-
# typed through parse_scalar_value so "false" becomes a
|
|
154
|
-
# real false — a raw "false" String is truthy in Ruby and
|
|
155
|
-
# would silently re-enable the flag it means to disable.
|
|
156
|
-
p_spec[field] = ctx.config.parse_scalar_value(val)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
res = ctx.config.set("providers.#{p_name}", p_spec, persist: scope)
|
|
160
|
-
ctx.refresh_llm_registry!
|
|
161
|
-
next if Base.report_persist_failure(ctx, res, scope)
|
|
162
|
-
|
|
163
|
-
ctx.out.puts "Updated provider '#{p_name}' field '#{field}' to '#{val}' [#{Base.scope_label(scope)}]."
|
|
164
|
-
end
|
|
165
|
-
end
|
|
43
|
+
handle_set_provider_field(clean_args, ctx, scope)
|
|
166
44
|
when "remove", "rm", "delete"
|
|
167
|
-
|
|
168
|
-
if p_name.nil? || p_name.empty?
|
|
169
|
-
ctx.out.puts "Usage: !provider remove <name> [--project|--global]"
|
|
170
|
-
else
|
|
171
|
-
res = ctx.config.remove_provider(p_name, persist: scope)
|
|
172
|
-
ctx.refresh_llm_registry!
|
|
173
|
-
if res[:ok]
|
|
174
|
-
ctx.out.puts "Removed provider '#{p_name}' [#{Base.scope_label(scope)}]."
|
|
175
|
-
elsif res[:reason] == :unknown_provider
|
|
176
|
-
ctx.out.puts "Provider '#{p_name}' not found."
|
|
177
|
-
else
|
|
178
|
-
Base.report_persist_failure(ctx, res, scope)
|
|
179
|
-
end
|
|
180
|
-
end
|
|
45
|
+
handle_remove_provider(clean_args, ctx, scope)
|
|
181
46
|
when "set-context", "context", "limit"
|
|
182
|
-
|
|
183
|
-
m_name = clean_args[2]
|
|
184
|
-
limit_val = clean_args[3]&.to_i
|
|
185
|
-
if p_name.nil? || p_name.empty? || m_name.nil? || m_name.empty? || limit_val.nil? || limit_val <= 0
|
|
186
|
-
ctx.out.puts "Usage: !provider set-context <provider> <model> <limit_in_tokens> [--project|--global]"
|
|
187
|
-
else
|
|
188
|
-
res = ctx.config.upsert_provider_model(p_name, m_name, context_limit: limit_val, persist: scope)
|
|
189
|
-
ctx.refresh_llm_registry!
|
|
190
|
-
if res[:ok]
|
|
191
|
-
ctx.out.puts "Updated context limit for model '#{m_name}' on provider '#{p_name}' to #{limit_val} tokens [#{Base.scope_label(scope)}]."
|
|
192
|
-
elsif res[:reason] == :unknown_provider
|
|
193
|
-
ctx.out.puts "Provider '#{p_name}' not found."
|
|
194
|
-
else
|
|
195
|
-
Base.report_persist_failure(ctx, res, scope)
|
|
196
|
-
end
|
|
197
|
-
end
|
|
47
|
+
handle_set_provider_context(clean_args, ctx, scope)
|
|
198
48
|
else
|
|
199
49
|
ctx.out.puts USAGE_PROVIDER
|
|
200
50
|
end
|
|
201
51
|
end
|
|
202
52
|
end
|
|
203
53
|
|
|
54
|
+
private_class_method def self.handle_add_provider(clean_args, ctx, scope)
|
|
55
|
+
p_name = clean_args[1]&.downcase
|
|
56
|
+
arg2 = clean_args[2]
|
|
57
|
+
arg3 = clean_args[3]
|
|
58
|
+
|
|
59
|
+
p_type = "openai_compatible"
|
|
60
|
+
p_endpoint = nil
|
|
61
|
+
|
|
62
|
+
if arg2 && (arg2.start_with?("http://") || arg2.start_with?("https://"))
|
|
63
|
+
p_endpoint = arg2
|
|
64
|
+
elsif arg2 && arg3
|
|
65
|
+
p_type = arg2.downcase
|
|
66
|
+
p_endpoint = arg3
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if p_name.nil? || p_name.empty? || p_endpoint.nil? || p_endpoint.empty?
|
|
70
|
+
ctx.out.puts "Usage: !provider add <name> [type] <endpoint_url> " \
|
|
71
|
+
"[--key=API_KEY | --key-env=ENV_VAR] [--model=NAME] [--context=LIMIT] [--project|--global]"
|
|
72
|
+
return
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
unless p_endpoint.start_with?("http://") || p_endpoint.start_with?("https://")
|
|
76
|
+
ctx.out.puts "Error: Endpoint must be a valid HTTP/HTTPS URL (starting with http:// or https://). " \
|
|
77
|
+
"Received: '#{p_endpoint}'\nUsage: !provider add <name> [type] <endpoint_url> " \
|
|
78
|
+
"[--key=API_KEY | --key-env=ENV_VAR] [--model=NAME] [--context=LIMIT] [--project|--global]"
|
|
79
|
+
return
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
key_arg = clean_args.find { |a| a.start_with?("--key=") || a.start_with?("--api-key=") }
|
|
83
|
+
key_env_arg = clean_args.find { |a| a.start_with?("--key-env=") }
|
|
84
|
+
model_arg = clean_args.find { |a| a.start_with?("--model=") }
|
|
85
|
+
context_arg = clean_args.find { |a| a.start_with?("--context=") }
|
|
86
|
+
|
|
87
|
+
api_key = key_arg&.split("=", 2)&.last
|
|
88
|
+
key_env = key_env_arg&.split("=", 2)&.last
|
|
89
|
+
m_name = model_arg ? model_arg.split("=", 2).last : "default"
|
|
90
|
+
c_limit = context_arg ? context_arg.split("=", 2).last.to_i : 32_768
|
|
91
|
+
|
|
92
|
+
normalized_endpoint = if ["openai_compatible", "openai"].include?(p_type)
|
|
93
|
+
Rubik::LLM::OpenAICompatibleProvider.normalize_endpoint(p_endpoint)
|
|
94
|
+
else
|
|
95
|
+
p_endpoint
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
p_spec = {
|
|
99
|
+
"type" => p_type,
|
|
100
|
+
"endpoint" => normalized_endpoint,
|
|
101
|
+
"api_key" => api_key,
|
|
102
|
+
"api_key_env" => key_env,
|
|
103
|
+
"models" => [
|
|
104
|
+
{ "name" => m_name, "context_limit" => c_limit }
|
|
105
|
+
]
|
|
106
|
+
}.compact
|
|
107
|
+
|
|
108
|
+
res = ctx.config.set("providers.#{p_name}", p_spec, persist: scope)
|
|
109
|
+
ctx.refresh_llm_registry!
|
|
110
|
+
return if Base.report_persist_failure(ctx, res, scope)
|
|
111
|
+
|
|
112
|
+
ctx.out.puts "Successfully added provider '#{p_name}' (#{p_type} -> #{normalized_endpoint}) " \
|
|
113
|
+
"with model '#{m_name}' [#{Base.scope_label(scope)}]."
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private_class_method def self.handle_add_provider_model(clean_args, ctx, scope)
|
|
117
|
+
p_name = clean_args[1]&.downcase
|
|
118
|
+
m_name = clean_args[2]
|
|
119
|
+
if p_name.nil? || p_name.empty? || m_name.nil? || m_name.empty?
|
|
120
|
+
ctx.out.puts "Usage: !provider add-model <provider_name> <model_name> [--context=LIMIT] [--project|--global]"
|
|
121
|
+
return
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
context_arg = clean_args.find { |a| a.start_with?("--context=") }
|
|
125
|
+
c_limit = context_arg ? context_arg.split("=", 2).last.to_i : 32_768
|
|
126
|
+
|
|
127
|
+
res = ctx.config.upsert_provider_model(p_name, m_name, context_limit: c_limit, persist: scope)
|
|
128
|
+
ctx.refresh_llm_registry!
|
|
129
|
+
if res[:ok]
|
|
130
|
+
ctx.out.puts "Added model '#{m_name}' (#{c_limit} tokens) to provider '#{p_name}' [#{Base.scope_label(scope)}]."
|
|
131
|
+
elsif res[:reason] == :unknown_provider
|
|
132
|
+
ctx.out.puts "Provider '#{p_name}' not found. Add it first via !provider add."
|
|
133
|
+
else
|
|
134
|
+
Base.report_persist_failure(ctx, res, scope)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private_class_method def self.handle_remove_provider_model(clean_args, ctx, scope)
|
|
139
|
+
p_name = clean_args[1]&.downcase
|
|
140
|
+
m_name = clean_args[2]
|
|
141
|
+
if p_name.nil? || p_name.empty? || m_name.nil? || m_name.empty?
|
|
142
|
+
ctx.out.puts "Usage: !provider remove-model <provider_name> <model_name> [--project|--global]"
|
|
143
|
+
return
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
res = ctx.config.remove_provider_model(p_name, m_name, persist: scope)
|
|
147
|
+
ctx.refresh_llm_registry!
|
|
148
|
+
if res[:ok]
|
|
149
|
+
ctx.out.puts "Removed model '#{m_name}' from provider '#{p_name}' [#{Base.scope_label(scope)}]."
|
|
150
|
+
elsif res[:reason] == :unknown_provider
|
|
151
|
+
ctx.out.puts "Provider '#{p_name}' not found."
|
|
152
|
+
else
|
|
153
|
+
Base.report_persist_failure(ctx, res, scope)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
private_class_method def self.handle_set_provider_field(clean_args, ctx, scope)
|
|
158
|
+
p_name = clean_args[1]&.downcase
|
|
159
|
+
field = clean_args[2]&.downcase
|
|
160
|
+
val = clean_args[3]
|
|
161
|
+
if p_name.nil? || p_name.empty? || field.nil? || val.nil?
|
|
162
|
+
ctx.out.puts "Usage: !provider set <name> <endpoint|key|key-env|type|stream|stream_timeout|open_timeout|tool_native|num_ctx> <value> [--project|--global]"
|
|
163
|
+
return
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
p_spec = ctx.config.provider_spec(p_name)
|
|
167
|
+
if p_spec.nil?
|
|
168
|
+
ctx.out.puts "Provider '#{p_name}' not found."
|
|
169
|
+
return
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
case field
|
|
173
|
+
when "key", "api_key"
|
|
174
|
+
p_spec["api_key"] = val
|
|
175
|
+
when "key-env", "key_env", "api_key_env"
|
|
176
|
+
p_spec["api_key_env"] = val
|
|
177
|
+
when "endpoint", "url", "host"
|
|
178
|
+
unless val.start_with?("http://") || val.start_with?("https://")
|
|
179
|
+
ctx.out.puts "Error: Endpoint must be a valid HTTP/HTTPS URL (starting with http:// or https://). Received: '#{val}'"
|
|
180
|
+
return
|
|
181
|
+
end
|
|
182
|
+
p_spec["endpoint"] = val
|
|
183
|
+
when "type"
|
|
184
|
+
p_spec["type"] = val
|
|
185
|
+
else
|
|
186
|
+
# Free-form fields (stream, tool_native, num_ctx, ...) are
|
|
187
|
+
# typed through parse_scalar_value so "false" becomes a
|
|
188
|
+
# real false — a raw "false" String is truthy in Ruby and
|
|
189
|
+
# would silently re-enable the flag it means to disable.
|
|
190
|
+
p_spec[field] = ctx.config.parse_scalar_value(val)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
res = ctx.config.set("providers.#{p_name}", p_spec, persist: scope)
|
|
194
|
+
ctx.refresh_llm_registry!
|
|
195
|
+
return if Base.report_persist_failure(ctx, res, scope)
|
|
196
|
+
|
|
197
|
+
ctx.out.puts "Updated provider '#{p_name}' field '#{field}' to '#{val}' [#{Base.scope_label(scope)}]."
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
private_class_method def self.handle_remove_provider(clean_args, ctx, scope)
|
|
201
|
+
p_name = clean_args[1]&.downcase
|
|
202
|
+
if p_name.nil? || p_name.empty?
|
|
203
|
+
ctx.out.puts "Usage: !provider remove <name> [--project|--global]"
|
|
204
|
+
return
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
res = ctx.config.remove_provider(p_name, persist: scope)
|
|
208
|
+
ctx.refresh_llm_registry!
|
|
209
|
+
if res[:ok]
|
|
210
|
+
ctx.out.puts "Removed provider '#{p_name}' [#{Base.scope_label(scope)}]."
|
|
211
|
+
elsif res[:reason] == :unknown_provider
|
|
212
|
+
ctx.out.puts "Provider '#{p_name}' not found."
|
|
213
|
+
else
|
|
214
|
+
Base.report_persist_failure(ctx, res, scope)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
private_class_method def self.handle_set_provider_context(clean_args, ctx, scope)
|
|
219
|
+
p_name = clean_args[1]&.downcase
|
|
220
|
+
m_name = clean_args[2]
|
|
221
|
+
limit_val = clean_args[3]&.to_i
|
|
222
|
+
if p_name.nil? || p_name.empty? || m_name.nil? || m_name.empty? || limit_val.nil? || limit_val <= 0
|
|
223
|
+
ctx.out.puts "Usage: !provider set-context <provider> <model> <limit_in_tokens> [--project|--global]"
|
|
224
|
+
return
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
res = ctx.config.upsert_provider_model(p_name, m_name, context_limit: limit_val, persist: scope)
|
|
228
|
+
ctx.refresh_llm_registry!
|
|
229
|
+
if res[:ok]
|
|
230
|
+
ctx.out.puts "Updated context limit for model '#{m_name}' on provider '#{p_name}' to #{limit_val} tokens [#{Base.scope_label(scope)}]."
|
|
231
|
+
elsif res[:reason] == :unknown_provider
|
|
232
|
+
ctx.out.puts "Provider '#{p_name}' not found."
|
|
233
|
+
else
|
|
234
|
+
Base.report_persist_failure(ctx, res, scope)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
204
238
|
private_class_method def self.register_providers_command(shell)
|
|
205
239
|
shell.register_command("providers", description: "List all configured LLM providers, endpoints, and models", audience: :both) do |_args, ctx|
|
|
206
240
|
render_provider_catalog(ctx)
|