riffer-rig 0.16.0 → 0.17.0
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 +17 -0
- data/README.md +3 -18
- data/lib/riffer/rig/acp/mapper.rb +14 -2
- data/lib/riffer/rig/acp/servers.rb +42 -0
- data/lib/riffer/rig/acp.rb +17 -56
- data/lib/riffer/rig/cli/flags.rb +5 -0
- data/lib/riffer/rig/cli/sessions.rb +0 -3
- data/lib/riffer/rig/cli.rb +0 -3
- data/lib/riffer/rig/commands/auth.rb +28 -20
- data/lib/riffer/rig/commands/model.rb +37 -34
- data/lib/riffer/rig/commands/reasoning.rb +10 -10
- data/lib/riffer/rig/commands/reload.rb +6 -6
- data/lib/riffer/rig/credentials.rb +46 -14
- data/lib/riffer/rig/directories.rb +7 -0
- data/lib/riffer/rig/extension.rb +1 -0
- data/lib/riffer/rig/headless.rb +3 -2
- data/lib/riffer/rig/loader/extension_source.rb +4 -4
- data/lib/riffer/rig/loader/reasoning.rb +25 -0
- data/lib/riffer/rig/loader/reloader.rb +91 -0
- data/lib/riffer/rig/loader.rb +52 -171
- data/lib/riffer/rig/mcp/auth.rb +11 -22
- data/lib/riffer/rig/mcp/declaration.rb +3 -3
- data/lib/riffer/rig/mcp.rb +0 -8
- data/lib/riffer/rig/prompts/agents_md.rb +1 -1
- data/lib/riffer/rig/provider_setup.rb +22 -3
- data/lib/riffer/rig/runtime/assembly.rb +128 -0
- data/lib/riffer/rig/{tools/bash → runtime}/job.rb +4 -5
- data/lib/riffer/rig/{tools/bash → runtime}/job_log.rb +1 -1
- data/lib/riffer/rig/runtime/job_notices.rb +89 -0
- data/lib/riffer/rig/{tools/bash → runtime}/jobs.rb +38 -29
- data/lib/riffer/rig/runtime/mcp_registrar.rb +4 -3
- data/lib/riffer/rig/runtime/snapshot.rb +18 -0
- data/lib/riffer/rig/runtime/stop_reason.rb +22 -0
- data/lib/riffer/rig/runtime/turn.rb +151 -0
- data/lib/riffer/rig/runtime.rb +108 -347
- data/lib/riffer/rig/sdk.rb +8 -10
- data/lib/riffer/rig/settings/file.rb +3 -3
- data/lib/riffer/rig/settings/model_options.rb +23 -0
- data/lib/riffer/rig/settings.rb +2 -1
- data/lib/riffer/rig/stores/entries.rb +4 -4
- data/lib/riffer/rig/stores/jsonl.rb +1 -1
- data/lib/riffer/rig/stores/message_entry.rb +1 -1
- data/lib/riffer/rig/stores/model_entry.rb +1 -1
- data/lib/riffer/rig/stores/reasoning_entry.rb +1 -1
- data/lib/riffer/rig/stores/recorder.rb +7 -11
- data/lib/riffer/rig/stores/skill_entry.rb +1 -1
- data/lib/riffer/rig/stores/timestamp.rb +6 -0
- data/lib/riffer/rig/terminal/banner.rb +9 -2
- data/lib/riffer/rig/terminal/count.rb +6 -0
- data/lib/riffer/rig/terminal/frame.rb +1 -3
- data/lib/riffer/rig/terminal/message_queue.rb +4 -4
- data/lib/riffer/rig/terminal/picker.rb +5 -2
- data/lib/riffer/rig/terminal/repl.rb +1 -1
- data/lib/riffer/rig/terminal/session.rb +0 -2
- data/lib/riffer/rig/terminal/status_bar.rb +2 -2
- data/lib/riffer/rig/terminal/theme.rb +1 -0
- data/lib/riffer/rig/terminal/ticker.rb +3 -1
- data/lib/riffer/rig/terminal/turn_report.rb +1 -1
- data/lib/riffer/rig/tools/bash/kill.rb +1 -1
- data/lib/riffer/rig/tools/bash/output.rb +3 -2
- data/lib/riffer/rig/tools/bash.rb +3 -4
- data/lib/riffer/rig/version.rb +1 -1
- data/sig/generated/riffer/rig/acp/mapper.rbs +4 -0
- data/sig/generated/riffer/rig/acp/servers.rbs +21 -0
- data/sig/generated/riffer/rig/acp.rbs +4 -19
- data/sig/generated/riffer/rig/cli/flags.rbs +2 -0
- data/sig/generated/riffer/rig/commands/auth.rbs +18 -5
- data/sig/generated/riffer/rig/commands/model.rbs +19 -13
- data/sig/generated/riffer/rig/commands/reasoning.rbs +4 -6
- data/sig/generated/riffer/rig/commands/reload.rbs +4 -4
- data/sig/generated/riffer/rig/credentials.rbs +18 -1
- data/sig/generated/riffer/rig/directories.rbs +5 -0
- data/sig/generated/riffer/rig/loader/reasoning.rbs +13 -0
- data/sig/generated/riffer/rig/loader/reloader.rbs +50 -0
- data/sig/generated/riffer/rig/loader.rbs +13 -88
- data/sig/generated/riffer/rig/mcp/auth.rbs +6 -9
- data/sig/generated/riffer/rig/mcp/declaration.rbs +0 -3
- data/sig/generated/riffer/rig/mcp.rbs +0 -4
- data/sig/generated/riffer/rig/provider_setup.rbs +10 -5
- data/sig/generated/riffer/rig/runtime/assembly.rbs +74 -0
- data/sig/generated/riffer/rig/{tools/bash → runtime}/job.rbs +8 -8
- data/sig/generated/riffer/rig/{tools/bash → runtime}/job_log.rbs +2 -2
- data/sig/generated/riffer/rig/runtime/job_notices.rbs +46 -0
- data/sig/generated/riffer/rig/runtime/jobs.rbs +86 -0
- data/sig/generated/riffer/rig/runtime/snapshot.rbs +12 -0
- data/sig/generated/riffer/rig/runtime/stop_reason.rbs +13 -0
- data/sig/generated/riffer/rig/runtime/turn.rbs +74 -0
- data/sig/generated/riffer/rig/runtime.rbs +45 -148
- data/sig/generated/riffer/rig/sdk.rbs +5 -5
- data/sig/generated/riffer/rig/settings/file.rbs +2 -2
- data/sig/generated/riffer/rig/settings/model_options.rbs +12 -0
- data/sig/generated/riffer/rig/stores/entries.rbs +2 -2
- data/sig/generated/riffer/rig/stores/recorder.rbs +2 -8
- data/sig/generated/riffer/rig/stores/timestamp.rbs +4 -0
- data/sig/generated/riffer/rig/terminal/banner.rbs +4 -0
- data/sig/generated/riffer/rig/terminal/count.rbs +4 -0
- data/sig/generated/riffer/rig/terminal/frame.rbs +0 -2
- data/sig/generated/riffer/rig/terminal/message_queue.rbs +5 -5
- data/sig/generated/riffer/rig/terminal/picker.rbs +4 -1
- data/sig/generated/riffer/rig/terminal/ticker.rbs +2 -0
- data/sig/generated/riffer/rig/tools/bash.rbs +0 -2
- data/sig/manual/riffer/rig/{terminal/tally.rbs → acp/servers.rbs} +2 -2
- data/sig/manual/riffer/rig/{terminal/spinner.rbs → loader/reasoning.rbs} +2 -2
- data/sig/manual/riffer/rig/mcp.rbs +0 -5
- data/sig/manual/riffer/rig/{loader/credentials.rbs → runtime/stop_reason.rbs} +2 -2
- metadata +24 -22
- data/lib/riffer/rig/loader/credentials.rb +0 -23
- data/lib/riffer/rig/terminal/message_queue/message.rb +0 -13
- data/lib/riffer/rig/terminal/reasoning.rb +0 -5
- data/lib/riffer/rig/terminal/spinner.rb +0 -13
- data/lib/riffer/rig/terminal/tally.rb +0 -12
- data/lib/riffer/rig/tools/bash/monotonic_clock.rb +0 -8
- data/sig/generated/riffer/rig/loader/credentials.rbs +0 -12
- data/sig/generated/riffer/rig/terminal/message_queue/message.rbs +0 -10
- data/sig/generated/riffer/rig/terminal/reasoning.rbs +0 -5
- data/sig/generated/riffer/rig/terminal/spinner.rbs +0 -9
- data/sig/generated/riffer/rig/terminal/tally.rbs +0 -7
- data/sig/generated/riffer/rig/tools/bash/jobs.rbs +0 -86
- data/sig/generated/riffer/rig/tools/bash/monotonic_clock.rbs +0 -6
|
@@ -5,7 +5,7 @@ require 'open3'
|
|
|
5
5
|
module Riffer::Rig::Credentials
|
|
6
6
|
extend self
|
|
7
7
|
|
|
8
|
-
PATH =
|
|
8
|
+
PATH = Riffer::Rig::Directories.riffer(Dir.home, 'auth.json') #: String
|
|
9
9
|
|
|
10
10
|
ENTRY_TYPE = 'api_key'
|
|
11
11
|
|
|
@@ -39,6 +39,36 @@ module Riffer::Rig::Credentials
|
|
|
39
39
|
Resolution.new(values: resolution.values, missing: missing)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# @rbs identifier: String | Symbol
|
|
43
|
+
# @rbs host: Riffer::Rig::Hosts::_Host
|
|
44
|
+
# @rbs env: Riffer::Rig::Env
|
|
45
|
+
# @rbs auth_path: String
|
|
46
|
+
# @rbs label: String
|
|
47
|
+
# @rbs setup: Riffer::Rig::ProviderSetup
|
|
48
|
+
# @rbs config: Riffer::Config?
|
|
49
|
+
# @rbs settings_path: String
|
|
50
|
+
# @rbs known: Hash[Symbol, String]
|
|
51
|
+
# @rbs return: (Hash[Symbol, String] | String)
|
|
52
|
+
def install_values(
|
|
53
|
+
identifier,
|
|
54
|
+
host:,
|
|
55
|
+
env:,
|
|
56
|
+
auth_path:,
|
|
57
|
+
label: identifier.to_s,
|
|
58
|
+
setup: Riffer::Rig::ProviderSetup.for(identifier),
|
|
59
|
+
config: Riffer.config,
|
|
60
|
+
settings_path: Riffer::Rig::Settings::File::PATH,
|
|
61
|
+
known: {}
|
|
62
|
+
)
|
|
63
|
+
resolution = install(identifier, host:, setup:, env:, config:, auth_path:, settings_path:, known:)
|
|
64
|
+
return resolution if resolution.is_a?(String)
|
|
65
|
+
return resolution.values if resolution.missing.empty?
|
|
66
|
+
|
|
67
|
+
"#{label} has no #{setup.missing_fields(resolution.missing)}; " \
|
|
68
|
+
'set it in the environment or run riffer interactively to paste it' \
|
|
69
|
+
"#{" (create one at #{setup.url})" if setup.url}"
|
|
70
|
+
end
|
|
71
|
+
|
|
42
72
|
# A registered extension provider has no Riffer::Config member, so the
|
|
43
73
|
# assignment stays built-ins-only.
|
|
44
74
|
# @rbs identifier: String | Symbol
|
|
@@ -107,7 +137,7 @@ module Riffer::Rig::Credentials
|
|
|
107
137
|
return stored if stored.is_a?(String)
|
|
108
138
|
return :stored if secret_fields.any? { |field| stored.key?(field.name.to_s) }
|
|
109
139
|
|
|
110
|
-
setup.chain ? :chain : :missing
|
|
140
|
+
setup.chain? ? :chain : :missing
|
|
111
141
|
end
|
|
112
142
|
|
|
113
143
|
private
|
|
@@ -119,14 +149,7 @@ module Riffer::Rig::Credentials
|
|
|
119
149
|
# @rbs auth_path: String
|
|
120
150
|
# @rbs settings_path: String
|
|
121
151
|
# @rbs return: (Resolution | String)
|
|
122
|
-
def resolve(
|
|
123
|
-
identifier,
|
|
124
|
-
host:,
|
|
125
|
-
setup: Riffer::Rig::ProviderSetup.for(identifier),
|
|
126
|
-
env: Riffer::Rig::Env.new,
|
|
127
|
-
auth_path: PATH,
|
|
128
|
-
settings_path: Riffer::Rig::Settings::File::PATH
|
|
129
|
-
)
|
|
152
|
+
def resolve(identifier, host:, setup:, env:, auth_path:, settings_path:)
|
|
130
153
|
secrets = stored_secrets(identifier, auth_path)
|
|
131
154
|
return secrets if secrets.is_a?(String)
|
|
132
155
|
|
|
@@ -136,13 +159,22 @@ module Riffer::Rig::Credentials
|
|
|
136
159
|
found = setup.fields.to_h do |field|
|
|
137
160
|
[field.name, env.value_for(field) || stored_value(field, secrets, plain, env) || field.fallback&.call]
|
|
138
161
|
end.compact
|
|
139
|
-
|
|
140
|
-
|
|
162
|
+
unresolved = setup.fields.select(&:required).reject { |field| found.key?(field.name) }
|
|
163
|
+
pending = outside_chain(unresolved, setup)
|
|
164
|
+
answers = ask_for(pending, identifier, host)
|
|
141
165
|
refusal = store(identifier, answers, setup:, auth_path:, settings_path:)
|
|
142
166
|
return refusal if refusal
|
|
143
167
|
|
|
144
|
-
values
|
|
145
|
-
|
|
168
|
+
Resolution.new(values: found.merge(answers), missing: pending.map(&:name) - answers.keys)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# @rbs fields: Array[Riffer::Rig::ProviderSetup::Field]
|
|
172
|
+
# @rbs setup: Riffer::Rig::ProviderSetup
|
|
173
|
+
# @rbs return: Array[Riffer::Rig::ProviderSetup::Field]
|
|
174
|
+
def outside_chain(fields, setup)
|
|
175
|
+
return fields unless fields.any?(&:secret) && setup.chain?
|
|
176
|
+
|
|
177
|
+
fields.reject(&:secret)
|
|
146
178
|
end
|
|
147
179
|
|
|
148
180
|
# @rbs fields: Array[Riffer::Rig::ProviderSetup::Field]
|
|
@@ -17,4 +17,11 @@ module Riffer::Rig::Directories
|
|
|
17
17
|
root = dirs.index { |dir| File.exist?(File.join(dir, '.git')) } || 0
|
|
18
18
|
dirs.take(root + 1)
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
# @rbs root: String
|
|
22
|
+
# @rbs *names: String
|
|
23
|
+
# @rbs return: String
|
|
24
|
+
def riffer(root, *names)
|
|
25
|
+
File.join(root, '.riffer', *names)
|
|
26
|
+
end
|
|
20
27
|
end
|
data/lib/riffer/rig/extension.rb
CHANGED
data/lib/riffer/rig/headless.rb
CHANGED
|
@@ -76,6 +76,7 @@ class Riffer::Rig::Headless
|
|
|
76
76
|
@json = json
|
|
77
77
|
@job_idle_timeout = job_idle_timeout
|
|
78
78
|
@stdin_timeout = stdin_timeout
|
|
79
|
+
@streamed_text = false
|
|
79
80
|
@wrote_text = false
|
|
80
81
|
@break_before_text = false
|
|
81
82
|
end
|
|
@@ -245,8 +246,8 @@ class Riffer::Rig::Headless
|
|
|
245
246
|
return 0 if stop_reason == :completed
|
|
246
247
|
|
|
247
248
|
@error.puts("turn ended: #{stop_reason}") if stop_reason
|
|
248
|
-
return 130 if stop_reason == Riffer::Rig::Runtime::INTERRUPT_CANCELLED
|
|
249
|
-
return 3 if stop_reason && Riffer::Rig::Runtime::STOPPED_WITHOUT_COMPLETING.include?(stop_reason)
|
|
249
|
+
return 130 if stop_reason == Riffer::Rig::Runtime::StopReason::INTERRUPT_CANCELLED
|
|
250
|
+
return 3 if stop_reason && Riffer::Rig::Runtime::StopReason::STOPPED_WITHOUT_COMPLETING.include?(stop_reason)
|
|
250
251
|
|
|
251
252
|
1
|
|
252
253
|
end
|
|
@@ -80,12 +80,12 @@ class Riffer::Rig::Loader::ExtensionSource
|
|
|
80
80
|
|
|
81
81
|
# @rbs return: String
|
|
82
82
|
def home_rig_path
|
|
83
|
-
File.expand_path(
|
|
83
|
+
File.expand_path(Riffer::Rig::Directories.riffer(@home, 'rig.rb'))
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
# @rbs return: String
|
|
87
87
|
def project_rig_path
|
|
88
|
-
File.expand_path(
|
|
88
|
+
File.expand_path(Riffer::Rig::Directories.riffer(@cwd, 'rig.rb'))
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
# @rbs autoload: bool
|
|
@@ -135,7 +135,7 @@ class Riffer::Rig::Loader::ExtensionSource
|
|
|
135
135
|
|
|
136
136
|
# @rbs return: String
|
|
137
137
|
def trust_path
|
|
138
|
-
|
|
138
|
+
Riffer::Rig::Directories.riffer(@home, 'trust.json')
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
# @rbs path: String
|
|
@@ -171,7 +171,7 @@ class Riffer::Rig::Loader::ExtensionSource
|
|
|
171
171
|
# @rbs feature: String
|
|
172
172
|
# @rbs return: bool
|
|
173
173
|
def local?(feature)
|
|
174
|
-
roots = [File.expand_path(
|
|
174
|
+
roots = [File.expand_path(Riffer::Rig::Directories.riffer(@home)), File.expand_path(@cwd)] #: Array[String]
|
|
175
175
|
roots.any? { |root| feature.start_with?("#{root}#{File::SEPARATOR}") }
|
|
176
176
|
end
|
|
177
177
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Riffer::Rig::Loader::Reasoning
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
# @rbs flag: String?
|
|
7
|
+
# @rbs env: String?
|
|
8
|
+
# @rbs return: String?
|
|
9
|
+
def forced(flag, env)
|
|
10
|
+
flag || env
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @rbs flag: String?
|
|
14
|
+
# @rbs env: String?
|
|
15
|
+
# @rbs return: String?
|
|
16
|
+
def rejection(flag, env)
|
|
17
|
+
level = forced(flag, env)
|
|
18
|
+
return unless level
|
|
19
|
+
|
|
20
|
+
rejection = Riffer::Rig::Settings::ModelOptions.reasoning_rejection(level)
|
|
21
|
+
return unless rejection
|
|
22
|
+
|
|
23
|
+
flag ? rejection : "RIFFER_REASONING: #{rejection}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Loader::Reloader
|
|
4
|
+
# @rbs @source: Riffer::Rig::Loader::ExtensionSource
|
|
5
|
+
# @rbs @host: Riffer::Rig::Hosts::_Host
|
|
6
|
+
# @rbs @settings: ^() -> (Hash[Symbol, untyped] | String)
|
|
7
|
+
# @rbs @credentials: ^(String) -> (Hash[Symbol, String] | String)
|
|
8
|
+
# @rbs @extensions: ^(Riffer::Rig::Settings::Document, Array[Riffer::Rig::Extension]) -> Array[Riffer::Rig::Extension]
|
|
9
|
+
# @rbs @rediscovers: bool
|
|
10
|
+
# @rbs @mode: (:auto | :manual | nil)
|
|
11
|
+
# @rbs @file_state: Hash[String, Time?]
|
|
12
|
+
|
|
13
|
+
# @rbs source: Riffer::Rig::Loader::ExtensionSource
|
|
14
|
+
# @rbs host: Riffer::Rig::Hosts::_Host
|
|
15
|
+
# @rbs settings: ^() -> (Hash[Symbol, untyped] | String)
|
|
16
|
+
# @rbs credentials: ^(String) -> (Hash[Symbol, String] | String)
|
|
17
|
+
# @rbs extensions: ^(Riffer::Rig::Settings::Document, Array[Riffer::Rig::Extension]) -> Array[Riffer::Rig::Extension]
|
|
18
|
+
# @rbs rediscovers: bool
|
|
19
|
+
# @rbs mode: (:auto | :manual | nil)
|
|
20
|
+
# @rbs return: void
|
|
21
|
+
def initialize(source:, host:, settings:, credentials:, extensions:, rediscovers:, mode:)
|
|
22
|
+
@source = source
|
|
23
|
+
@host = host
|
|
24
|
+
@settings = settings
|
|
25
|
+
@credentials = credentials
|
|
26
|
+
@extensions = extensions
|
|
27
|
+
@rediscovers = rediscovers
|
|
28
|
+
@mode = mode
|
|
29
|
+
@file_state = file_state
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @rbs runtime: Riffer::Rig::Runtime
|
|
33
|
+
# @rbs force: bool
|
|
34
|
+
# @rbs return: (Riffer::Rig::Runtime | String)
|
|
35
|
+
def reload(runtime, force: false)
|
|
36
|
+
return runtime if !force && file_state == @file_state
|
|
37
|
+
return runtime if !force && manual?
|
|
38
|
+
|
|
39
|
+
outcome = perform(runtime)
|
|
40
|
+
# Every attempt advances the state, failed ones included, so a failure is
|
|
41
|
+
# not retried at every request.
|
|
42
|
+
@file_state = file_state
|
|
43
|
+
outcome
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# @rbs return: bool
|
|
49
|
+
def manual?
|
|
50
|
+
return @mode == :manual if @mode
|
|
51
|
+
|
|
52
|
+
settings = @settings.call
|
|
53
|
+
settings.is_a?(Hash) && Riffer::Rig::Settings::Document.new(settings).reload == :manual
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @rbs runtime: Riffer::Rig::Runtime
|
|
57
|
+
# @rbs return: (Riffer::Rig::Runtime | String)
|
|
58
|
+
def perform(runtime)
|
|
59
|
+
settings = @settings.call
|
|
60
|
+
return notify_failure(settings) if settings.is_a?(String)
|
|
61
|
+
|
|
62
|
+
rediscovered = @rediscovers ? @source.rediscover : [] #: Array[Riffer::Rig::Extension] | String
|
|
63
|
+
return notify_failure(rediscovered) if rediscovered.is_a?(String)
|
|
64
|
+
|
|
65
|
+
candidates = @extensions.call(Riffer::Rig::Settings::Document.new(settings), rediscovered)
|
|
66
|
+
extensions = @source.validated(candidates, settings)
|
|
67
|
+
provider = runtime.model.partition('/').first
|
|
68
|
+
credentials = @credentials.call(provider)
|
|
69
|
+
return notify_failure(credentials) if credentials.is_a?(String)
|
|
70
|
+
|
|
71
|
+
runtime.merge_credentials(provider.to_sym, credentials)
|
|
72
|
+
runtime.rebuild(extensions: extensions, settings: settings)
|
|
73
|
+
runtime
|
|
74
|
+
rescue StandardError => e
|
|
75
|
+
# The automatic check runs mid-turn with nothing else to catch a raise,
|
|
76
|
+
# and a raised attempt must still advance the file state.
|
|
77
|
+
notify_failure(e.message)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @rbs message: String
|
|
81
|
+
# @rbs return: String
|
|
82
|
+
def notify_failure(message)
|
|
83
|
+
@host.notify(message, level: :error)
|
|
84
|
+
message
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @rbs return: Hash[String, Time?]
|
|
88
|
+
def file_state
|
|
89
|
+
@rediscovers ? @source.file_state : {}
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/riffer/rig/loader.rb
CHANGED
|
@@ -10,12 +10,8 @@ class Riffer::Rig::Loader
|
|
|
10
10
|
# @rbs @riffer_config: Riffer::Config
|
|
11
11
|
# @rbs @store: Riffer::Rig::Stores::_Store | nil
|
|
12
12
|
# @rbs @source: Riffer::Rig::Loader::ExtensionSource
|
|
13
|
-
# @rbs @
|
|
14
|
-
# @rbs @
|
|
15
|
-
# @rbs @extra_extensions: Array[Riffer::Rig::Extension]
|
|
16
|
-
# @rbs @recorder_extensions: Array[Riffer::Rig::Extension]
|
|
17
|
-
# @rbs @file_state: Hash[String, Time?]?
|
|
18
|
-
# @rbs @reload_mode: (:auto | :manual | nil)
|
|
13
|
+
# @rbs @reloaders: ObjectSpace::WeakKeyMap[Riffer::Rig::Runtime, Riffer::Rig::Loader::Reloader]
|
|
14
|
+
# @rbs @sdk: singleton(Riffer::Rig::SDK)
|
|
19
15
|
|
|
20
16
|
# @rbs cwd: String
|
|
21
17
|
# @rbs host: Riffer::Rig::Hosts::_Host
|
|
@@ -26,7 +22,7 @@ class Riffer::Rig::Loader
|
|
|
26
22
|
# @rbs options: Riffer::Rig::Loader::Options
|
|
27
23
|
# @rbs reload: (:auto | :manual | nil)
|
|
28
24
|
# @rbs extra_extensions: Array[Riffer::Rig::Extension]
|
|
29
|
-
# @rbs snapshot: Hash[Symbol, untyped]?
|
|
25
|
+
# @rbs snapshot: (Riffer::Rig::Runtime::Snapshot | Hash[Symbol, untyped])?
|
|
30
26
|
# @rbs return: Riffer::Rig::Runtime
|
|
31
27
|
def self.runtime(
|
|
32
28
|
cwd:,
|
|
@@ -49,6 +45,7 @@ class Riffer::Rig::Loader
|
|
|
49
45
|
# @rbs home: String
|
|
50
46
|
# @rbs riffer_config: Riffer::Config
|
|
51
47
|
# @rbs store: Riffer::Rig::Stores::_Store | nil
|
|
48
|
+
# @rbs sdk: singleton(Riffer::Rig::SDK)
|
|
52
49
|
# @rbs return: void
|
|
53
50
|
def initialize(
|
|
54
51
|
cwd:,
|
|
@@ -56,7 +53,8 @@ class Riffer::Rig::Loader
|
|
|
56
53
|
env: Riffer::Rig::Env.load,
|
|
57
54
|
home: Dir.home,
|
|
58
55
|
riffer_config: Riffer.config,
|
|
59
|
-
store: Riffer::Rig::Stores::JSONL.new(home: home)
|
|
56
|
+
store: Riffer::Rig::Stores::JSONL.new(home: home),
|
|
57
|
+
sdk: Riffer::Rig::SDK
|
|
60
58
|
)
|
|
61
59
|
raise ConfigurationError, env.message if env.is_a?(Riffer::Rig::Env::Invalid)
|
|
62
60
|
|
|
@@ -66,18 +64,15 @@ class Riffer::Rig::Loader
|
|
|
66
64
|
@home = home
|
|
67
65
|
@riffer_config = riffer_config
|
|
68
66
|
@store = store
|
|
67
|
+
@sdk = sdk
|
|
69
68
|
@source = Riffer::Rig::Loader::ExtensionSource.new(cwd: cwd, home: home, host: host)
|
|
70
|
-
@
|
|
71
|
-
@extensions_enabled = true
|
|
72
|
-
@recorder_extensions = []
|
|
73
|
-
@file_state = nil
|
|
74
|
-
@reload_mode = nil
|
|
69
|
+
@reloaders = ObjectSpace::WeakKeyMap.new
|
|
75
70
|
end
|
|
76
71
|
|
|
77
72
|
# @rbs options: Riffer::Rig::Loader::Options
|
|
78
73
|
# @rbs reload: (:auto | :manual | nil)
|
|
79
74
|
# @rbs extra_extensions: Array[Riffer::Rig::Extension]
|
|
80
|
-
# @rbs snapshot: Hash[Symbol, untyped]?
|
|
75
|
+
# @rbs snapshot: (Riffer::Rig::Runtime::Snapshot | Hash[Symbol, untyped])?
|
|
81
76
|
# @rbs return: Riffer::Rig::Runtime
|
|
82
77
|
def runtime(options = Options.new, reload: nil, extra_extensions: [], snapshot: nil)
|
|
83
78
|
settings = merged_settings
|
|
@@ -85,31 +80,27 @@ class Riffer::Rig::Loader
|
|
|
85
80
|
|
|
86
81
|
document = Riffer::Rig::Settings::Document.new(settings)
|
|
87
82
|
selected = select_model(options.model || @env.model || document.model)
|
|
88
|
-
|
|
83
|
+
rejection = Riffer::Rig::Loader::Reasoning.rejection(options.reasoning, @env.reasoning)
|
|
84
|
+
raise ConfigurationError, rejection if rejection
|
|
85
|
+
|
|
89
86
|
provider = Riffer::Rig::Settings::ModelOptions.provider_for(selected).to_s
|
|
90
87
|
ensure_sdk(provider)
|
|
91
88
|
credentials = install_credentials(provider)
|
|
92
89
|
raise ConfigurationError, credentials if credentials.is_a?(String)
|
|
93
90
|
|
|
94
91
|
@riffer_config.catalog_files = document.catalog_files
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@reload_mode = reload
|
|
98
|
-
@stripped = { skills: options.skills, agents_md: options.agents_md }.reject { |_name, kept| kept }.keys
|
|
99
|
-
loaded = bundled(document)
|
|
100
|
-
loaded += @source.discover(autoload: document.autoload) if options.extensions
|
|
101
|
-
loaded += @extra_extensions
|
|
92
|
+
stripped = { skills: options.skills, agents_md: options.agents_md }.reject { |_name, kept| kept }.keys
|
|
93
|
+
discovered = options.extensions ? @source.discover(autoload: document.autoload) : [] #: Array[Riffer::Rig::Extension]
|
|
102
94
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
95
|
+
saved = snapshot && Riffer::Rig::Runtime::Snapshot.of(snapshot)
|
|
96
|
+
recorder = recorder_for(document, resumed: !saved.nil?)
|
|
97
|
+
recording = recorder ? [Riffer::Rig::Stores::Recorder.extension(recorder)] : [] #: Array[Riffer::Rig::Extension]
|
|
106
98
|
|
|
107
|
-
|
|
108
|
-
reasoning = forced_reasoning(options, snapshot)
|
|
99
|
+
saved = snapshot_without_forced(saved, options)
|
|
109
100
|
|
|
110
101
|
built = Riffer::Rig::Runtime.new(
|
|
111
102
|
selected,
|
|
112
|
-
extensions:
|
|
103
|
+
extensions: bundled(document, stripped) + discovered + extra_extensions + recording,
|
|
113
104
|
tools: options.tools,
|
|
114
105
|
settings: settings,
|
|
115
106
|
host: @host,
|
|
@@ -118,17 +109,29 @@ class Riffer::Rig::Loader
|
|
|
118
109
|
credentials: { provider.to_sym => credentials },
|
|
119
110
|
env: @env,
|
|
120
111
|
auth_path: home_auth_path,
|
|
121
|
-
|
|
112
|
+
settings_path: home_settings_path,
|
|
113
|
+
reasoning: Riffer::Rig::Loader::Reasoning.forced(options.reasoning, @env.reasoning),
|
|
122
114
|
riffer_config: @riffer_config,
|
|
123
115
|
model_options: Riffer::Rig::Settings::ModelOptions.model_options(selected),
|
|
124
116
|
native_tools: document.native_tools,
|
|
125
|
-
snapshot:
|
|
117
|
+
snapshot: saved
|
|
126
118
|
)
|
|
127
|
-
|
|
119
|
+
reloader = Riffer::Rig::Loader::Reloader.new(
|
|
120
|
+
source: @source,
|
|
121
|
+
host: @host,
|
|
122
|
+
settings: -> { merged_settings },
|
|
123
|
+
credentials: ->(name) { install_credentials(name) },
|
|
124
|
+
extensions: ->(current, found) { bundled(current, stripped) + extra_extensions + found + recording },
|
|
125
|
+
rediscovers: options.extensions,
|
|
126
|
+
mode: reload
|
|
127
|
+
)
|
|
128
|
+
@reloaders[built] = reloader
|
|
129
|
+
built.install_command(Riffer::Rig::Commands::Reload.command(reloader))
|
|
128
130
|
store = @store
|
|
129
131
|
built.install_command(Riffer::Rig::Commands::Trace.command(store)) if store && recorder
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
unless (reload || document.reload) == :manual
|
|
133
|
+
built.install_reload_check(->(runtime) { _ = reloader.reload(runtime) })
|
|
134
|
+
end
|
|
132
135
|
recorder&.attach(built)
|
|
133
136
|
built
|
|
134
137
|
end
|
|
@@ -145,8 +148,6 @@ class Riffer::Rig::Loader
|
|
|
145
148
|
resume(latest.id, options)
|
|
146
149
|
end
|
|
147
150
|
|
|
148
|
-
# A resume carries the history over and nothing else: the settings,
|
|
149
|
-
# credentials, extensions and tools are today's.
|
|
150
151
|
# @rbs id: String
|
|
151
152
|
# @rbs options: Riffer::Rig::Loader::Options
|
|
152
153
|
# @rbs extra_extensions: Array[Riffer::Rig::Extension]
|
|
@@ -190,56 +191,22 @@ class Riffer::Rig::Loader
|
|
|
190
191
|
store.delete(id)
|
|
191
192
|
end
|
|
192
193
|
|
|
193
|
-
# Without force, a no-op while the tracked file set and the two rig.rb paths
|
|
194
|
-
# look as the last discovery left them and the reload mode is not manual —
|
|
195
|
-
# the entry point the automatic trigger (#133) calls. The snapshot advances
|
|
196
|
-
# on every attempt, failed ones included, so a failure does not retry every
|
|
197
|
-
# request. Failures are notified once and reported as the message, the way
|
|
198
|
-
# Settings::ModelOptions.rejection reports a rejection; the Runtime is the success value.
|
|
199
194
|
# @rbs runtime: Riffer::Rig::Runtime
|
|
200
195
|
# @rbs force: bool
|
|
201
196
|
# @rbs return: (Riffer::Rig::Runtime | String)
|
|
202
197
|
def reload(runtime, force: false)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
outcome = perform_reload(runtime)
|
|
207
|
-
@file_state = file_state
|
|
208
|
-
outcome
|
|
198
|
+
reloader = @reloaders[runtime]
|
|
199
|
+
reloader ? reloader.reload(runtime, force: force) : runtime
|
|
209
200
|
end
|
|
210
201
|
|
|
211
202
|
private
|
|
212
203
|
|
|
213
|
-
# The reload mode a build runs under: the keyword wins over the setting,
|
|
214
|
-
# the setting over the default.
|
|
215
|
-
# @rbs document: Riffer::Rig::Settings::Document
|
|
216
|
-
# @rbs return: (:auto | :manual)
|
|
217
|
-
def reload_mode(document)
|
|
218
|
-
@reload_mode || document.reload
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
# @rbs return: bool
|
|
222
|
-
def manual_reload?
|
|
223
|
-
return true if @reload_mode == :manual
|
|
224
|
-
|
|
225
|
-
settings = merged_settings
|
|
226
|
-
settings.is_a?(Hash) && reload_mode(Riffer::Rig::Settings::Document.new(settings)) == :manual
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# The automatic trigger: runs at every before_request boundary, so the
|
|
230
|
-
# change check decides and a no-op costs the stat alone.
|
|
231
|
-
# @rbs runtime: Riffer::Rig::Runtime
|
|
232
|
-
# @rbs return: void
|
|
233
|
-
def automatic_reload(runtime)
|
|
234
|
-
_ = reload(runtime)
|
|
235
|
-
end
|
|
236
|
-
|
|
237
204
|
# @rbs return: (Hash[Symbol, untyped] | String)
|
|
238
205
|
def merged_settings
|
|
239
206
|
home = read_settings(home_settings_path)
|
|
240
207
|
return home if home.is_a?(String)
|
|
241
208
|
|
|
242
|
-
project = read_settings(
|
|
209
|
+
project = read_settings(Riffer::Rig::Directories.riffer(@cwd, 'settings.json'))
|
|
243
210
|
return project if project.is_a?(String)
|
|
244
211
|
|
|
245
212
|
Riffer::Rig::Settings.merge(home, project)
|
|
@@ -254,57 +221,11 @@ class Riffer::Rig::Loader
|
|
|
254
221
|
"#{path} sets \"model\" to a value that is not a string"
|
|
255
222
|
end
|
|
256
223
|
|
|
257
|
-
# Notifies every failure once and reports its message, unexpected raises
|
|
258
|
-
# included — the automatic check runs mid-turn with no other isolation, and a
|
|
259
|
-
# raised attempt must still advance the snapshot in reload. A rebuilt Runtime
|
|
260
|
-
# is the success value.
|
|
261
|
-
# @rbs runtime: Riffer::Rig::Runtime
|
|
262
|
-
# @rbs return: (Riffer::Rig::Runtime | String)
|
|
263
|
-
def perform_reload(runtime)
|
|
264
|
-
settings = merged_settings
|
|
265
|
-
return notify_failure(settings) if settings.is_a?(String)
|
|
266
|
-
|
|
267
|
-
candidates = reload_candidates(Riffer::Rig::Settings::Document.new(settings))
|
|
268
|
-
return notify_failure(candidates) if candidates.is_a?(String)
|
|
269
|
-
|
|
270
|
-
extensions = @source.validated(candidates + @recorder_extensions, settings)
|
|
271
|
-
provider = runtime.model.partition('/').first
|
|
272
|
-
credentials = install_credentials(provider)
|
|
273
|
-
return notify_failure(credentials) if credentials.is_a?(String)
|
|
274
|
-
|
|
275
|
-
runtime.merge_credentials(provider.to_sym, credentials)
|
|
276
|
-
runtime.rebuild(extensions: extensions, settings: settings)
|
|
277
|
-
runtime
|
|
278
|
-
rescue StandardError => e
|
|
279
|
-
notify_failure(e.message)
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
# @rbs message: String
|
|
283
|
-
# @rbs return: String
|
|
284
|
-
def notify_failure(message)
|
|
285
|
-
@host.notify(message, level: :error)
|
|
286
|
-
message
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
# @rbs document: Riffer::Rig::Settings::Document
|
|
290
|
-
# @rbs return: (Array[Riffer::Rig::Extension] | String)
|
|
291
|
-
def reload_candidates(document)
|
|
292
|
-
candidates = bundled(document) + @extra_extensions
|
|
293
|
-
return candidates unless @extensions_enabled
|
|
294
|
-
|
|
295
|
-
rediscovered = @source.rediscover
|
|
296
|
-
rediscovered.is_a?(String) ? rediscovered : candidates + rediscovered
|
|
297
|
-
end
|
|
298
|
-
|
|
299
224
|
# @rbs document: Riffer::Rig::Settings::Document
|
|
225
|
+
# @rbs stripped: Array[Symbol]
|
|
300
226
|
# @rbs return: Array[Riffer::Rig::Extension]
|
|
301
|
-
def bundled(document)
|
|
302
|
-
Riffer::Rig::Bundled::BY_NAME.except(*document.disabled.map(&:to_sym),
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
# @rbs return: Hash[String, Time?]
|
|
306
|
-
def file_state
|
|
307
|
-
@extensions_enabled ? @source.file_state : {}
|
|
227
|
+
def bundled(document, stripped)
|
|
228
|
+
Riffer::Rig::Bundled::BY_NAME.except(*document.disabled.map(&:to_sym), *stripped).values
|
|
308
229
|
end
|
|
309
230
|
|
|
310
231
|
# @rbs configured: String?
|
|
@@ -348,14 +269,14 @@ class Riffer::Rig::Loader
|
|
|
348
269
|
return unless sdk
|
|
349
270
|
|
|
350
271
|
gem, requirement = sdk
|
|
351
|
-
message =
|
|
272
|
+
message = @sdk.ensure(gem, requirement, host: @host)
|
|
352
273
|
raise ConfigurationError, message if message
|
|
353
274
|
end
|
|
354
275
|
|
|
355
276
|
# @rbs provider: String
|
|
356
277
|
# @rbs return: (Hash[Symbol, String] | String)
|
|
357
278
|
def install_credentials(provider)
|
|
358
|
-
Riffer::Rig::
|
|
279
|
+
Riffer::Rig::Credentials.install_values(
|
|
359
280
|
provider,
|
|
360
281
|
host: @host,
|
|
361
282
|
env: @env,
|
|
@@ -367,65 +288,25 @@ class Riffer::Rig::Loader
|
|
|
367
288
|
|
|
368
289
|
# @rbs return: String
|
|
369
290
|
def home_settings_path
|
|
370
|
-
|
|
291
|
+
Riffer::Rig::Directories.riffer(@home, 'settings.json')
|
|
371
292
|
end
|
|
372
293
|
|
|
373
294
|
# @rbs return: String
|
|
374
295
|
def home_auth_path
|
|
375
|
-
|
|
376
|
-
end
|
|
377
|
-
|
|
378
|
-
# A reasoning level from the flag or the environment is refused, the way a
|
|
379
|
-
# bad model string is; one from the settings document or a session's recorded
|
|
380
|
-
# switch falls back to the model's default instead.
|
|
381
|
-
# @rbs options: Riffer::Rig::Loader::Options
|
|
382
|
-
# @rbs return: void
|
|
383
|
-
def check_reasoning(options)
|
|
384
|
-
forced = options.reasoning || @env.reasoning
|
|
385
|
-
return unless forced
|
|
386
|
-
|
|
387
|
-
rejection = Riffer::Rig::Settings::ModelOptions.reasoning_rejection(forced)
|
|
388
|
-
return unless rejection
|
|
389
|
-
|
|
390
|
-
raise ConfigurationError, options.reasoning ? rejection : "RIFFER_REASONING: #{rejection}"
|
|
391
|
-
end
|
|
392
|
-
|
|
393
|
-
# The reasoning in effect for the build, over the settings document: the flag,
|
|
394
|
-
# then the session's recorded switch, then RIFFER_REASONING.
|
|
395
|
-
# @rbs options: Riffer::Rig::Loader::Options
|
|
396
|
-
# @rbs snapshot: Hash[Symbol, untyped]?
|
|
397
|
-
# @rbs return: String?
|
|
398
|
-
def forced_reasoning(options, snapshot)
|
|
399
|
-
options.reasoning || recorded_reasoning(snapshot) || @env.reasoning
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
# @rbs snapshot: Hash[Symbol, untyped]?
|
|
403
|
-
# @rbs return: String?
|
|
404
|
-
def recorded_reasoning(snapshot)
|
|
405
|
-
saved = snapshot && snapshot[:reasoning]
|
|
406
|
-
Riffer::Rig::Settings::ModelOptions.reasoning_level(saved) if saved.is_a?(String)
|
|
407
|
-
end
|
|
408
|
-
|
|
409
|
-
# A settings level riffer does not accept silently keeps the model's default.
|
|
410
|
-
# @rbs document: Riffer::Rig::Settings::Document
|
|
411
|
-
# @rbs return: String?
|
|
412
|
-
def settings_reasoning(document)
|
|
413
|
-
Riffer::Rig::Settings::ModelOptions.reasoning_level(document.reasoning)
|
|
296
|
+
Riffer::Rig::Directories.riffer(@home, 'auth.json')
|
|
414
297
|
end
|
|
415
298
|
|
|
416
|
-
#
|
|
417
|
-
# not recorded, so a later resume restores the session's own switches.
|
|
418
|
-
# @rbs snapshot: Hash[Symbol, untyped]?
|
|
299
|
+
# @rbs snapshot: Riffer::Rig::Runtime::Snapshot?
|
|
419
300
|
# @rbs options: Riffer::Rig::Loader::Options
|
|
420
|
-
# @rbs return:
|
|
301
|
+
# @rbs return: Riffer::Rig::Runtime::Snapshot?
|
|
421
302
|
def snapshot_without_forced(snapshot, options)
|
|
422
|
-
|
|
423
|
-
|
|
303
|
+
# The flags beat the session's recorded switches, which the Runtime would
|
|
304
|
+
# otherwise restore over them.
|
|
305
|
+
snapshot = snapshot&.without_model if options.model
|
|
306
|
+
snapshot = snapshot&.without_reasoning if options.reasoning
|
|
424
307
|
snapshot
|
|
425
308
|
end
|
|
426
309
|
|
|
427
|
-
# The store records unless it was declined with store: nil or globally with
|
|
428
|
-
# "sessions": {"save": false}.
|
|
429
310
|
# @rbs document: Riffer::Rig::Settings::Document
|
|
430
311
|
# @rbs resumed: bool
|
|
431
312
|
# @rbs return: Riffer::Rig::Stores::Recorder?
|