riffer-rig 0.2.1 → 0.4.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 +24 -0
- data/README.md +2 -2
- data/lib/riffer/rig/cli.rb +31 -2
- data/lib/riffer/rig/coding_agent.rb +35 -31
- data/lib/riffer/rig/credentials.rb +24 -4
- data/lib/riffer/rig/repl.rb +75 -22
- data/lib/riffer/rig/settings/pricing.rb +19 -0
- data/lib/riffer/rig/settings.rb +33 -8
- data/lib/riffer/rig/token_tally.rb +22 -6
- data/lib/riffer/rig/tools/bash.rb +23 -5
- data/lib/riffer/rig/tools/edit.rb +16 -2
- data/lib/riffer/rig/tools/read.rb +6 -1
- data/lib/riffer/rig/tools/write.rb +4 -0
- data/lib/riffer/rig/ui/animator.rb +51 -11
- data/lib/riffer/rig/ui/banner.rb +28 -4
- data/lib/riffer/rig/ui/cursor.rb +14 -2
- data/lib/riffer/rig/ui/palette.rb +13 -7
- data/lib/riffer/rig/ui/renderer.rb +137 -29
- data/lib/riffer/rig/ui/smoother.rb +56 -14
- data/lib/riffer/rig/ui/theme.rb +38 -7
- data/lib/riffer/rig/version.rb +1 -1
- data/sig/generated/riffer/rig/cli.rbs +41 -0
- data/sig/generated/riffer/rig/coding_agent.rbs +9 -0
- data/sig/generated/riffer/rig/credentials.rbs +55 -0
- data/sig/generated/riffer/rig/repl.rbs +68 -0
- data/sig/generated/riffer/rig/settings/pricing.rbs +23 -0
- data/sig/generated/riffer/rig/settings.rbs +92 -0
- data/sig/generated/riffer/rig/token_tally.rbs +61 -0
- data/sig/generated/riffer/rig/tools/bash.rbs +28 -0
- data/sig/generated/riffer/rig/tools/edit.rbs +11 -0
- data/sig/generated/riffer/rig/tools/read.rbs +12 -0
- data/sig/generated/riffer/rig/tools/write.rbs +9 -0
- data/sig/generated/riffer/rig/ui/animator.rbs +76 -0
- data/sig/generated/riffer/rig/ui/banner.rbs +42 -0
- data/sig/generated/riffer/rig/ui/cursor.rbs +33 -0
- data/sig/generated/riffer/rig/ui/palette.rbs +17 -0
- data/sig/generated/riffer/rig/ui/renderer.rbs +116 -0
- data/sig/generated/riffer/rig/ui/smoother.rbs +72 -0
- data/sig/generated/riffer/rig/ui/theme.rbs +60 -0
- data/sig/generated/riffer/rig/version.rbs +7 -0
- data/sig/generated/riffer/rig.rbs +2 -0
- data/sig/manual/riffer/rig/cli.rbs +8 -0
- data/sig/manual/riffer/rig/credentials.rbs +8 -0
- data/sig/manual/riffer/rig/settings.rbs +8 -0
- data/sig/manual/riffer/rig/tools.rbs +5 -0
- data/sig/manual/riffer/rig/ui/banner.rbs +8 -0
- data/sig/manual/riffer/rig/ui.rbs +5 -0
- metadata +31 -4
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# typing. On a non-TTY output it is transparent: writes pass through
|
|
5
5
|
# synchronously and no thread ever runs.
|
|
6
6
|
class Riffer::Rig::UI::Smoother
|
|
7
|
-
TICK_SECONDS = 1.0 / 60.0
|
|
7
|
+
TICK_SECONDS = 1.0 / 60.0 #: Float
|
|
8
8
|
|
|
9
9
|
# A frame removes only a sixtieth of the remaining backlog — an exponential
|
|
10
10
|
# decay whose tail keeps text flowing for a second or more, so the reveal
|
|
@@ -12,9 +12,25 @@ class Riffer::Rig::UI::Smoother
|
|
|
12
12
|
# releases so the effective rate can settle at the provider's throughput;
|
|
13
13
|
# the floor is fractional (10 chars/s) so it can't over-drain a backlog
|
|
14
14
|
# that's trickling in slower than 60 chars/s.
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
#
|
|
16
|
+
BACKLOG_FRACTION_PER_TICK = Rational(1, 60) #: Rational
|
|
17
17
|
|
|
18
|
+
MIN_CHARS_PER_TICK = Rational(1, 6) #: Rational
|
|
19
|
+
|
|
20
|
+
# @rbs @io: untyped
|
|
21
|
+
# @rbs @theme: Riffer::Rig::UI::Theme
|
|
22
|
+
# @rbs @clock: singleton(Kernel)
|
|
23
|
+
# @rbs @backlog: String
|
|
24
|
+
# @rbs @carry: Rational
|
|
25
|
+
# @rbs @mutex: Mutex
|
|
26
|
+
# @rbs @thread: Thread?
|
|
27
|
+
# @rbs @stop: bool
|
|
28
|
+
# @rbs @newline_pending: bool
|
|
29
|
+
|
|
30
|
+
# @rbs io: untyped
|
|
31
|
+
# @rbs ?theme: Riffer::Rig::UI::Theme
|
|
32
|
+
# @rbs clock: singleton(Kernel)
|
|
33
|
+
# @rbs return: void
|
|
18
34
|
def initialize(io: $stdout, theme: Riffer::Rig::UI::Theme.for(io), clock: Kernel)
|
|
19
35
|
@io = io
|
|
20
36
|
@theme = theme
|
|
@@ -24,8 +40,10 @@ class Riffer::Rig::UI::Smoother
|
|
|
24
40
|
@mutex = Mutex.new
|
|
25
41
|
@thread = nil
|
|
26
42
|
@stop = false
|
|
43
|
+
@newline_pending = false
|
|
27
44
|
end
|
|
28
45
|
|
|
46
|
+
# @rbs return: void
|
|
29
47
|
def start
|
|
30
48
|
return unless enabled?
|
|
31
49
|
return if @thread
|
|
@@ -39,17 +57,24 @@ class Riffer::Rig::UI::Smoother
|
|
|
39
57
|
end
|
|
40
58
|
end
|
|
41
59
|
|
|
60
|
+
# @rbs content: String
|
|
61
|
+
# @rbs return: self
|
|
42
62
|
def <<(content)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
63
|
+
@mutex.synchronize do
|
|
64
|
+
if enabled?
|
|
65
|
+
@backlog << content
|
|
66
|
+
else
|
|
67
|
+
@io.print(content)
|
|
68
|
+
@io.flush
|
|
69
|
+
end
|
|
70
|
+
@newline_pending = !content.end_with?("\n")
|
|
48
71
|
end
|
|
49
72
|
self
|
|
50
73
|
end
|
|
51
74
|
|
|
52
75
|
# The write sits inside the mutex so a concurrent tick can't reorder a drain.
|
|
76
|
+
#
|
|
77
|
+
# @rbs return: void
|
|
53
78
|
def tick
|
|
54
79
|
@mutex.synchronize do
|
|
55
80
|
return if @backlog.empty?
|
|
@@ -59,26 +84,42 @@ class Riffer::Rig::UI::Smoother
|
|
|
59
84
|
return if count.zero?
|
|
60
85
|
|
|
61
86
|
@carry -= count
|
|
62
|
-
@
|
|
87
|
+
chunk = @backlog.slice!(0, count) || ''
|
|
88
|
+
@newline_pending = !chunk.end_with?("\n")
|
|
89
|
+
@io.print(chunk)
|
|
63
90
|
@io.flush
|
|
64
91
|
end
|
|
65
92
|
end
|
|
66
93
|
|
|
67
|
-
# Called before non-
|
|
94
|
+
# Called before non-prose output so printed order matches stream order. The
|
|
95
|
+
# backlog is flushed, then any partial prose block the model left without a
|
|
96
|
+
# trailing newline gets one, so block spacing never depends on the model's
|
|
97
|
+
# last character.
|
|
98
|
+
#
|
|
99
|
+
# @rbs return: void
|
|
68
100
|
def drain
|
|
69
101
|
@mutex.synchronize do
|
|
70
|
-
|
|
102
|
+
unless @backlog.empty?
|
|
103
|
+
backlog = @backlog.slice!(0, @backlog.length) || ''
|
|
104
|
+
@newline_pending = !backlog.end_with?("\n")
|
|
105
|
+
@io.print(backlog)
|
|
106
|
+
end
|
|
107
|
+
return unless @newline_pending
|
|
71
108
|
|
|
72
|
-
@
|
|
109
|
+
@newline_pending = false
|
|
110
|
+
@io.print("\n")
|
|
73
111
|
@io.flush
|
|
74
112
|
end
|
|
75
113
|
end
|
|
76
114
|
|
|
77
115
|
# Idempotent: the REPL's ensure path runs it even after a happy-path finish.
|
|
116
|
+
#
|
|
117
|
+
# @rbs return: void
|
|
78
118
|
def finish
|
|
79
|
-
|
|
119
|
+
thread = @thread
|
|
120
|
+
if thread
|
|
80
121
|
@stop = true
|
|
81
|
-
|
|
122
|
+
thread.join
|
|
82
123
|
@thread = nil
|
|
83
124
|
end
|
|
84
125
|
drain
|
|
@@ -86,6 +127,7 @@ class Riffer::Rig::UI::Smoother
|
|
|
86
127
|
|
|
87
128
|
private
|
|
88
129
|
|
|
130
|
+
# @rbs return: bool
|
|
89
131
|
def enabled?
|
|
90
132
|
@theme.enabled && @io.respond_to?(:tty?) && @io.tty?
|
|
91
133
|
end
|
data/lib/riffer/rig/ui/theme.rb
CHANGED
|
@@ -2,43 +2,74 @@
|
|
|
2
2
|
|
|
3
3
|
# +enabled+ gates every escape so the same code path emits plain, escape-free
|
|
4
4
|
# text when piped or tested.
|
|
5
|
-
Riffer::Rig::UI::Theme = Data.define(:enabled)
|
|
6
|
-
|
|
7
|
-
# Behaviour lives in a reopened class (rather than the `Data.define` block) so
|
|
8
|
-
# the type checker can analyse the methods.
|
|
9
5
|
class Riffer::Rig::UI::Theme
|
|
6
|
+
# @rbs @enabled: bool
|
|
7
|
+
|
|
8
|
+
# @rbs return: bool
|
|
9
|
+
# @dynamic enabled
|
|
10
|
+
attr_reader :enabled #: bool
|
|
11
|
+
|
|
12
|
+
# @rbs enabled: bool
|
|
13
|
+
# @rbs return: void
|
|
14
|
+
def initialize(enabled:)
|
|
15
|
+
@enabled = enabled
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @rbs io: untyped
|
|
19
|
+
# @rbs return: Riffer::Rig::UI::Theme
|
|
10
20
|
def self.for(io)
|
|
11
21
|
new(enabled: io.respond_to?(:tty?) && io.tty? && !ENV.key?('NO_COLOR'))
|
|
12
22
|
end
|
|
13
23
|
|
|
24
|
+
# @rbs text: String
|
|
25
|
+
# @rbs rgb: Array[Integer]
|
|
26
|
+
# @rbs return: String
|
|
14
27
|
def paint(text, rgb)
|
|
15
|
-
return text
|
|
28
|
+
return text unless enabled
|
|
16
29
|
|
|
17
30
|
"\e[38;2;#{rgb[0]};#{rgb[1]};#{rgb[2]}m#{text}\e[0m"
|
|
18
31
|
end
|
|
19
32
|
|
|
33
|
+
# @rbs text: String
|
|
34
|
+
# @rbs return: String
|
|
20
35
|
def pink(text) = paint(text, Riffer::Rig::UI::Palette::PINK)
|
|
21
36
|
|
|
37
|
+
# @rbs text: String
|
|
38
|
+
# @rbs return: String
|
|
22
39
|
def magenta(text) = paint(text, Riffer::Rig::UI::Palette::MAGENTA)
|
|
23
40
|
|
|
41
|
+
# @rbs text: String
|
|
42
|
+
# @rbs return: String
|
|
24
43
|
def purple(text) = paint(text, Riffer::Rig::UI::Palette::PURPLE)
|
|
25
44
|
|
|
45
|
+
# @rbs text: String
|
|
46
|
+
# @rbs return: String
|
|
26
47
|
def blue(text) = paint(text, Riffer::Rig::UI::Palette::BLUE)
|
|
27
48
|
|
|
49
|
+
# @rbs text: String
|
|
50
|
+
# @rbs return: String
|
|
28
51
|
def cyan(text) = paint(text, Riffer::Rig::UI::Palette::CYAN)
|
|
29
52
|
|
|
53
|
+
# @rbs text: String
|
|
54
|
+
# @rbs return: String
|
|
30
55
|
def grey(text) = paint(text, Riffer::Rig::UI::Palette::GREY)
|
|
31
56
|
|
|
57
|
+
# @rbs text: String
|
|
58
|
+
# @rbs return: String
|
|
32
59
|
def red(text) = paint(text, Riffer::Rig::UI::Palette::RED)
|
|
33
60
|
|
|
61
|
+
# @rbs text: String
|
|
62
|
+
# @rbs return: String
|
|
34
63
|
def dim(text)
|
|
35
|
-
return text
|
|
64
|
+
return text unless enabled
|
|
36
65
|
|
|
37
66
|
"\e[2m#{text}\e[0m"
|
|
38
67
|
end
|
|
39
68
|
|
|
69
|
+
# @rbs text: String
|
|
70
|
+
# @rbs return: String
|
|
40
71
|
def bold(text)
|
|
41
|
-
return text
|
|
72
|
+
return text unless enabled
|
|
42
73
|
|
|
43
74
|
"\e[1m#{text}\e[0m"
|
|
44
75
|
end
|
data/lib/riffer/rig/version.rb
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/cli.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module Riffer::Rig::CLI
|
|
4
|
+
PROVIDER_URLS: Hash[String, String]
|
|
5
|
+
|
|
6
|
+
# @rbs output: untyped
|
|
7
|
+
# @rbs input: untyped
|
|
8
|
+
# @rbs return: Integer
|
|
9
|
+
def start: (?output: untyped, ?input: untyped) -> Integer
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
# @rbs provider: String?
|
|
14
|
+
# @rbs api_key: String
|
|
15
|
+
# @rbs return: void
|
|
16
|
+
def configure_provider: (String? provider, String api_key) -> void
|
|
17
|
+
|
|
18
|
+
# @rbs provider: String?
|
|
19
|
+
# @rbs theme: Riffer::Rig::UI::Theme
|
|
20
|
+
# @rbs output: untyped
|
|
21
|
+
# @rbs input: untyped
|
|
22
|
+
# @rbs return: String?
|
|
23
|
+
def onboard: (String? provider, Riffer::Rig::UI::Theme theme, output: untyped, input: untyped) -> String?
|
|
24
|
+
|
|
25
|
+
# @rbs provider: String?
|
|
26
|
+
# @rbs return: String
|
|
27
|
+
def env_var_for: (String? provider) -> String
|
|
28
|
+
|
|
29
|
+
# @rbs input: untyped
|
|
30
|
+
# @rbs return: String?
|
|
31
|
+
def read_secret: (untyped input) -> String?
|
|
32
|
+
|
|
33
|
+
# @rbs theme: Riffer::Rig::UI::Theme
|
|
34
|
+
# @rbs animator: Riffer::Rig::UI::Animator
|
|
35
|
+
# @rbs model: String
|
|
36
|
+
# @rbs return: void
|
|
37
|
+
def reveal_banner: (Riffer::Rig::UI::Theme theme, Riffer::Rig::UI::Animator animator, String model) -> void
|
|
38
|
+
|
|
39
|
+
# @rbs return: String
|
|
40
|
+
def count_skills: () -> String
|
|
41
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/credentials.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Reads and writes API keys for each supported provider in
|
|
4
|
+
# <tt>~/.riffer/auth.json</tt>.
|
|
5
|
+
#
|
|
6
|
+
# Keys are stored under the provider name, e.g.:
|
|
7
|
+
#
|
|
8
|
+
# {
|
|
9
|
+
# "anthropic": "sk-ant-...",
|
|
10
|
+
# "openai": "sk-...",
|
|
11
|
+
# "gemini": "...",
|
|
12
|
+
# "openrouter": "sk-or-..."
|
|
13
|
+
# }
|
|
14
|
+
#
|
|
15
|
+
# The environment variable checked per provider:
|
|
16
|
+
#
|
|
17
|
+
# anthropic → ANTHROPIC_API_KEY
|
|
18
|
+
# openai → OPENAI_API_KEY
|
|
19
|
+
# gemini → GEMINI_API_KEY
|
|
20
|
+
# openrouter → OPENROUTER_API_KEY
|
|
21
|
+
module Riffer::Rig::Credentials
|
|
22
|
+
PATH: String
|
|
23
|
+
|
|
24
|
+
PROVIDER_ENV_VARS: Hash[String, String]
|
|
25
|
+
|
|
26
|
+
# Returns the API key for +provider+, checking the environment variable first,
|
|
27
|
+
# then the stored file. Returns +nil+ if no key is available.
|
|
28
|
+
#
|
|
29
|
+
# @rbs provider: String?
|
|
30
|
+
# @rbs path: String
|
|
31
|
+
# @rbs return: String?
|
|
32
|
+
def api_key_for: (String? provider, ?path: String) -> String?
|
|
33
|
+
|
|
34
|
+
# Saves an API key for +provider+ to the auth file with 0600 permissions.
|
|
35
|
+
#
|
|
36
|
+
# @rbs provider: String
|
|
37
|
+
# @rbs key: String
|
|
38
|
+
# @rbs path: String
|
|
39
|
+
# @rbs return: String
|
|
40
|
+
def save_api_key: (String provider, String key, ?path: String) -> String
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# @rbs provider: String?
|
|
45
|
+
# @rbs path: String
|
|
46
|
+
# @rbs return: String?
|
|
47
|
+
def key_from_file: (String? provider, String path) -> String?
|
|
48
|
+
|
|
49
|
+
# The parsed auth file, or an empty hash when the file is absent or
|
|
50
|
+
# malformed.
|
|
51
|
+
#
|
|
52
|
+
# @rbs path: String
|
|
53
|
+
# @rbs return: Hash[String, untyped]
|
|
54
|
+
def read: (String path) -> Hash[String, untyped]
|
|
55
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/repl.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::REPL
|
|
4
|
+
EXIT_COMMANDS: Array[String]
|
|
5
|
+
|
|
6
|
+
SKILL_COMMAND: Regexp
|
|
7
|
+
|
|
8
|
+
@output: untyped
|
|
9
|
+
|
|
10
|
+
@input: untyped
|
|
11
|
+
|
|
12
|
+
@theme: Riffer::Rig::UI::Theme
|
|
13
|
+
|
|
14
|
+
@cursor: Riffer::Rig::UI::Cursor
|
|
15
|
+
|
|
16
|
+
@smoother: Riffer::Rig::UI::Smoother
|
|
17
|
+
|
|
18
|
+
@animator: Riffer::Rig::UI::Animator
|
|
19
|
+
|
|
20
|
+
@renderer: Riffer::Rig::UI::Renderer
|
|
21
|
+
|
|
22
|
+
@agent: Riffer::Agent
|
|
23
|
+
|
|
24
|
+
# @rbs ?agent: Riffer::Agent
|
|
25
|
+
# @rbs ?renderer: Riffer::Rig::UI::Renderer
|
|
26
|
+
# @rbs input: untyped
|
|
27
|
+
# @rbs output: untyped
|
|
28
|
+
# @rbs ?theme: Riffer::Rig::UI::Theme
|
|
29
|
+
# @rbs ?animator: Riffer::Rig::UI::Animator
|
|
30
|
+
# @rbs ?smoother: Riffer::Rig::UI::Smoother
|
|
31
|
+
# @rbs ?cursor: Riffer::Rig::UI::Cursor
|
|
32
|
+
# @rbs return: void
|
|
33
|
+
def initialize: (agent: untyped, renderer: untyped, ?input: untyped, ?output: untyped, ?theme: untyped, ?animator: untyped, ?smoother: untyped, ?cursor: untyped) -> void
|
|
34
|
+
|
|
35
|
+
# @rbs return: Symbol
|
|
36
|
+
def run: () -> Symbol
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# @rbs prompt: String
|
|
41
|
+
# @rbs return: void
|
|
42
|
+
def run_turn: (String prompt) -> void
|
|
43
|
+
|
|
44
|
+
# @rbs name: String
|
|
45
|
+
# @rbs args: String
|
|
46
|
+
# @rbs return: void
|
|
47
|
+
def run_skill_command: (String name, String args) -> void
|
|
48
|
+
|
|
49
|
+
# @rbs name: String
|
|
50
|
+
# @rbs return: String?
|
|
51
|
+
def activate_skill: (String name) -> String?
|
|
52
|
+
|
|
53
|
+
# @rbs name: String
|
|
54
|
+
# @rbs body: String
|
|
55
|
+
# @rbs return: String
|
|
56
|
+
def skill_block: (String name, String body) -> String
|
|
57
|
+
|
|
58
|
+
# Chrome lines that never pass through the renderer (errors, exit line) end
|
|
59
|
+
# their line with a newline but leave the renderer's tool-group state alone —
|
|
60
|
+
# they can only follow prose, never mid-group.
|
|
61
|
+
#
|
|
62
|
+
# @rbs return: void
|
|
63
|
+
def print_block: () ?{ (?) -> untyped } -> void
|
|
64
|
+
|
|
65
|
+
# @rbs message: Riffer::Messages::Base
|
|
66
|
+
# @rbs return: void
|
|
67
|
+
def render_tool_result: (Riffer::Messages::Base message) -> void
|
|
68
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/settings/pricing.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# USD per million tokens, parsed from a model's entry in the settings file.
|
|
4
|
+
class Riffer::Rig::Settings::Pricing
|
|
5
|
+
# @dynamic input, output, cache_write, cache_read
|
|
6
|
+
attr_reader input: Float
|
|
7
|
+
|
|
8
|
+
# @dynamic input, output, cache_write, cache_read
|
|
9
|
+
attr_reader output: Float
|
|
10
|
+
|
|
11
|
+
# @dynamic input, output, cache_write, cache_read
|
|
12
|
+
attr_reader cache_write: Float
|
|
13
|
+
|
|
14
|
+
# @dynamic input, output, cache_write, cache_read
|
|
15
|
+
attr_reader cache_read: Float
|
|
16
|
+
|
|
17
|
+
# @rbs input: Float
|
|
18
|
+
# @rbs output: Float
|
|
19
|
+
# @rbs cache_write: Float
|
|
20
|
+
# @rbs cache_read: Float
|
|
21
|
+
# @rbs return: void
|
|
22
|
+
def initialize: (input: Float, output: Float, cache_write: Float, cache_read: Float) -> void
|
|
23
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/settings.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Reads and writes the riffer-rig user settings file at
|
|
4
|
+
# <tt>~/.riffer/settings.json</tt>.
|
|
5
|
+
#
|
|
6
|
+
# Example file:
|
|
7
|
+
#
|
|
8
|
+
# {
|
|
9
|
+
# "model": "anthropic/claude-sonnet-4-6",
|
|
10
|
+
# "reasoning": "low",
|
|
11
|
+
# "models": {
|
|
12
|
+
# "anthropic/claude-sonnet-4-6": {
|
|
13
|
+
# "input": 3.0,
|
|
14
|
+
# "output": 15.0,
|
|
15
|
+
# "cache_write": 3.75,
|
|
16
|
+
# "cache_read": 0.3
|
|
17
|
+
# }
|
|
18
|
+
# }
|
|
19
|
+
# }
|
|
20
|
+
#
|
|
21
|
+
# All keys are optional. Missing pricing means cost display is suppressed.
|
|
22
|
+
# <tt>"reasoning"</tt> is translated to the appropriate provider-specific
|
|
23
|
+
# parameter on each API call; accepted values depend on the provider:
|
|
24
|
+
#
|
|
25
|
+
# - Anthropic: <tt>"low"</tt>, <tt>"medium"</tt>, <tt>"high"</tt>,
|
|
26
|
+
# <tt>"xhigh"</tt>, <tt>"max"</tt>
|
|
27
|
+
# - OpenAI / OpenRouter: <tt>"low"</tt>, <tt>"medium"</tt>, <tt>"high"</tt>,
|
|
28
|
+
# <tt>"xhigh"</tt>
|
|
29
|
+
#
|
|
30
|
+
# Omitting the key (or supplying an unrecognised value) leaves the model's
|
|
31
|
+
# default reasoning behaviour unchanged.
|
|
32
|
+
module Riffer::Rig::Settings
|
|
33
|
+
PATH: String
|
|
34
|
+
|
|
35
|
+
DEFAULT_MODEL: ::String
|
|
36
|
+
|
|
37
|
+
REASONING_LEVELS_BY_PROVIDER: Hash[String, Array[String]]
|
|
38
|
+
|
|
39
|
+
# Returns the configured model string, or +DEFAULT_MODEL+ if not set.
|
|
40
|
+
#
|
|
41
|
+
# @rbs path: String
|
|
42
|
+
# @rbs return: String
|
|
43
|
+
def model: (?path: String) -> String
|
|
44
|
+
|
|
45
|
+
# Returns model options for the configured model and reasoning level, ready
|
|
46
|
+
# to pass directly to the Riffer agent's +model_options+.
|
|
47
|
+
#
|
|
48
|
+
# @rbs path: String
|
|
49
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
50
|
+
def model_options: (?path: String) -> Hash[Symbol, untyped]
|
|
51
|
+
|
|
52
|
+
# Returns the provider prefix for +model_string+, e.g. <tt>"anthropic"</tt>
|
|
53
|
+
# for <tt>"anthropic/claude-sonnet-4-6"</tt>. Returns +nil+ if the model
|
|
54
|
+
# string contains no slash.
|
|
55
|
+
#
|
|
56
|
+
# @rbs model_string: String
|
|
57
|
+
# @rbs return: String?
|
|
58
|
+
def provider_for: (String model_string) -> String?
|
|
59
|
+
|
|
60
|
+
# Returns the pricing for +model+, or +nil+ if not configured.
|
|
61
|
+
#
|
|
62
|
+
# @rbs model: String
|
|
63
|
+
# @rbs path: String
|
|
64
|
+
# @rbs return: Pricing?
|
|
65
|
+
def pricing_for: (String model, ?path: String) -> Pricing?
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# @rbs provider: String?
|
|
70
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
71
|
+
def base_options: (String? provider) -> Hash[Symbol, untyped]
|
|
72
|
+
|
|
73
|
+
# @rbs path: String
|
|
74
|
+
# @rbs ?provider: String?
|
|
75
|
+
# @rbs return: String?
|
|
76
|
+
def reasoning_for: (?path: String, ?provider: untyped) -> String?
|
|
77
|
+
|
|
78
|
+
# Maps a reasoning level to the provider-specific model option hash expected
|
|
79
|
+
# by Riffer. Returns an empty hash when +level+ is +nil+.
|
|
80
|
+
#
|
|
81
|
+
# @rbs level: String?
|
|
82
|
+
# @rbs provider: String?
|
|
83
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
84
|
+
def reasoning_options: (String? level, String? provider) -> Hash[Symbol, untyped]
|
|
85
|
+
|
|
86
|
+
# The parsed settings file, or an empty hash when the file is absent or
|
|
87
|
+
# malformed.
|
|
88
|
+
#
|
|
89
|
+
# @rbs path: String
|
|
90
|
+
# @rbs return: Hash[String, untyped]
|
|
91
|
+
def read: (String path) -> Hash[String, untyped]
|
|
92
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/token_tally.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
# Accumulates token usage across multiple turns in a session and computes an
|
|
4
|
+
# estimated cost based on a pricing object sourced from
|
|
5
|
+
# +Riffer::Rig::Settings+.
|
|
6
|
+
#
|
|
7
|
+
# Instantiate once per REPL session, call +add+ after each turn, then read
|
|
8
|
+
# +total_tokens+ and +estimated_cost+ for display.
|
|
9
|
+
#
|
|
10
|
+
# pricing = Riffer::Rig::Settings.pricing_for('anthropic/claude-sonnet-4-6')
|
|
11
|
+
# tally = Riffer::Rig::TokenTally.new(pricing: pricing)
|
|
12
|
+
# tally.add(usage)
|
|
13
|
+
# tally.estimated_cost # => 0.000042 (USD), or nil if pricing is nil
|
|
14
|
+
class Riffer::Rig::TokenTally
|
|
15
|
+
@input_tokens: Integer
|
|
16
|
+
|
|
17
|
+
@output_tokens: Integer
|
|
18
|
+
|
|
19
|
+
@cache_write_tokens: Integer
|
|
20
|
+
|
|
21
|
+
@cache_read_tokens: Integer
|
|
22
|
+
|
|
23
|
+
@pricing: Riffer::Rig::Settings::Pricing?
|
|
24
|
+
|
|
25
|
+
# @dynamic input_tokens, output_tokens, cache_write_tokens, cache_read_tokens
|
|
26
|
+
attr_reader input_tokens: untyped
|
|
27
|
+
|
|
28
|
+
# @dynamic input_tokens, output_tokens, cache_write_tokens, cache_read_tokens
|
|
29
|
+
attr_reader output_tokens: untyped
|
|
30
|
+
|
|
31
|
+
# @dynamic input_tokens, output_tokens, cache_write_tokens, cache_read_tokens
|
|
32
|
+
attr_reader cache_write_tokens: untyped
|
|
33
|
+
|
|
34
|
+
# @dynamic input_tokens, output_tokens, cache_write_tokens, cache_read_tokens
|
|
35
|
+
attr_reader cache_read_tokens: untyped
|
|
36
|
+
|
|
37
|
+
# @rbs ?pricing: Riffer::Rig::Settings::Pricing?
|
|
38
|
+
# @rbs return: void
|
|
39
|
+
def initialize: (?pricing: untyped) -> void
|
|
40
|
+
|
|
41
|
+
# Accumulates token counts from a +Riffer::Providers::TokenUsage+ object.
|
|
42
|
+
#
|
|
43
|
+
# @rbs usage: Riffer::Providers::TokenUsage
|
|
44
|
+
# @rbs return: void
|
|
45
|
+
def add: (Riffer::Providers::TokenUsage usage) -> void
|
|
46
|
+
|
|
47
|
+
# Returns the total token count across all categories.
|
|
48
|
+
#
|
|
49
|
+
# @rbs return: Integer
|
|
50
|
+
def total_tokens: () -> Integer
|
|
51
|
+
|
|
52
|
+
# Returns +true+ if any tokens have been counted.
|
|
53
|
+
#
|
|
54
|
+
# @rbs return: bool
|
|
55
|
+
def any?: () -> bool
|
|
56
|
+
|
|
57
|
+
# Returns the estimated cost in USD, or +nil+ if no pricing was provided.
|
|
58
|
+
#
|
|
59
|
+
# @rbs return: Float?
|
|
60
|
+
def estimated_cost: () -> Float?
|
|
61
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/tools/bash.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Tools::Bash < Riffer::Tool
|
|
4
|
+
DEFAULT_TIMEOUT_MS: Integer
|
|
5
|
+
|
|
6
|
+
MAX_OUTPUT_BYTES: Integer
|
|
7
|
+
|
|
8
|
+
# @rbs context: Riffer::Agent::Context?
|
|
9
|
+
# @rbs command: String
|
|
10
|
+
# @rbs ?timeout_ms: Integer
|
|
11
|
+
# @rbs return: Riffer::Tools::Response
|
|
12
|
+
def call: (context: Riffer::Agent::Context?, command: String, ?timeout_ms: untyped) -> Riffer::Tools::Response
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
# @rbs command: String
|
|
17
|
+
# @rbs timeout_seconds: Float
|
|
18
|
+
# @rbs return: [String, Integer]
|
|
19
|
+
def run: (String command, Float timeout_seconds) -> [ String, Integer ]
|
|
20
|
+
|
|
21
|
+
# @rbs pid: Integer
|
|
22
|
+
# @rbs return: Integer
|
|
23
|
+
def kill_group: (Integer pid) -> Integer
|
|
24
|
+
|
|
25
|
+
# @rbs output: String
|
|
26
|
+
# @rbs return: String
|
|
27
|
+
def truncate: (String output) -> String
|
|
28
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/tools/edit.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Tools::Edit < Riffer::Tool
|
|
4
|
+
# @rbs context: Riffer::Agent::Context?
|
|
5
|
+
# @rbs path: String
|
|
6
|
+
# @rbs old_string: String
|
|
7
|
+
# @rbs new_string: String
|
|
8
|
+
# @rbs ?replace_all: bool
|
|
9
|
+
# @rbs return: Riffer::Tools::Response
|
|
10
|
+
def call: (context: Riffer::Agent::Context?, path: String, old_string: String, new_string: String, ?replace_all: untyped) -> Riffer::Tools::Response
|
|
11
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/tools/read.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Tools::Read < Riffer::Tool
|
|
4
|
+
DEFAULT_LIMIT: Integer
|
|
5
|
+
|
|
6
|
+
# @rbs context: Riffer::Agent::Context?
|
|
7
|
+
# @rbs path: String
|
|
8
|
+
# @rbs ?offset: Integer?
|
|
9
|
+
# @rbs ?limit: Integer
|
|
10
|
+
# @rbs return: Riffer::Tools::Response
|
|
11
|
+
def call: (context: Riffer::Agent::Context?, path: String, ?offset: untyped, ?limit: untyped) -> Riffer::Tools::Response
|
|
12
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Generated from lib/riffer/rig/tools/write.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Tools::Write < Riffer::Tool
|
|
4
|
+
# @rbs context: Riffer::Agent::Context?
|
|
5
|
+
# @rbs path: String
|
|
6
|
+
# @rbs content: String
|
|
7
|
+
# @rbs return: Riffer::Tools::Response
|
|
8
|
+
def call: (context: Riffer::Agent::Context?, path: String, content: String) -> Riffer::Tools::Response
|
|
9
|
+
end
|