riffer-rig 0.4.0 → 0.6.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 +37 -0
- data/README.md +43 -6
- data/lib/riffer/rig/cli.rb +51 -47
- data/lib/riffer/rig/coding_agent.rb +1 -6
- data/lib/riffer/rig/credentials/resolution.rb +16 -0
- data/lib/riffer/rig/credentials.rb +151 -60
- data/lib/riffer/rig/env.rb +29 -0
- data/lib/riffer/rig/events/command_output.rb +27 -0
- data/lib/riffer/rig/events/event.rb +30 -0
- data/lib/riffer/rig/events/notify.rb +27 -0
- data/lib/riffer/rig/events/session_end.rb +24 -0
- data/lib/riffer/rig/events/session_start.rb +27 -0
- data/lib/riffer/rig/events/skill_activated.rb +24 -0
- data/lib/riffer/rig/events/turn_end.rb +32 -0
- data/lib/riffer/rig/events.rb +4 -0
- data/lib/riffer/rig/extension.rb +27 -0
- data/lib/riffer/rig/hosts/base.rb +36 -0
- data/lib/riffer/rig/hosts/mirror.rb +60 -0
- data/lib/riffer/rig/hosts/null.rb +36 -0
- data/lib/riffer/rig/provider_setup/field.rb +24 -0
- data/lib/riffer/rig/provider_setup.rb +84 -0
- data/lib/riffer/rig/registrar.rb +35 -0
- data/lib/riffer/rig/repl.rb +42 -31
- data/lib/riffer/rig/runtime/cancel_flag.rb +27 -0
- data/lib/riffer/rig/runtime.rb +225 -0
- data/lib/riffer/rig/settings/document.rb +28 -0
- data/lib/riffer/rig/settings/pricing.rb +11 -1
- data/lib/riffer/rig/settings.rb +49 -60
- data/lib/riffer/rig/token_tally.rb +1 -21
- data/lib/riffer/rig/tools/bash.rb +33 -12
- data/lib/riffer/rig/tools/edit.rb +1 -1
- data/lib/riffer/rig/tools/read.rb +2 -2
- data/lib/riffer/rig/ui/animator.rb +8 -15
- data/lib/riffer/rig/ui/cursor.rb +4 -6
- data/lib/riffer/rig/ui/renderer.rb +18 -31
- data/lib/riffer/rig/ui/smoother.rb +15 -23
- data/lib/riffer/rig/ui/theme.rb +4 -6
- data/lib/riffer/rig/version.rb +1 -1
- data/lib/riffer/rig.rb +26 -0
- data/sig/generated/riffer/rig/cli.rbs +27 -19
- data/sig/generated/riffer/rig/credentials/resolution.rbs +13 -0
- data/sig/generated/riffer/rig/credentials.rbs +71 -41
- data/sig/generated/riffer/rig/env.rbs +20 -0
- data/sig/generated/riffer/rig/events/command_output.rbs +19 -0
- data/sig/generated/riffer/rig/events/event.rbs +20 -0
- data/sig/generated/riffer/rig/events/notify.rbs +19 -0
- data/sig/generated/riffer/rig/events/session_end.rbs +16 -0
- data/sig/generated/riffer/rig/events/session_start.rbs +19 -0
- data/sig/generated/riffer/rig/events/skill_activated.rbs +16 -0
- data/sig/generated/riffer/rig/events/turn_end.rbs +22 -0
- data/sig/generated/riffer/rig/events.rbs +4 -0
- data/sig/generated/riffer/rig/extension.rbs +24 -0
- data/sig/generated/riffer/rig/hosts/base.rbs +26 -0
- data/sig/generated/riffer/rig/hosts/mirror.rbs +41 -0
- data/sig/generated/riffer/rig/hosts/null.rbs +26 -0
- data/sig/generated/riffer/rig/provider_setup/field.rbs +22 -0
- data/sig/generated/riffer/rig/provider_setup.rbs +30 -0
- data/sig/generated/riffer/rig/registrar.rbs +25 -0
- data/sig/generated/riffer/rig/repl.rbs +17 -16
- data/sig/generated/riffer/rig/runtime/cancel_flag.rbs +19 -0
- data/sig/generated/riffer/rig/runtime.rbs +120 -0
- data/sig/generated/riffer/rig/settings/document.rbs +24 -0
- data/sig/generated/riffer/rig/settings/pricing.rbs +4 -1
- data/sig/generated/riffer/rig/settings.rbs +28 -49
- data/sig/generated/riffer/rig/token_tally.rbs +2 -21
- data/sig/generated/riffer/rig/tools/bash.rbs +15 -3
- data/sig/generated/riffer/rig/tools/edit.rbs +2 -2
- data/sig/generated/riffer/rig/tools/read.rbs +3 -3
- data/sig/generated/riffer/rig/ui/animator.rbs +8 -16
- data/sig/generated/riffer/rig/ui/cursor.rbs +4 -6
- data/sig/generated/riffer/rig/ui/renderer.rbs +13 -35
- data/sig/generated/riffer/rig/ui/smoother.rbs +9 -22
- data/sig/generated/riffer/rig/ui/theme.rbs +3 -5
- data/sig/generated/riffer/rig.rbs +12 -0
- data/sig/manual/riffer/rig/cli.rbs +2 -4
- data/sig/manual/riffer/rig/credentials.rbs +2 -4
- data/sig/manual/riffer/rig/hosts.rbs +5 -0
- data/sig/manual/riffer/rig/settings.rbs +2 -4
- data/sig/manual/riffer/rig/ui/banner.rbs +2 -4
- data/sig/manual/riffer/rig.rbs +11 -0
- metadata +45 -3
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::SessionEnd < Riffer::Rig::Events::Event
|
|
4
|
+
# @dynamic reason
|
|
5
|
+
attr_reader :reason #: Symbol
|
|
6
|
+
|
|
7
|
+
# @rbs reason: Symbol
|
|
8
|
+
# @rbs return: void
|
|
9
|
+
def initialize(reason)
|
|
10
|
+
super()
|
|
11
|
+
@reason = reason
|
|
12
|
+
freeze
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @rbs return: Symbol
|
|
16
|
+
def type
|
|
17
|
+
:session_end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
21
|
+
def to_h
|
|
22
|
+
{ type: type, reason: reason }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::SessionStart < Riffer::Rig::Events::Event
|
|
4
|
+
# @dynamic id, reason
|
|
5
|
+
attr_reader :id #: String
|
|
6
|
+
attr_reader :reason #: Symbol
|
|
7
|
+
|
|
8
|
+
# @rbs id: String
|
|
9
|
+
# @rbs reason: Symbol
|
|
10
|
+
# @rbs return: void
|
|
11
|
+
def initialize(id, reason)
|
|
12
|
+
super()
|
|
13
|
+
@id = id
|
|
14
|
+
@reason = reason
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @rbs return: Symbol
|
|
19
|
+
def type
|
|
20
|
+
:session_start
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
24
|
+
def to_h
|
|
25
|
+
{ type: type, id: id, reason: reason }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::SkillActivated < Riffer::Rig::Events::Event
|
|
4
|
+
# @dynamic name
|
|
5
|
+
attr_reader :name #: String
|
|
6
|
+
|
|
7
|
+
# @rbs name: String
|
|
8
|
+
# @rbs return: void
|
|
9
|
+
def initialize(name)
|
|
10
|
+
super()
|
|
11
|
+
@name = name
|
|
12
|
+
freeze
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @rbs return: Symbol
|
|
16
|
+
def type
|
|
17
|
+
:skill_activated
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
21
|
+
def to_h
|
|
22
|
+
{ type: type, name: name }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::TurnEnd < Riffer::Rig::Events::Event
|
|
4
|
+
# @dynamic stop_reason, usage
|
|
5
|
+
attr_reader :stop_reason #: Symbol
|
|
6
|
+
attr_reader :usage #: ::Riffer::Providers::TokenUsage?
|
|
7
|
+
|
|
8
|
+
# @rbs stop_reason: Symbol
|
|
9
|
+
# @rbs usage: ::Riffer::Providers::TokenUsage?
|
|
10
|
+
# @rbs return: void
|
|
11
|
+
def initialize(stop_reason, usage)
|
|
12
|
+
super()
|
|
13
|
+
@stop_reason = stop_reason
|
|
14
|
+
@usage = usage
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @rbs return: Float?
|
|
19
|
+
def cost
|
|
20
|
+
usage&.cost
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @rbs return: Symbol
|
|
24
|
+
def type
|
|
25
|
+
:turn_end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
29
|
+
def to_h
|
|
30
|
+
{ type: type, stop_reason: stop_reason, usage: usage }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Extension
|
|
4
|
+
# @rbs @name: String
|
|
5
|
+
# @rbs @requires: Gem::Requirement?
|
|
6
|
+
# @rbs @block: ^(::Riffer::Rig::Registrar) -> void
|
|
7
|
+
|
|
8
|
+
# @dynamic name, requires
|
|
9
|
+
attr_reader :name #: String
|
|
10
|
+
attr_reader :requires #: Gem::Requirement?
|
|
11
|
+
|
|
12
|
+
# @rbs name: String
|
|
13
|
+
# @rbs requires: String?
|
|
14
|
+
# @rbs &block: (::Riffer::Rig::Registrar) -> void
|
|
15
|
+
# @rbs return: void
|
|
16
|
+
def initialize(name, requires: nil, &block)
|
|
17
|
+
@name = name
|
|
18
|
+
@requires = requires && Gem::Requirement.new(requires)
|
|
19
|
+
@block = block
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @rbs registrar: Riffer::Rig::Registrar
|
|
23
|
+
# @rbs return: void
|
|
24
|
+
def run(registrar)
|
|
25
|
+
@block.call(registrar)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Hosts::Base
|
|
4
|
+
# @rbs return: Set[Symbol]
|
|
5
|
+
def capabilities
|
|
6
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# @rbs question: String?
|
|
10
|
+
# @rbs options: Array[String]?
|
|
11
|
+
# @rbs secret: bool
|
|
12
|
+
# @rbs return: String?
|
|
13
|
+
def ask(question = nil, options: nil, secret: false)
|
|
14
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @rbs question: String?
|
|
18
|
+
# @rbs return: bool
|
|
19
|
+
def confirm(question = nil)
|
|
20
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @rbs message: String?
|
|
24
|
+
# @rbs level: Symbol
|
|
25
|
+
# @rbs return: void
|
|
26
|
+
def notify(message = nil, level: :info)
|
|
27
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @rbs label: String?
|
|
31
|
+
# @rbs &: ? () -> void
|
|
32
|
+
# @rbs return: void
|
|
33
|
+
def progress(label = nil, &)
|
|
34
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Hosts::Mirror < Riffer::Rig::Hosts::Base
|
|
4
|
+
# @rbs @host: Riffer::Rig::Hosts::Base
|
|
5
|
+
# @rbs @queue: Array[Riffer::Rig::Events::Event]
|
|
6
|
+
|
|
7
|
+
# @dynamic capabilities
|
|
8
|
+
attr_reader :capabilities #: Set[Symbol]
|
|
9
|
+
|
|
10
|
+
# @rbs host: Riffer::Rig::Hosts::Base
|
|
11
|
+
# @rbs return: void
|
|
12
|
+
def initialize(host)
|
|
13
|
+
super()
|
|
14
|
+
@host = host
|
|
15
|
+
@capabilities = host.capabilities
|
|
16
|
+
@queue = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @rbs question: String?
|
|
20
|
+
# @rbs options: Array[String]?
|
|
21
|
+
# @rbs secret: bool
|
|
22
|
+
# @rbs return: String?
|
|
23
|
+
def ask(question = nil, options: nil, secret: false)
|
|
24
|
+
@host.ask(question, options: options, secret: secret)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @rbs question: String?
|
|
28
|
+
# @rbs return: bool
|
|
29
|
+
def confirm(question = nil)
|
|
30
|
+
@host.confirm(question)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @rbs message: String?
|
|
34
|
+
# @rbs level: Symbol
|
|
35
|
+
# @rbs return: void
|
|
36
|
+
def notify(message = nil, level: :info)
|
|
37
|
+
queue(Riffer::Rig::Events::Notify.new(message, level))
|
|
38
|
+
@host.notify(message, level: level)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @rbs label: String?
|
|
42
|
+
# @rbs &block: ? () -> void
|
|
43
|
+
# @rbs return: void
|
|
44
|
+
def progress(label = nil, &block)
|
|
45
|
+
@host.progress(label) { block&.call }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @rbs event: Riffer::Rig::Events::Event
|
|
49
|
+
# @rbs return: void
|
|
50
|
+
def queue(event)
|
|
51
|
+
@queue << event
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @rbs return: Array[Riffer::Rig::Events::Event]
|
|
55
|
+
def drain
|
|
56
|
+
queued = @queue
|
|
57
|
+
@queue = []
|
|
58
|
+
queued
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Hosts::Null < Riffer::Rig::Hosts::Base
|
|
4
|
+
# @rbs return: Set[Symbol]
|
|
5
|
+
def capabilities
|
|
6
|
+
Set.new.freeze
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# @rbs question: String?
|
|
10
|
+
# @rbs options: Array[String]?
|
|
11
|
+
# @rbs secret: bool
|
|
12
|
+
# @rbs return: String?
|
|
13
|
+
def ask(question = nil, options: nil, secret: false)
|
|
14
|
+
nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @rbs question: String?
|
|
18
|
+
# @rbs return: bool
|
|
19
|
+
def confirm(question = nil)
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @rbs message: String?
|
|
24
|
+
# @rbs level: Symbol
|
|
25
|
+
# @rbs return: void
|
|
26
|
+
def notify(message = nil, level: :info)
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @rbs label: String?
|
|
31
|
+
# @rbs &block: ? () -> void
|
|
32
|
+
# @rbs return: void
|
|
33
|
+
def progress(label = nil, &block)
|
|
34
|
+
block&.call
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::ProviderSetup::Field
|
|
4
|
+
# @dynamic name, env, secret, required, fallback
|
|
5
|
+
attr_reader :name #: Symbol
|
|
6
|
+
attr_reader :env #: Array[String]
|
|
7
|
+
attr_reader :secret, :required #: bool
|
|
8
|
+
attr_reader :fallback #: (^() -> String?)?
|
|
9
|
+
|
|
10
|
+
# @rbs name: Symbol
|
|
11
|
+
# @rbs env: Array[String]
|
|
12
|
+
# @rbs secret: bool
|
|
13
|
+
# @rbs required: bool
|
|
14
|
+
# @rbs fallback: (^() -> String?)?
|
|
15
|
+
# @rbs return: void
|
|
16
|
+
def initialize(name:, env:, secret:, required:, fallback: nil)
|
|
17
|
+
@name = name
|
|
18
|
+
@env = env.freeze
|
|
19
|
+
@secret = secret
|
|
20
|
+
@required = required
|
|
21
|
+
@fallback = fallback
|
|
22
|
+
freeze
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::ProviderSetup
|
|
4
|
+
# @dynamic url, chain, fields
|
|
5
|
+
attr_reader :url #: String?
|
|
6
|
+
attr_reader :chain #: bool
|
|
7
|
+
attr_reader :fields #: Array[Riffer::Rig::ProviderSetup::Field]
|
|
8
|
+
|
|
9
|
+
# @rbs fields: Array[Riffer::Rig::ProviderSetup::Field]
|
|
10
|
+
# @rbs url: String?
|
|
11
|
+
# @rbs chain: bool
|
|
12
|
+
# @rbs return: void
|
|
13
|
+
def initialize(fields:, url: nil, chain: false)
|
|
14
|
+
@url = url
|
|
15
|
+
@chain = chain
|
|
16
|
+
@fields = fields.freeze
|
|
17
|
+
freeze
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
AWS_SHARED_CONFIG_REGION = lambda do
|
|
21
|
+
require 'aws-sdk-core'
|
|
22
|
+
Object.const_get(:Aws).shared_config.region
|
|
23
|
+
rescue LoadError
|
|
24
|
+
nil
|
|
25
|
+
end #: ^() -> String?
|
|
26
|
+
|
|
27
|
+
# Upstream candidate: each riffer provider could declare its own credential
|
|
28
|
+
# fields, and this table would go.
|
|
29
|
+
TABLE = {
|
|
30
|
+
anthropic: new(
|
|
31
|
+
url: 'https://console.anthropic.com/settings/keys',
|
|
32
|
+
fields: [Field.new(name: :api_key, env: ['ANTHROPIC_API_KEY'], secret: true, required: true)]
|
|
33
|
+
),
|
|
34
|
+
openai: new(
|
|
35
|
+
url: 'https://platform.openai.com/api-keys',
|
|
36
|
+
fields: [
|
|
37
|
+
Field.new(name: :api_key, env: ['OPENAI_API_KEY'], secret: true, required: true),
|
|
38
|
+
Field.new(name: :base_url, env: ['OPENAI_BASE_URL'], secret: false, required: false)
|
|
39
|
+
]
|
|
40
|
+
),
|
|
41
|
+
gemini: new(
|
|
42
|
+
url: 'https://aistudio.google.com/app/apikey',
|
|
43
|
+
fields: [Field.new(name: :api_key, env: ['GEMINI_API_KEY'], secret: true, required: true)]
|
|
44
|
+
),
|
|
45
|
+
openrouter: new(
|
|
46
|
+
url: 'https://openrouter.ai/keys',
|
|
47
|
+
fields: [Field.new(name: :api_key, env: ['OPENROUTER_API_KEY'], secret: true, required: true)]
|
|
48
|
+
),
|
|
49
|
+
azure_openai: new(
|
|
50
|
+
url: 'https://portal.azure.com',
|
|
51
|
+
fields: [
|
|
52
|
+
Field.new(name: :endpoint, env: ['AZURE_OPENAI_ENDPOINT'], secret: false, required: true),
|
|
53
|
+
Field.new(name: :api_key, env: ['AZURE_OPENAI_API_KEY'], secret: true, required: true)
|
|
54
|
+
]
|
|
55
|
+
),
|
|
56
|
+
amazon_bedrock: new(
|
|
57
|
+
url: 'https://console.aws.amazon.com/bedrock',
|
|
58
|
+
chain: true,
|
|
59
|
+
fields: [
|
|
60
|
+
Field.new(
|
|
61
|
+
name: :region,
|
|
62
|
+
env: %w[AWS_REGION AWS_DEFAULT_REGION],
|
|
63
|
+
secret: false,
|
|
64
|
+
required: true,
|
|
65
|
+
fallback: AWS_SHARED_CONFIG_REGION
|
|
66
|
+
),
|
|
67
|
+
Field.new(name: :api_token, env: ['AWS_BEARER_TOKEN_BEDROCK'], secret: true, required: false)
|
|
68
|
+
]
|
|
69
|
+
)
|
|
70
|
+
}.freeze #: Hash[Symbol, Riffer::Rig::ProviderSetup]
|
|
71
|
+
|
|
72
|
+
# @rbs identifier: String | Symbol
|
|
73
|
+
# @rbs return: Riffer::Rig::ProviderSetup?
|
|
74
|
+
def self.[](identifier)
|
|
75
|
+
TABLE[identifier.to_sym]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @rbs identifier: String | Symbol
|
|
79
|
+
# @rbs return: Riffer::Rig::ProviderSetup
|
|
80
|
+
def self.for(identifier)
|
|
81
|
+
self[identifier] ||
|
|
82
|
+
new(fields: [Field.new(name: :api_key, env: ["#{identifier.to_s.upcase}_API_KEY"], secret: true, required: true)])
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Registrar
|
|
4
|
+
# @rbs @tools: Array[singleton(Riffer::Tool)]
|
|
5
|
+
# @rbs @prompts: Hash[Symbol, ^(Riffer::Rig::Runtime) -> String?]
|
|
6
|
+
|
|
7
|
+
# @rbs return: void
|
|
8
|
+
def initialize
|
|
9
|
+
@tools = []
|
|
10
|
+
@prompts = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @rbs klass: singleton(Riffer::Tool)
|
|
14
|
+
# @rbs return: void
|
|
15
|
+
def tool(klass)
|
|
16
|
+
@tools << klass
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @rbs name: Symbol
|
|
20
|
+
# @rbs &block: (Riffer::Rig::Runtime) -> String?
|
|
21
|
+
# @rbs return: void
|
|
22
|
+
def prompt(name, &block)
|
|
23
|
+
@prompts[name] = block
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @rbs return: Array[singleton(Riffer::Tool)]
|
|
27
|
+
def tools
|
|
28
|
+
@tools.dup
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @rbs return: Hash[Symbol, ^(Riffer::Rig::Runtime) -> String?]
|
|
32
|
+
def prompts
|
|
33
|
+
@prompts.dup
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/riffer/rig/repl.rb
CHANGED
|
@@ -11,28 +11,28 @@ class Riffer::Rig::REPL
|
|
|
11
11
|
# @rbs @smoother: Riffer::Rig::UI::Smoother
|
|
12
12
|
# @rbs @cursor: Riffer::Rig::UI::Cursor
|
|
13
13
|
# @rbs @theme: Riffer::Rig::UI::Theme
|
|
14
|
-
# @rbs @input:
|
|
15
|
-
# @rbs @output:
|
|
16
|
-
|
|
17
|
-
# @rbs
|
|
18
|
-
# @rbs
|
|
19
|
-
# @rbs input:
|
|
20
|
-
# @rbs output:
|
|
21
|
-
# @rbs
|
|
22
|
-
# @rbs
|
|
23
|
-
# @rbs
|
|
24
|
-
# @rbs
|
|
14
|
+
# @rbs @input: IO
|
|
15
|
+
# @rbs @output: IO
|
|
16
|
+
|
|
17
|
+
# @rbs agent: Riffer::Agent
|
|
18
|
+
# @rbs renderer: Riffer::Rig::UI::Renderer
|
|
19
|
+
# @rbs input: IO
|
|
20
|
+
# @rbs output: IO
|
|
21
|
+
# @rbs theme: Riffer::Rig::UI::Theme
|
|
22
|
+
# @rbs animator: Riffer::Rig::UI::Animator
|
|
23
|
+
# @rbs smoother: Riffer::Rig::UI::Smoother
|
|
24
|
+
# @rbs cursor: Riffer::Rig::UI::Cursor
|
|
25
25
|
# @rbs return: void
|
|
26
|
-
def initialize(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
def initialize(
|
|
27
|
+
agent:,
|
|
28
|
+
renderer:,
|
|
29
|
+
input: $stdin,
|
|
30
|
+
output: $stdout,
|
|
31
|
+
theme: Riffer::Rig::UI::Theme.for(output),
|
|
32
|
+
animator: Riffer::Rig::UI::Animator.new(io: output, theme:),
|
|
33
|
+
smoother: Riffer::Rig::UI::Smoother.new(io: output, theme:),
|
|
34
|
+
cursor: Riffer::Rig::UI::Cursor.new(io: output, theme:)
|
|
35
|
+
)
|
|
36
36
|
@agent = agent
|
|
37
37
|
@renderer = renderer
|
|
38
38
|
@animator = animator
|
|
@@ -73,15 +73,17 @@ class Riffer::Rig::REPL
|
|
|
73
73
|
# @rbs prompt: String
|
|
74
74
|
# @rbs return: void
|
|
75
75
|
def run_turn(prompt)
|
|
76
|
+
# A hidden cursor can't flicker against the animator's erase-and-redraw
|
|
77
|
+
# churn.
|
|
76
78
|
@cursor.hide
|
|
77
|
-
|
|
79
|
+
start_animator
|
|
78
80
|
@smoother.start
|
|
79
81
|
@agent.stream(prompt).each do |event|
|
|
80
82
|
case event
|
|
81
83
|
when Riffer::StreamEvents::ReasoningDelta
|
|
82
|
-
|
|
84
|
+
start_animator(:reasoning)
|
|
83
85
|
when Riffer::StreamEvents::ReasoningDone
|
|
84
|
-
|
|
86
|
+
start_animator
|
|
85
87
|
when Riffer::StreamEvents::ToolCallDelta, Riffer::StreamEvents::FinishReasonDone
|
|
86
88
|
next
|
|
87
89
|
when Riffer::StreamEvents::SkillActivation
|
|
@@ -89,13 +91,13 @@ class Riffer::Rig::REPL
|
|
|
89
91
|
# spinner comes straight back on to cover the silent stretch.
|
|
90
92
|
@animator.stop
|
|
91
93
|
@renderer.render(event)
|
|
92
|
-
|
|
94
|
+
start_animator
|
|
93
95
|
next
|
|
94
96
|
when Riffer::StreamEvents::TokenUsageDone
|
|
95
97
|
# Usage renders nothing inline — it flushes below the turn's output.
|
|
96
98
|
# Restarting here covers the silent tool-execution stretch that follows.
|
|
97
99
|
@renderer.render(event)
|
|
98
|
-
|
|
100
|
+
start_animator
|
|
99
101
|
next
|
|
100
102
|
else
|
|
101
103
|
@animator.stop
|
|
@@ -154,12 +156,11 @@ class Riffer::Rig::REPL
|
|
|
154
156
|
"<skill name=\"#{name}\">\n#{body}\n</skill>"
|
|
155
157
|
end
|
|
156
158
|
|
|
157
|
-
#
|
|
158
|
-
# their line with a newline but leave the renderer's tool-group state alone —
|
|
159
|
-
# they can only follow prose, never mid-group.
|
|
160
|
-
#
|
|
159
|
+
# @rbs &block: () -> String
|
|
161
160
|
# @rbs return: void
|
|
162
161
|
def print_block(&)
|
|
162
|
+
# Bypasses the renderer's tool-group state: these lines (errors, the exit
|
|
163
|
+
# line) can only follow prose, never land mid-group.
|
|
163
164
|
@output.puts
|
|
164
165
|
@output.puts(yield)
|
|
165
166
|
@output.flush
|
|
@@ -172,6 +173,16 @@ class Riffer::Rig::REPL
|
|
|
172
173
|
|
|
173
174
|
@animator.stop
|
|
174
175
|
@renderer.render_tool_result(message)
|
|
175
|
-
|
|
176
|
+
start_animator
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# @rbs mode: Symbol
|
|
180
|
+
# @rbs return: void
|
|
181
|
+
def start_animator(mode = :neutral)
|
|
182
|
+
# The smoother's backlog may still be trickling out from streamed prose,
|
|
183
|
+
# and spinner frames drawn mid-drain would carve `\r…\e[K` through a
|
|
184
|
+
# half-printed sentence.
|
|
185
|
+
@smoother.drain
|
|
186
|
+
@animator.start(mode)
|
|
176
187
|
end
|
|
177
188
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Runtime::CancelFlag
|
|
4
|
+
# @rbs @mutex: Thread::Mutex
|
|
5
|
+
# @rbs @set: bool
|
|
6
|
+
|
|
7
|
+
# @rbs return: void
|
|
8
|
+
def initialize
|
|
9
|
+
@mutex = Mutex.new
|
|
10
|
+
@set = false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @rbs return: void
|
|
14
|
+
def set
|
|
15
|
+
@mutex.synchronize { @set = true }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @rbs return: void
|
|
19
|
+
def clear
|
|
20
|
+
@mutex.synchronize { @set = false }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @rbs return: bool
|
|
24
|
+
def set?
|
|
25
|
+
@mutex.synchronize { @set }
|
|
26
|
+
end
|
|
27
|
+
end
|