rbs_goose 0.1.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -2
- data/.ruby-version +1 -1
- data/Gemfile +20 -13
- data/Gemfile.lock +69 -38
- data/README-ja.md +88 -0
- data/README.md +25 -22
- data/Rakefile +53 -8
- data/Steepfile +1 -3
- data/evaluations/01_rbs_goose/claude_haiku.log +1494 -0
- data/evaluations/01_rbs_goose/claude_opus.log +1439 -0
- data/evaluations/01_rbs_goose/claude_sonnet.log +1437 -0
- data/evaluations/01_rbs_goose/gpt-3-5-turbo.log +1241 -0
- data/evaluations/01_rbs_goose/gpt-4-turbo.log +1304 -0
- data/examples/fix_errors/Steepfile +6 -0
- data/examples/fix_errors/error_messages.txt +26 -0
- data/{lib/rbs_goose/examples/rbs_samples → examples/fix_errors}/refined/sig/email.rbs +1 -1
- data/{lib/rbs_goose/examples/rbs_samples → examples/fix_errors}/refined/sig/person.rbs +2 -2
- data/{lib/rbs_goose/examples/rbs_samples → examples/fix_errors}/refined/sig/phone.rbs +1 -1
- data/examples/fix_errors/sig/email.rbs +11 -0
- data/examples/fix_errors/sig/person.rbs +15 -0
- data/examples/fix_errors/sig/phone.rbs +15 -0
- data/examples/rbs_samples/lib/email.rb +16 -0
- data/examples/rbs_samples/lib/person.rb +23 -0
- data/examples/rbs_samples/lib/phone.rb +22 -0
- data/examples/rbs_samples/refined/sig/email.rbs +11 -0
- data/examples/rbs_samples/refined/sig/person.rbs +15 -0
- data/examples/rbs_samples/refined/sig/phone.rbs +15 -0
- data/examples/rbs_syntax/Steepfile +6 -0
- data/examples/rbs_syntax/lib/a.rb +53 -0
- data/examples/rbs_syntax/lib/list.rb +9 -0
- data/examples/rbs_syntax/lib/main.rb +17 -0
- data/examples/rbs_syntax/refined/sig/a.rbs +47 -0
- data/examples/rbs_syntax/refined/sig/list.rbs +7 -0
- data/examples/rbs_syntax/refined/sig/main.rbs +26 -0
- data/examples/rbs_syntax/sig/a.rbs +47 -0
- data/examples/rbs_syntax/sig/list.rbs +7 -0
- data/examples/rbs_syntax/sig/main.rbs +26 -0
- data/lib/rbs_goose/configuration.rb +93 -23
- data/lib/rbs_goose/io/example.rb +2 -2
- data/lib/rbs_goose/io/example_group.rb +16 -7
- data/lib/rbs_goose/io/target_group.rb +1 -1
- data/lib/rbs_goose/io/typed_ruby.rb +3 -3
- data/lib/rbs_goose/orthoses/fix_error.rb +26 -0
- data/lib/rbs_goose/orthoses/infer.rb +26 -0
- data/lib/rbs_goose/orthoses.rb +9 -0
- data/lib/rbs_goose/templates/base.rb +77 -0
- data/lib/rbs_goose/templates/fix_error_template.rb +43 -0
- data/lib/rbs_goose/templates/infer_template.rb +30 -0
- data/lib/rbs_goose/templates.rb +3 -1
- data/lib/rbs_goose/type_inferrer.rb +85 -2
- data/lib/rbs_goose/version.rb +1 -1
- data/lib/rbs_goose.rb +24 -4
- data/rbs_collection.lock.yaml +63 -47
- data/renovate.json +5 -0
- data/sig/rbs_goose/configuration.rbs +20 -12
- data/sig/rbs_goose/io/example.rbs +7 -7
- data/sig/rbs_goose/io/example_group.rbs +9 -7
- data/sig/rbs_goose/io/file.rbs +10 -10
- data/sig/rbs_goose/io/target_group.rbs +2 -2
- data/sig/rbs_goose/io/typed_ruby.rbs +7 -7
- data/sig/rbs_goose/io.rbs +1 -1
- data/sig/rbs_goose/orthoses/fix_error.rbs +11 -0
- data/sig/rbs_goose/orthoses/infer.rbs +11 -0
- data/sig/rbs_goose/orthoses.rbs +2 -0
- data/sig/rbs_goose/templates/base.rbs +18 -0
- data/sig/rbs_goose/templates/fix_error_template.rbs +6 -0
- data/sig/rbs_goose/templates/infer_template.rbs +6 -0
- data/sig/rbs_goose/templates.rbs +1 -1
- data/sig/rbs_goose/type_inferrer.rbs +9 -1
- data/sig/rbs_goose.rbs +18 -9
- metadata +137 -21
- data/README-EN-US.md +0 -85
- data/lib/rbs_goose/templates/default_template.rb +0 -49
- data/sig/rbs_goose/templates/default_template.rbs +0 -10
- /data/{lib/rbs_goose/examples/rbs_samples → examples/fix_errors}/lib/email.rb +0 -0
- /data/{lib/rbs_goose/examples/rbs_samples → examples/fix_errors}/lib/person.rb +0 -0
- /data/{lib/rbs_goose/examples/rbs_samples → examples/fix_errors}/lib/phone.rb +0 -0
- /data/{lib/rbs_goose/examples → examples}/rbs_samples/Steepfile +0 -0
- /data/{lib/rbs_goose/examples → examples}/rbs_samples/sig/email.rbs +0 -0
- /data/{lib/rbs_goose/examples → examples}/rbs_samples/sig/person.rbs +0 -0
- /data/{lib/rbs_goose/examples → examples}/rbs_samples/sig/phone.rbs +0 -0
@@ -0,0 +1,1304 @@
|
|
1
|
+
I, [2024-05-08T16:24:42.109799 #67754] INFO -- : [RbsGoose::Orthoses::Infer]#call start
|
2
|
+
I, [2024-05-08T16:24:42.109839 #67754] INFO -- : [Orthoses::CreateFileByName]#call start
|
3
|
+
I, [2024-05-08T16:24:42.111041 #67754] INFO -- : [Orthoses::Filter]#call start
|
4
|
+
I, [2024-05-08T16:24:42.111051 #67754] INFO -- : [Orthoses::Mixin]#call start
|
5
|
+
I, [2024-05-08T16:24:42.111061 #67754] INFO -- : [Orthoses::RBSPrototypeRB]#call start
|
6
|
+
I, [2024-05-08T16:24:42.111065 #67754] INFO -- : [Orthoses::Store]#call start
|
7
|
+
I, [2024-05-08T16:24:42.111068 #67754] INFO -- : [loader].call start
|
8
|
+
I, [2024-05-08T16:24:42.111072 #67754] INFO -- : [loader].call end
|
9
|
+
I, [2024-05-08T16:24:42.111078 #67754] INFO -- : [Orthoses::Store]#call end
|
10
|
+
I, [2024-05-08T16:24:42.683748 #67754] INFO -- : [Orthoses::RBSPrototypeRB]#call end
|
11
|
+
I, [2024-05-08T16:24:43.226994 #67754] INFO -- : [Orthoses::Mixin]#call end
|
12
|
+
I, [2024-05-08T16:24:43.227039 #67754] INFO -- : [Orthoses::Filter]#call end
|
13
|
+
I, [2024-05-08T16:24:43.228694 #67754] INFO -- : Generate file to sig/rbs_goose.rbs
|
14
|
+
I, [2024-05-08T16:24:43.229157 #67754] INFO -- : Generate file to sig/rbs_goose/configuration.rbs
|
15
|
+
I, [2024-05-08T16:24:43.229289 #67754] INFO -- : Generate file to sig/rbs_goose/io.rbs
|
16
|
+
I, [2024-05-08T16:24:43.229488 #67754] INFO -- : Generate file to sig/rbs_goose/io/example.rbs
|
17
|
+
I, [2024-05-08T16:24:43.235905 #67754] INFO -- : Generate file to sig/rbs_goose/io/example_group.rbs
|
18
|
+
I, [2024-05-08T16:24:43.236205 #67754] INFO -- : Generate file to sig/rbs_goose/io/file.rbs
|
19
|
+
I, [2024-05-08T16:24:43.236353 #67754] INFO -- : Generate file to sig/rbs_goose/io/target_group.rbs
|
20
|
+
I, [2024-05-08T16:24:43.236520 #67754] INFO -- : Generate file to sig/rbs_goose/io/typed_ruby.rbs
|
21
|
+
I, [2024-05-08T16:24:43.236619 #67754] INFO -- : Generate file to sig/rbs_goose/orthoses.rbs
|
22
|
+
I, [2024-05-08T16:24:43.236803 #67754] INFO -- : Generate file to sig/rbs_goose/orthoses/fix_error.rbs
|
23
|
+
I, [2024-05-08T16:24:43.236936 #67754] INFO -- : Generate file to sig/rbs_goose/orthoses/infer.rbs
|
24
|
+
I, [2024-05-08T16:24:43.237042 #67754] INFO -- : Generate file to sig/rbs_goose/templates.rbs
|
25
|
+
I, [2024-05-08T16:24:43.237295 #67754] INFO -- : Generate file to sig/rbs_goose/templates/base.rbs
|
26
|
+
I, [2024-05-08T16:24:43.237730 #67754] INFO -- : Generate file to sig/rbs_goose/templates/fix_error_template.rbs
|
27
|
+
I, [2024-05-08T16:24:43.237918 #67754] INFO -- : Generate file to sig/rbs_goose/templates/infer_template.rbs
|
28
|
+
I, [2024-05-08T16:24:43.238097 #67754] INFO -- : Generate file to sig/rbs_goose/type_inferrer.rbs
|
29
|
+
I, [2024-05-08T16:24:43.238189 #67754] INFO -- : Generate file to sig/rbs_goose/error.rbs
|
30
|
+
I, [2024-05-08T16:24:43.238216 #67754] INFO -- : [Orthoses::CreateFileByName]#call end
|
31
|
+
Run RbsGoose.(Code Directory: lib, Signature Directory: sig)
|
32
|
+
!!!!!!!! Prompt !!!!!!!!
|
33
|
+
|
34
|
+
role: user
|
35
|
+
content:
|
36
|
+
Act as Ruby type inferrer.
|
37
|
+
When ruby source codes and RBS type signatures are given, refine each RBS type signatures. Each file should be split in markdown code format.
|
38
|
+
Use class names, variable names, etc., to infer type.
|
39
|
+
|
40
|
+
|
41
|
+
========Input========
|
42
|
+
```ruby:lib/email.rb
|
43
|
+
class Email
|
44
|
+
# @dynamic address
|
45
|
+
attr_reader :address
|
46
|
+
|
47
|
+
def initialize(address:)
|
48
|
+
@address = address
|
49
|
+
end
|
50
|
+
|
51
|
+
def ==(other)
|
52
|
+
other.is_a?(self.class) && other.address == address
|
53
|
+
end
|
54
|
+
|
55
|
+
def hash
|
56
|
+
self.class.hash ^ address.hash
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
```rbs:sig/email.rbs
|
62
|
+
class Email
|
63
|
+
@address: untyped
|
64
|
+
|
65
|
+
attr_reader address: untyped
|
66
|
+
|
67
|
+
def initialize: (address: untyped) -> void
|
68
|
+
|
69
|
+
def ==: (untyped other) -> untyped
|
70
|
+
|
71
|
+
def hash: () -> untyped
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
```ruby:lib/person.rb
|
76
|
+
class Person
|
77
|
+
# @dynamic name, contacts
|
78
|
+
attr_reader :name
|
79
|
+
attr_reader :contacts
|
80
|
+
|
81
|
+
def initialize(name:)
|
82
|
+
@name = name
|
83
|
+
@contacts = []
|
84
|
+
end
|
85
|
+
|
86
|
+
def name=(name)
|
87
|
+
@name = name
|
88
|
+
end
|
89
|
+
|
90
|
+
def guess_country()
|
91
|
+
contacts.map do |contact|
|
92
|
+
case contact
|
93
|
+
when Phone
|
94
|
+
contact.country
|
95
|
+
end
|
96
|
+
end.compact.first
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
```rbs:sig/person.rbs
|
102
|
+
class Person
|
103
|
+
@name: untyped
|
104
|
+
|
105
|
+
@contacts: untyped
|
106
|
+
|
107
|
+
attr_reader name: untyped
|
108
|
+
|
109
|
+
attr_reader contacts: untyped
|
110
|
+
|
111
|
+
def initialize: (name: untyped) -> void
|
112
|
+
|
113
|
+
def name=: (untyped name) -> void
|
114
|
+
|
115
|
+
def guess_country: () -> untyped
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
```ruby:lib/phone.rb
|
120
|
+
class Phone
|
121
|
+
# @dynamic country, number
|
122
|
+
attr_reader :country, :number
|
123
|
+
|
124
|
+
def initialize(country:, number:)
|
125
|
+
@country = country
|
126
|
+
@number = number
|
127
|
+
end
|
128
|
+
|
129
|
+
def ==(other)
|
130
|
+
if other.is_a?(Phone)
|
131
|
+
# @type var other: Phone
|
132
|
+
other.country == country && other.number == number
|
133
|
+
else
|
134
|
+
false
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def hash
|
139
|
+
self.class.hash ^ country.hash ^ number.hash
|
140
|
+
end
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
```rbs:sig/phone.rbs
|
145
|
+
class Phone
|
146
|
+
@country: untyped
|
147
|
+
|
148
|
+
@number: untyped
|
149
|
+
|
150
|
+
attr_reader country: untyped
|
151
|
+
|
152
|
+
attr_reader number: untyped
|
153
|
+
|
154
|
+
def initialize: (country: untyped, number: untyped) -> void
|
155
|
+
|
156
|
+
def ==: (untyped other) -> (untyped | nil)
|
157
|
+
|
158
|
+
def hash: () -> untyped
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
|
163
|
+
========Output========
|
164
|
+
```rbs:sig/email.rbs
|
165
|
+
class Email
|
166
|
+
@address: String
|
167
|
+
|
168
|
+
attr_reader address: String
|
169
|
+
|
170
|
+
def initialize: (address: String) -> void
|
171
|
+
|
172
|
+
def ==: (Object other) -> bool
|
173
|
+
|
174
|
+
def hash: () -> Integer
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
```rbs:sig/person.rbs
|
179
|
+
class Person
|
180
|
+
@name: String
|
181
|
+
|
182
|
+
@contacts: Array[(Email | Phone)]
|
183
|
+
|
184
|
+
attr_reader name: String
|
185
|
+
|
186
|
+
attr_reader contacts: Array[(Email | Phone)]
|
187
|
+
|
188
|
+
def initialize: (name: String) -> void
|
189
|
+
|
190
|
+
def name=: (String name) -> void
|
191
|
+
|
192
|
+
def guess_country: () -> (String | nil)
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
```rbs:sig/phone.rbs
|
197
|
+
class Phone
|
198
|
+
@country: String
|
199
|
+
|
200
|
+
@number: String
|
201
|
+
|
202
|
+
attr_reader country: String
|
203
|
+
|
204
|
+
attr_reader number: String
|
205
|
+
|
206
|
+
def initialize: (country: String, number: String) -> void
|
207
|
+
|
208
|
+
def ==: (Object other) -> (bool | nil)
|
209
|
+
|
210
|
+
def hash: () -> Integer
|
211
|
+
end
|
212
|
+
```
|
213
|
+
|
214
|
+
|
215
|
+
========Input========
|
216
|
+
```ruby:lib/rbs_goose/configuration.rb
|
217
|
+
# frozen_string_literal: true
|
218
|
+
|
219
|
+
require 'langchain'
|
220
|
+
|
221
|
+
require 'forwardable'
|
222
|
+
|
223
|
+
module RbsGoose
|
224
|
+
class Configuration
|
225
|
+
extend Forwardable
|
226
|
+
|
227
|
+
LLMConfig = Struct.new(:client, :mode, keyword_init: true)
|
228
|
+
|
229
|
+
TemplateConfig = Struct.new(:instruction, :example_groups, :template_class, keyword_init: true) do
|
230
|
+
def build_template
|
231
|
+
template_class.new(instruction:, example_groups:)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def initialize(&)
|
236
|
+
self.infer_template = default_infer_template
|
237
|
+
self.fix_error_template = default_fix_error_template
|
238
|
+
instance_eval(&) if block_given?
|
239
|
+
end
|
240
|
+
|
241
|
+
attr_accessor :llm, :infer_template, :fix_error_template
|
242
|
+
|
243
|
+
def use_open_ai( # rubocop:disable Metrics/MethodLength
|
244
|
+
open_ai_access_token, model_name: 'gpt-3.5-turbo-1106', mode: :chat,
|
245
|
+
llm_options: {}, default_options: {}
|
246
|
+
)
|
247
|
+
@llm = LLMConfig.new(
|
248
|
+
client: ::Langchain::LLM::OpenAI.new(
|
249
|
+
api_key: open_ai_access_token,
|
250
|
+
llm_options: { request_timeout: 600 }.merge(llm_options),
|
251
|
+
default_options: {
|
252
|
+
completion_model_name: model_name,
|
253
|
+
chat_completion_model_name: model_name
|
254
|
+
}.merge(default_options)
|
255
|
+
),
|
256
|
+
mode:
|
257
|
+
)
|
258
|
+
end
|
259
|
+
|
260
|
+
def use_anthropic(access_token, model_name: 'claude-3-haiku-20240307', mode: :chat, default_options: {}) # rubocop:disable Metrics/MethodLength
|
261
|
+
@llm = LLMConfig.new(
|
262
|
+
client: ::Langchain::LLM::Anthropic.new(
|
263
|
+
api_key: access_token,
|
264
|
+
default_options: {
|
265
|
+
completion_model_name: model_name,
|
266
|
+
chat_completion_model_name: model_name,
|
267
|
+
max_tokens_to_sample: 4096
|
268
|
+
}.merge(default_options)
|
269
|
+
),
|
270
|
+
mode:
|
271
|
+
)
|
272
|
+
end
|
273
|
+
|
274
|
+
def use_ollama(url: 'http://localhost:11434', model_name: 'codegemma', mode: :complete, default_options: {}) # rubocop:disable Metrics/MethodLength
|
275
|
+
@llm = LLMConfig.new(
|
276
|
+
client: ::Langchain::LLM::Ollama.new(
|
277
|
+
url:,
|
278
|
+
default_options: {
|
279
|
+
temperature: 0.0,
|
280
|
+
completion_model_name: model_name,
|
281
|
+
chat_completion_model_name: model_name
|
282
|
+
}.merge(default_options)
|
283
|
+
),
|
284
|
+
mode:
|
285
|
+
)
|
286
|
+
end
|
287
|
+
|
288
|
+
def_delegator :llm, :client, :llm_client
|
289
|
+
def_delegator :llm, :mode, :llm_mode
|
290
|
+
def_delegator :infer_template, :instruction, :infer_instruction
|
291
|
+
def_delegator :infer_template, :example_groups, :infer_example_groups
|
292
|
+
|
293
|
+
private
|
294
|
+
|
295
|
+
def default_infer_template
|
296
|
+
TemplateConfig.new(
|
297
|
+
instruction: default_infer_instruction,
|
298
|
+
example_groups: default_infer_example_groups,
|
299
|
+
template_class: Templates::InferTemplate
|
300
|
+
)
|
301
|
+
end
|
302
|
+
|
303
|
+
def default_fix_error_template
|
304
|
+
TemplateConfig.new(
|
305
|
+
instruction: default_fix_error_instruction,
|
306
|
+
example_groups: default_fix_error_example_groups,
|
307
|
+
template_class: Templates::FixErrorTemplate
|
308
|
+
)
|
309
|
+
end
|
310
|
+
|
311
|
+
def default_infer_instruction
|
312
|
+
<<~INSTRUCTION
|
313
|
+
Act as Ruby type inferrer.
|
314
|
+
When ruby source codes and RBS type signatures are given, refine each RBS type signatures. Each file should be split in markdown code format.
|
315
|
+
Use class names, variable names, etc., to infer type.
|
316
|
+
INSTRUCTION
|
317
|
+
end
|
318
|
+
|
319
|
+
def default_infer_example_groups
|
320
|
+
[RbsGoose::IO::ExampleGroup.default_examples[:rbs_samples]]
|
321
|
+
end
|
322
|
+
|
323
|
+
def default_fix_error_instruction
|
324
|
+
<<~INSTRUCTION
|
325
|
+
You are a highly skilled programmer.
|
326
|
+
Based on the following Ruby code, the RBS code that is a type definition, and the type checking error messages for them, modify the RBS code and output it.
|
327
|
+
All RBS files should be output, regardless of whether they have been modified or not.
|
328
|
+
INSTRUCTION
|
329
|
+
end
|
330
|
+
|
331
|
+
def default_fix_error_example_groups
|
332
|
+
[RbsGoose::IO::ExampleGroup.default_examples[:fix_errors]]
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
```
|
337
|
+
|
338
|
+
```rbs:sig/rbs_goose/configuration.rbs
|
339
|
+
class RbsGoose::Configuration
|
340
|
+
@llm: untyped
|
341
|
+
extend Forwardable
|
342
|
+
LLMConfig: untyped
|
343
|
+
TemplateConfig: untyped
|
344
|
+
def initialize: () ?{ () -> untyped } -> void
|
345
|
+
attr_accessor llm: untyped
|
346
|
+
attr_accessor infer_template: untyped
|
347
|
+
attr_accessor fix_error_template: untyped
|
348
|
+
def use_open_ai: (untyped open_ai_access_token, ?model_name: ::String, ?mode: ::Symbol, ?llm_options: ::Hash[untyped, untyped], ?default_options: ::Hash[untyped, untyped]) -> untyped
|
349
|
+
def use_anthropic: (untyped access_token, ?model_name: ::String, ?mode: ::Symbol, ?default_options: ::Hash[untyped, untyped]) -> untyped
|
350
|
+
def use_ollama: (?url: ::String, ?model_name: ::String, ?mode: ::Symbol, ?default_options: ::Hash[untyped, untyped]) -> untyped
|
351
|
+
private def default_infer_template: () -> untyped
|
352
|
+
private def default_fix_error_template: () -> untyped
|
353
|
+
private def default_infer_instruction: () -> "Act as Ruby type inferrer.\nWhen ruby source codes and RBS type signatures are given, refine each RBS type signatures. Each file should be split in markdown code format.\nUse class names, variable names, etc., to infer type.\n"
|
354
|
+
private def default_infer_example_groups: () -> ::Array[untyped]
|
355
|
+
private def default_fix_error_instruction: () -> "You are a highly skilled programmer.\nBased on the following Ruby code, the RBS code that is a type definition, and the type checking error messages for them, modify the RBS code and output it.\nAll RBS files should be output, regardless of whether they have been modified or not.\n"
|
356
|
+
private def default_fix_error_example_groups: () -> ::Array[untyped]
|
357
|
+
end
|
358
|
+
```
|
359
|
+
|
360
|
+
```ruby:lib/rbs_goose/io/example.rb
|
361
|
+
# frozen_string_literal: true
|
362
|
+
|
363
|
+
module RbsGoose
|
364
|
+
module IO
|
365
|
+
class Example
|
366
|
+
class << self
|
367
|
+
def from_path(ruby_path:, rbs_path:, refined_rbs_dir:, base_path:)
|
368
|
+
Example.new(
|
369
|
+
typed_ruby: TypedRuby.from_path(ruby_path:, rbs_path:, base_path:),
|
370
|
+
refined_rbs: File.new(path: rbs_path, base_path: ::File.join(base_path, refined_rbs_dir))
|
371
|
+
)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def initialize(typed_ruby:, refined_rbs:)
|
376
|
+
@typed_ruby = typed_ruby
|
377
|
+
@refined_rbs = refined_rbs
|
378
|
+
end
|
379
|
+
|
380
|
+
def to_h
|
381
|
+
{ typed_ruby:, refined_rbs: }
|
382
|
+
end
|
383
|
+
|
384
|
+
attr_reader :typed_ruby, :refined_rbs
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
```
|
389
|
+
|
390
|
+
```rbs:sig/rbs_goose/io/example.rbs
|
391
|
+
class RbsGoose::IO::Example
|
392
|
+
@typed_ruby: untyped
|
393
|
+
@refined_rbs: untyped
|
394
|
+
def self.from_path: (ruby_path: untyped, rbs_path: untyped, refined_rbs_dir: untyped, base_path: untyped) -> untyped
|
395
|
+
def initialize: (typed_ruby: untyped, refined_rbs: untyped) -> void
|
396
|
+
def to_h: () -> { typed_ruby: untyped, refined_rbs: untyped }
|
397
|
+
attr_reader typed_ruby: untyped
|
398
|
+
attr_reader refined_rbs: untyped
|
399
|
+
end
|
400
|
+
```
|
401
|
+
|
402
|
+
```ruby:lib/rbs_goose/io/example_group.rb
|
403
|
+
# frozen_string_literal: true
|
404
|
+
|
405
|
+
module RbsGoose
|
406
|
+
module IO
|
407
|
+
class ExampleGroup < Array
|
408
|
+
attr_accessor :error_messages
|
409
|
+
|
410
|
+
class << self
|
411
|
+
def load_from(base_path, code_dir: 'lib', sig_dir: 'sig', refined_dir: 'refined')
|
412
|
+
new.tap do |group|
|
413
|
+
Dir.glob('**/*.rb', base: ::File.join(base_path, code_dir)).each do |path|
|
414
|
+
group << load_example(base_path, code_dir, path, refined_dir, sig_dir)
|
415
|
+
end
|
416
|
+
# Read error messages if exists
|
417
|
+
error_path = ::File.join(base_path, 'error_messages.txt')
|
418
|
+
group.error_messages = ::File.read(error_path).strip if ::File.exist?(error_path)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
def default_examples
|
423
|
+
example_dir = ::File.join(__dir__.to_s, '../../../examples')
|
424
|
+
@default_examples ||= Dir.glob('*', base: example_dir).to_h do |dir|
|
425
|
+
[dir.to_sym, load_from(::File.join(example_dir, dir))]
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
private
|
430
|
+
|
431
|
+
def load_example(base_path, code_dir, path, refined_dir, sig_dir)
|
432
|
+
Example.from_path(
|
433
|
+
ruby_path: ::File.join(code_dir, path),
|
434
|
+
rbs_path: to_rbs_path(path, sig_dir),
|
435
|
+
refined_rbs_dir: refined_dir,
|
436
|
+
base_path:
|
437
|
+
)
|
438
|
+
end
|
439
|
+
|
440
|
+
def to_rbs_path(path, sig_dir)
|
441
|
+
::File.join(sig_dir, "#{path}s")
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def to_target_group
|
446
|
+
TargetGroup.new.tap do |g|
|
447
|
+
each { g << _1.typed_ruby }
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def to_refined_rbs_list
|
452
|
+
map(&:refined_rbs)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
```
|
458
|
+
|
459
|
+
```rbs:sig/rbs_goose/io/example_group.rbs
|
460
|
+
class RbsGoose::IO::ExampleGroup < ::Array[untyped]
|
461
|
+
self.@default_examples: untyped
|
462
|
+
attr_accessor error_messages: untyped
|
463
|
+
def self.load_from: (untyped base_path, ?code_dir: ::String, ?sig_dir: ::String, ?refined_dir: ::String) -> untyped
|
464
|
+
def self.default_examples: () -> untyped
|
465
|
+
private def self.load_example: (untyped base_path, untyped code_dir, untyped path, untyped refined_dir, untyped sig_dir) -> untyped
|
466
|
+
private def self.to_rbs_path: (untyped path, untyped sig_dir) -> untyped
|
467
|
+
def to_target_group: () -> untyped
|
468
|
+
def to_refined_rbs_list: () -> untyped
|
469
|
+
end
|
470
|
+
```
|
471
|
+
|
472
|
+
```ruby:lib/rbs_goose/io/file.rb
|
473
|
+
# frozen_string_literal: true
|
474
|
+
|
475
|
+
module RbsGoose
|
476
|
+
module IO
|
477
|
+
class File
|
478
|
+
MARKDOWN_REGEXP = /\A```(?<type>ruby|rbs):(?<path>.+)\n(?<content>[\s\S]+)```\Z/
|
479
|
+
|
480
|
+
class << self
|
481
|
+
def from_markdown(markdown)
|
482
|
+
parsed = markdown.match(MARKDOWN_REGEXP)
|
483
|
+
raise ArgumentError, "Ruby or RBS Markdown parsing failed.\n#{markdown}" unless parsed
|
484
|
+
|
485
|
+
new(path: parsed[:path].to_s, content: parsed[:content].to_s)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
def initialize(path:, content: nil, base_path: nil)
|
490
|
+
@path = path
|
491
|
+
@base_path = base_path
|
492
|
+
@content = content&.strip
|
493
|
+
load_content if @content.nil?
|
494
|
+
end
|
495
|
+
|
496
|
+
def load_content
|
497
|
+
@content = ::File.read(absolute_path).strip
|
498
|
+
end
|
499
|
+
|
500
|
+
def absolute_path
|
501
|
+
base_path ? ::File.join(base_path, path) : path
|
502
|
+
end
|
503
|
+
|
504
|
+
def type
|
505
|
+
@type ||= case path
|
506
|
+
in /\.rb\z/
|
507
|
+
:ruby
|
508
|
+
in /\.rbs\z/
|
509
|
+
:rbs
|
510
|
+
else
|
511
|
+
raise ArgumentError, "Unknown file type: #{path}"
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def to_s
|
516
|
+
"```#{type}:#{path}\n#{content}\n```\n"
|
517
|
+
end
|
518
|
+
|
519
|
+
def content=(content)
|
520
|
+
@content = content.strip
|
521
|
+
end
|
522
|
+
|
523
|
+
def write
|
524
|
+
::File.write(path, content)
|
525
|
+
end
|
526
|
+
|
527
|
+
def ==(other)
|
528
|
+
self.class == other.class &&
|
529
|
+
@path == other.path &&
|
530
|
+
@content == other.content
|
531
|
+
end
|
532
|
+
|
533
|
+
attr_reader :path, :content, :base_path
|
534
|
+
end
|
535
|
+
end
|
536
|
+
end
|
537
|
+
```
|
538
|
+
|
539
|
+
```rbs:sig/rbs_goose/io/file.rbs
|
540
|
+
class RbsGoose::IO::File
|
541
|
+
@path: untyped
|
542
|
+
@base_path: untyped
|
543
|
+
@content: untyped
|
544
|
+
@type: untyped
|
545
|
+
MARKDOWN_REGEXP: ::Regexp
|
546
|
+
def self.from_markdown: (untyped markdown) -> untyped
|
547
|
+
def initialize: (path: untyped, ?content: untyped?, ?base_path: untyped?) -> void
|
548
|
+
def load_content: () -> untyped
|
549
|
+
def absolute_path: () -> untyped
|
550
|
+
def type: () -> untyped
|
551
|
+
def to_s: () -> ::String
|
552
|
+
def content=: (untyped content) -> untyped
|
553
|
+
def write: () -> untyped
|
554
|
+
def ==: (untyped other) -> untyped
|
555
|
+
attr_reader path: untyped
|
556
|
+
attr_reader content: untyped
|
557
|
+
attr_reader base_path: untyped
|
558
|
+
end
|
559
|
+
```
|
560
|
+
|
561
|
+
```ruby:lib/rbs_goose/io/target_group.rb
|
562
|
+
# frozen_string_literal: true
|
563
|
+
|
564
|
+
module RbsGoose
|
565
|
+
module IO
|
566
|
+
class TargetGroup < Array
|
567
|
+
class << self
|
568
|
+
def load_from(base_path, code_dir: 'lib', sig_dir: 'sig')
|
569
|
+
new.tap do |group|
|
570
|
+
Dir.glob('**/*.rb', base: ::File.join(base_path, code_dir)).each do |path|
|
571
|
+
group << TypedRuby.from_path(
|
572
|
+
ruby_path: ::File.join(code_dir, path),
|
573
|
+
rbs_path: ::File.join(sig_dir, "#{path}s"),
|
574
|
+
base_path:
|
575
|
+
)
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
580
|
+
end
|
581
|
+
end
|
582
|
+
end
|
583
|
+
```
|
584
|
+
|
585
|
+
```rbs:sig/rbs_goose/io/target_group.rbs
|
586
|
+
class RbsGoose::IO::TargetGroup < ::Array[untyped]
|
587
|
+
def self.load_from: (untyped base_path, ?code_dir: ::String, ?sig_dir: ::String) -> untyped
|
588
|
+
end
|
589
|
+
```
|
590
|
+
|
591
|
+
```ruby:lib/rbs_goose/io/typed_ruby.rb
|
592
|
+
# frozen_string_literal: true
|
593
|
+
|
594
|
+
module RbsGoose
|
595
|
+
module IO
|
596
|
+
class TypedRuby
|
597
|
+
class << self
|
598
|
+
def from_path(ruby_path:, rbs_path:, base_path:)
|
599
|
+
ruby = File.new(path: ruby_path, base_path:)
|
600
|
+
rbs = begin
|
601
|
+
File.new(path: rbs_path, base_path:)
|
602
|
+
rescue StandardError
|
603
|
+
nil
|
604
|
+
end
|
605
|
+
new(ruby:, rbs:)
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
def initialize(ruby:, rbs:)
|
610
|
+
raise ArgumentError, 'ruby must have ".rb" extension' unless ruby.type == :ruby
|
611
|
+
raise ArgumentError, 'rbs must have ".rbs" extension' if !rbs.nil? && rbs.type != :rbs
|
612
|
+
|
613
|
+
@ruby = ruby
|
614
|
+
@rbs = rbs
|
615
|
+
end
|
616
|
+
|
617
|
+
def to_s
|
618
|
+
rbs.nil? ? ruby.to_s : "#{ruby}\n#{rbs}"
|
619
|
+
end
|
620
|
+
|
621
|
+
attr_reader :ruby, :rbs
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|
625
|
+
```
|
626
|
+
|
627
|
+
```rbs:sig/rbs_goose/io/typed_ruby.rbs
|
628
|
+
class RbsGoose::IO::TypedRuby
|
629
|
+
@ruby: untyped
|
630
|
+
@rbs: untyped
|
631
|
+
def self.from_path: (ruby_path: untyped, rbs_path: untyped, base_path: untyped) -> untyped
|
632
|
+
def initialize: (ruby: untyped, rbs: untyped) -> void
|
633
|
+
def to_s: () -> (untyped | ::String)
|
634
|
+
attr_reader ruby: untyped
|
635
|
+
attr_reader rbs: untyped
|
636
|
+
end
|
637
|
+
```
|
638
|
+
|
639
|
+
```ruby:lib/rbs_goose/io.rb
|
640
|
+
# frozen_string_literal: true
|
641
|
+
|
642
|
+
require_relative 'io/example'
|
643
|
+
require_relative 'io/example_group'
|
644
|
+
require_relative 'io/file'
|
645
|
+
require_relative 'io/target_group'
|
646
|
+
require_relative 'io/typed_ruby'
|
647
|
+
|
648
|
+
module RbsGoose
|
649
|
+
module IO
|
650
|
+
end
|
651
|
+
end
|
652
|
+
```
|
653
|
+
|
654
|
+
```rbs:sig/rbs_goose/io.rbs
|
655
|
+
module RbsGoose::IO
|
656
|
+
end
|
657
|
+
```
|
658
|
+
|
659
|
+
```ruby:lib/rbs_goose/orthoses/fix_error.rb
|
660
|
+
# frozen_string_literal: true
|
661
|
+
|
662
|
+
require 'orthoses/outputable'
|
663
|
+
|
664
|
+
module RbsGoose
|
665
|
+
module Orthoses
|
666
|
+
# Call RbsGoose::TypeInferrer to fix steep type errors.
|
667
|
+
# use TbsGoose::Orthoses::FixError, code_dir: 'lib', sig_dir: 'sig' do |config|
|
668
|
+
# config.use_open_ai('open_ai_access_token')
|
669
|
+
# end
|
670
|
+
class FixError
|
671
|
+
def initialize(loader, code_dir: 'lib', sig_dir: 'sig', &)
|
672
|
+
@loader = loader
|
673
|
+
@code_dir = code_dir
|
674
|
+
@sig_dir = sig_dir
|
675
|
+
RbsGoose.configure(&) if block_given?
|
676
|
+
end
|
677
|
+
|
678
|
+
def call
|
679
|
+
@loader.call.tap do
|
680
|
+
RbsGoose.fix_error(code_dir: @code_dir, sig_dir: @sig_dir)
|
681
|
+
end
|
682
|
+
end
|
683
|
+
end
|
684
|
+
end
|
685
|
+
end
|
686
|
+
```
|
687
|
+
|
688
|
+
```rbs:sig/rbs_goose/orthoses/fix_error.rbs
|
689
|
+
# Call RbsGoose::TypeInferrer to fix steep type errors.
|
690
|
+
# use TbsGoose::Orthoses::FixError, code_dir: 'lib', sig_dir: 'sig' do |config|
|
691
|
+
# config.use_open_ai('open_ai_access_token')
|
692
|
+
# end
|
693
|
+
class RbsGoose::Orthoses::FixError
|
694
|
+
@loader: untyped
|
695
|
+
@code_dir: untyped
|
696
|
+
@sig_dir: untyped
|
697
|
+
def initialize: (untyped loader, ?code_dir: ::String, ?sig_dir: ::String) ?{ () -> untyped } -> void
|
698
|
+
def call: () -> untyped
|
699
|
+
end
|
700
|
+
```
|
701
|
+
|
702
|
+
```ruby:lib/rbs_goose/orthoses/infer.rb
|
703
|
+
# frozen_string_literal: true
|
704
|
+
|
705
|
+
require 'orthoses/outputable'
|
706
|
+
|
707
|
+
module RbsGoose
|
708
|
+
module Orthoses
|
709
|
+
# Call RbsGoose::TypeInferrer to infer RBS type signatures.
|
710
|
+
# use TbsGoose::Orthoses::Infer, code_dir: 'lib', sig_dir: 'sig' do |config|
|
711
|
+
# config.use_open_ai('open_ai_access_token')
|
712
|
+
# end
|
713
|
+
class Infer
|
714
|
+
def initialize(loader, code_dir: 'lib', sig_dir: 'sig', &)
|
715
|
+
@loader = loader
|
716
|
+
@code_dir = code_dir
|
717
|
+
@sig_dir = sig_dir
|
718
|
+
RbsGoose.configure(&) if block_given?
|
719
|
+
end
|
720
|
+
|
721
|
+
def call
|
722
|
+
@loader.call.tap do
|
723
|
+
RbsGoose.run(code_dir: @code_dir, sig_dir: @sig_dir)
|
724
|
+
end
|
725
|
+
end
|
726
|
+
end
|
727
|
+
end
|
728
|
+
end
|
729
|
+
```
|
730
|
+
|
731
|
+
```rbs:sig/rbs_goose/orthoses/infer.rbs
|
732
|
+
# Call RbsGoose::TypeInferrer to infer RBS type signatures.
|
733
|
+
# use TbsGoose::Orthoses::Infer, code_dir: 'lib', sig_dir: 'sig' do |config|
|
734
|
+
# config.use_open_ai('open_ai_access_token')
|
735
|
+
# end
|
736
|
+
class RbsGoose::Orthoses::Infer
|
737
|
+
@loader: untyped
|
738
|
+
@code_dir: untyped
|
739
|
+
@sig_dir: untyped
|
740
|
+
def initialize: (untyped loader, ?code_dir: ::String, ?sig_dir: ::String) ?{ () -> untyped } -> void
|
741
|
+
def call: () -> untyped
|
742
|
+
end
|
743
|
+
```
|
744
|
+
|
745
|
+
```ruby:lib/rbs_goose/orthoses.rb
|
746
|
+
# frozen_string_literal: true
|
747
|
+
|
748
|
+
require_relative 'orthoses/fix_error'
|
749
|
+
require_relative 'orthoses/infer'
|
750
|
+
|
751
|
+
module RbsGoose
|
752
|
+
module Orthoses
|
753
|
+
end
|
754
|
+
end
|
755
|
+
```
|
756
|
+
|
757
|
+
```rbs:sig/rbs_goose/orthoses.rbs
|
758
|
+
module RbsGoose::Orthoses
|
759
|
+
end
|
760
|
+
```
|
761
|
+
|
762
|
+
```ruby:lib/rbs_goose/templates/base.rb
|
763
|
+
# frozen_string_literal: true
|
764
|
+
|
765
|
+
require 'langchain'
|
766
|
+
|
767
|
+
module RbsGoose
|
768
|
+
module Templates
|
769
|
+
class Base
|
770
|
+
def initialize(instruction:, example_groups:)
|
771
|
+
@instruction = instruction
|
772
|
+
@example_groups = example_groups
|
773
|
+
end
|
774
|
+
|
775
|
+
def format(**args)
|
776
|
+
Langchain::Prompt::FewShotPromptTemplate.new(
|
777
|
+
prefix:,
|
778
|
+
suffix:,
|
779
|
+
example_prompt:,
|
780
|
+
examples: example_groups.map { transform_example_group(_1) },
|
781
|
+
input_variables:
|
782
|
+
).format(**format_args(args)).strip
|
783
|
+
end
|
784
|
+
|
785
|
+
def format_system_prompt
|
786
|
+
prefix.strip
|
787
|
+
end
|
788
|
+
|
789
|
+
def format_user_prompt(**args)
|
790
|
+
Langchain::Prompt::FewShotPromptTemplate.new(
|
791
|
+
prefix: '',
|
792
|
+
suffix:,
|
793
|
+
example_prompt:,
|
794
|
+
examples: example_groups.map { transform_example_group(_1) },
|
795
|
+
input_variables:
|
796
|
+
).format(**format_args(args)).strip
|
797
|
+
end
|
798
|
+
|
799
|
+
def parse_result(result)
|
800
|
+
result.scan(/```.+?```/m).map { IO::File.from_markdown(_1) }
|
801
|
+
end
|
802
|
+
|
803
|
+
private
|
804
|
+
|
805
|
+
def input_template_string
|
806
|
+
raise NotImplementedError
|
807
|
+
end
|
808
|
+
|
809
|
+
def input_variables
|
810
|
+
raise NotImplementedError
|
811
|
+
end
|
812
|
+
|
813
|
+
def transform_example_group(example_group)
|
814
|
+
raise NotImplementedError
|
815
|
+
end
|
816
|
+
|
817
|
+
def format_args(args)
|
818
|
+
raise NotImplementedError
|
819
|
+
end
|
820
|
+
|
821
|
+
def prefix
|
822
|
+
instruction
|
823
|
+
end
|
824
|
+
|
825
|
+
def suffix
|
826
|
+
"#{input_template_string}\n"
|
827
|
+
end
|
828
|
+
|
829
|
+
def example_prompt
|
830
|
+
Langchain::Prompt::PromptTemplate.new(
|
831
|
+
template: "#{input_template_string}\n{refined_rbs_list}",
|
832
|
+
input_variables: input_variables + ['refined_rbs_list']
|
833
|
+
)
|
834
|
+
end
|
835
|
+
|
836
|
+
attr_reader :instruction, :example_groups
|
837
|
+
end
|
838
|
+
end
|
839
|
+
end
|
840
|
+
```
|
841
|
+
|
842
|
+
```rbs:sig/rbs_goose/templates/base.rbs
|
843
|
+
class RbsGoose::Templates::Base
|
844
|
+
@instruction: untyped
|
845
|
+
@example_groups: untyped
|
846
|
+
def initialize: (instruction: untyped, example_groups: untyped) -> void
|
847
|
+
def format: (**untyped args) -> untyped
|
848
|
+
def format_system_prompt: () -> untyped
|
849
|
+
def format_user_prompt: (**untyped args) -> untyped
|
850
|
+
def parse_result: (untyped result) -> untyped
|
851
|
+
private def input_template_string: () -> untyped
|
852
|
+
private def input_variables: () -> untyped
|
853
|
+
private def transform_example_group: (untyped example_group) -> untyped
|
854
|
+
private def format_args: (untyped args) -> untyped
|
855
|
+
private def prefix: () -> untyped
|
856
|
+
private def suffix: () -> ::String
|
857
|
+
private def example_prompt: () -> untyped
|
858
|
+
private attr_reader instruction: untyped
|
859
|
+
private attr_reader example_groups: untyped
|
860
|
+
end
|
861
|
+
```
|
862
|
+
|
863
|
+
```ruby:lib/rbs_goose/templates/fix_error_template.rb
|
864
|
+
# frozen_string_literal: true
|
865
|
+
|
866
|
+
require 'langchain'
|
867
|
+
|
868
|
+
module RbsGoose
|
869
|
+
module Templates
|
870
|
+
class FixErrorTemplate < Base
|
871
|
+
private
|
872
|
+
|
873
|
+
def input_variables
|
874
|
+
%w[typed_ruby_list error_messages]
|
875
|
+
end
|
876
|
+
|
877
|
+
def input_template_string
|
878
|
+
<<~TEMPLATE.strip
|
879
|
+
========Input========
|
880
|
+
{typed_ruby_list}
|
881
|
+
|
882
|
+
========Errors========
|
883
|
+
{error_messages}
|
884
|
+
|
885
|
+
|
886
|
+
========Output========
|
887
|
+
TEMPLATE
|
888
|
+
end
|
889
|
+
|
890
|
+
def format_args(args)
|
891
|
+
{
|
892
|
+
typed_ruby_list: args[:typed_ruby_list].join("\n"),
|
893
|
+
error_messages: args[:error_messages]
|
894
|
+
}
|
895
|
+
end
|
896
|
+
|
897
|
+
def transform_example_group(example_group)
|
898
|
+
{
|
899
|
+
typed_ruby_list: example_group.map(&:typed_ruby).join("\n"),
|
900
|
+
error_messages: example_group.error_messages,
|
901
|
+
refined_rbs_list: example_group.map(&:refined_rbs).join("\n")
|
902
|
+
}
|
903
|
+
end
|
904
|
+
end
|
905
|
+
end
|
906
|
+
end
|
907
|
+
```
|
908
|
+
|
909
|
+
```rbs:sig/rbs_goose/templates/fix_error_template.rbs
|
910
|
+
class RbsGoose::Templates::FixErrorTemplate < ::RbsGoose::Templates::Base
|
911
|
+
private def input_variables: () -> ::Array["typed_ruby_list" | "error_messages"]
|
912
|
+
private def input_template_string: () -> untyped
|
913
|
+
private def format_args: (untyped args) -> { typed_ruby_list: untyped, error_messages: untyped }
|
914
|
+
private def transform_example_group: (untyped example_group) -> { typed_ruby_list: untyped, error_messages: untyped, refined_rbs_list: untyped }
|
915
|
+
end
|
916
|
+
```
|
917
|
+
|
918
|
+
```ruby:lib/rbs_goose/templates/infer_template.rb
|
919
|
+
# frozen_string_literal: true
|
920
|
+
|
921
|
+
require 'langchain'
|
922
|
+
|
923
|
+
module RbsGoose
|
924
|
+
module Templates
|
925
|
+
class InferTemplate < Base
|
926
|
+
private
|
927
|
+
|
928
|
+
def input_variables
|
929
|
+
%w[typed_ruby_list]
|
930
|
+
end
|
931
|
+
|
932
|
+
def input_template_string
|
933
|
+
"========Input========\n{typed_ruby_list}\n\n========Output========"
|
934
|
+
end
|
935
|
+
|
936
|
+
def format_args(args)
|
937
|
+
{ typed_ruby_list: args[:typed_ruby_list].join("\n") }
|
938
|
+
end
|
939
|
+
|
940
|
+
def transform_example_group(example_group)
|
941
|
+
{
|
942
|
+
typed_ruby_list: example_group.map(&:typed_ruby).join("\n"),
|
943
|
+
refined_rbs_list: example_group.map(&:refined_rbs).join("\n")
|
944
|
+
}
|
945
|
+
end
|
946
|
+
end
|
947
|
+
end
|
948
|
+
end
|
949
|
+
```
|
950
|
+
|
951
|
+
```rbs:sig/rbs_goose/templates/infer_template.rbs
|
952
|
+
class RbsGoose::Templates::InferTemplate < ::RbsGoose::Templates::Base
|
953
|
+
private def input_variables: () -> ::Array["typed_ruby_list"]
|
954
|
+
private def input_template_string: () -> "========Input========\n{typed_ruby_list}\n\n========Output========"
|
955
|
+
private def format_args: (untyped args) -> { typed_ruby_list: untyped }
|
956
|
+
private def transform_example_group: (untyped example_group) -> { typed_ruby_list: untyped, refined_rbs_list: untyped }
|
957
|
+
end
|
958
|
+
```
|
959
|
+
|
960
|
+
```ruby:lib/rbs_goose/templates.rb
|
961
|
+
# frozen_string_literal: true
|
962
|
+
|
963
|
+
require_relative 'templates/base'
|
964
|
+
require_relative 'templates/fix_error_template'
|
965
|
+
require_relative 'templates/infer_template'
|
966
|
+
|
967
|
+
module RbsGoose
|
968
|
+
module Templates
|
969
|
+
end
|
970
|
+
end
|
971
|
+
```
|
972
|
+
|
973
|
+
```rbs:sig/rbs_goose/templates.rbs
|
974
|
+
module RbsGoose::Templates
|
975
|
+
end
|
976
|
+
```
|
977
|
+
|
978
|
+
```ruby:lib/rbs_goose/type_inferrer.rb
|
979
|
+
# frozen_string_literal: true
|
980
|
+
|
981
|
+
require 'steep'
|
982
|
+
require 'steep/cli'
|
983
|
+
require 'benchmark'
|
984
|
+
|
985
|
+
require_relative 'templates'
|
986
|
+
|
987
|
+
module RbsGoose
|
988
|
+
class TypeInferrer
|
989
|
+
def infer(target_group)
|
990
|
+
call_llm(RbsGoose.infer_template, { typed_ruby_list: target_group })
|
991
|
+
end
|
992
|
+
|
993
|
+
def fix_error(target_group)
|
994
|
+
error_messages = steep_check
|
995
|
+
call_llm(RbsGoose.fix_error_template, { typed_ruby_list: target_group, error_messages: })
|
996
|
+
end
|
997
|
+
|
998
|
+
private
|
999
|
+
|
1000
|
+
def call_llm(template, format_args)
|
1001
|
+
result =
|
1002
|
+
case RbsGoose.llm_mode.to_sym
|
1003
|
+
when :complete
|
1004
|
+
call_llm_complete(format_args, template)
|
1005
|
+
when :chat
|
1006
|
+
call_llm_chat(format_args, template)
|
1007
|
+
else
|
1008
|
+
raise "Invalid LLM mode: #{RbsGoose.llm_mode}"
|
1009
|
+
end
|
1010
|
+
template.parse_result(result)
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
def call_llm_complete(format_args, template)
|
1014
|
+
prompt = template.format(**format_args)
|
1015
|
+
llm_debug(prompt) do
|
1016
|
+
RbsGoose.llm.complete(prompt:)
|
1017
|
+
end.completion
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def call_llm_chat(format_args, template)
|
1021
|
+
messages = [
|
1022
|
+
{ role: 'user', content: template.format(**format_args) }
|
1023
|
+
]
|
1024
|
+
llm_debug(messages.map { "role: #{_1[:role]}\ncontent:\n#{_1[:content]}" }.join("\n")) do
|
1025
|
+
RbsGoose.llm.chat(messages:)
|
1026
|
+
end.chat_completion
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
def llm_debug(prompt)
|
1030
|
+
return yield if ENV['DEBUG'].nil?
|
1031
|
+
|
1032
|
+
puts "!!!!!!!! Prompt !!!!!!!!\n\n#{prompt}\n\n"
|
1033
|
+
result = nil
|
1034
|
+
sec = Benchmark.realtime do
|
1035
|
+
result = yield
|
1036
|
+
end
|
1037
|
+
puts "!!!!!!!! Stats !!!!!!!!\n\n spend: #{sec}[s]\n prompt_tokens: #{result.prompt_tokens}\n completion_tokens: #{result.completion_tokens}\n"
|
1038
|
+
puts "!!!!!!!! Result !!!!!!!!\n\n#{result.chat_completion}\n\n"
|
1039
|
+
result
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
def steep_check
|
1043
|
+
stdin, stdout, stderr = io_stubs
|
1044
|
+
disable_rainbow do
|
1045
|
+
steep_cli = Steep::CLI.new(stdout:, stdin:, stderr:, argv: [])
|
1046
|
+
steep_cli.process_check
|
1047
|
+
end
|
1048
|
+
stdout.string
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
def io_stubs
|
1052
|
+
[StringIO.new(''), StringIO.new(+'', 'w+'), StringIO.new(+'', 'w+')]
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
def disable_rainbow
|
1056
|
+
rainbow_enabled = Rainbow.enabled
|
1057
|
+
Rainbow.enabled = false
|
1058
|
+
yield
|
1059
|
+
Rainbow.enabled = rainbow_enabled
|
1060
|
+
end
|
1061
|
+
end
|
1062
|
+
end
|
1063
|
+
```
|
1064
|
+
|
1065
|
+
```rbs:sig/rbs_goose/type_inferrer.rbs
|
1066
|
+
class RbsGoose::TypeInferrer
|
1067
|
+
def infer: (untyped target_group) -> untyped
|
1068
|
+
def fix_error: (untyped target_group) -> untyped
|
1069
|
+
private def call_llm: (untyped template, untyped format_args) -> untyped
|
1070
|
+
private def call_llm_complete: (untyped format_args, untyped template) -> untyped
|
1071
|
+
private def call_llm_chat: (untyped format_args, untyped template) -> untyped
|
1072
|
+
private def llm_debug: (untyped prompt) { () -> untyped } -> untyped
|
1073
|
+
private def steep_check: () -> untyped
|
1074
|
+
private def io_stubs: () -> ::Array[untyped]
|
1075
|
+
private def disable_rainbow: () { () -> untyped } -> untyped
|
1076
|
+
end
|
1077
|
+
```
|
1078
|
+
|
1079
|
+
```ruby:lib/rbs_goose/version.rb
|
1080
|
+
# frozen_string_literal: true
|
1081
|
+
|
1082
|
+
module RbsGoose
|
1083
|
+
VERSION = '0.1.1'
|
1084
|
+
end
|
1085
|
+
```
|
1086
|
+
|
1087
|
+
```ruby:lib/rbs_goose.rb
|
1088
|
+
# frozen_string_literal: true
|
1089
|
+
|
1090
|
+
require_relative 'rbs_goose/configuration'
|
1091
|
+
require_relative 'rbs_goose/io'
|
1092
|
+
require_relative 'rbs_goose/orthoses'
|
1093
|
+
require_relative 'rbs_goose/type_inferrer'
|
1094
|
+
require_relative 'rbs_goose/version'
|
1095
|
+
|
1096
|
+
require 'forwardable'
|
1097
|
+
|
1098
|
+
module RbsGoose
|
1099
|
+
class Error < StandardError; end
|
1100
|
+
|
1101
|
+
class << self
|
1102
|
+
extend Forwardable
|
1103
|
+
|
1104
|
+
def configure(&)
|
1105
|
+
@configuration = Configuration.new(&)
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
def reset_configuration
|
1109
|
+
@configuration = nil
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
def run(code_dir: 'lib', sig_dir: 'sig', base_path: ::Dir.pwd)
|
1113
|
+
puts "Run RbsGoose.(Code Directory: #{code_dir}, Signature Directory: #{sig_dir})"
|
1114
|
+
target_group = RbsGoose::IO::TargetGroup.load_from(base_path, code_dir:, sig_dir:)
|
1115
|
+
RbsGoose::TypeInferrer.new.infer(target_group).each do |refined_rbs|
|
1116
|
+
puts "write refined rbs to #{refined_rbs.path}\n"
|
1117
|
+
refined_rbs.write
|
1118
|
+
puts "done.\n\n"
|
1119
|
+
end
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
def fix_error(code_dir: 'lib', sig_dir: 'sig', base_path: ::Dir.pwd)
|
1123
|
+
puts "Run FixError.(Code Directory: #{code_dir}, Signature Directory: #{sig_dir})"
|
1124
|
+
target_group = RbsGoose::IO::TargetGroup.load_from(base_path, code_dir:, sig_dir:)
|
1125
|
+
RbsGoose::TypeInferrer.new.fix_error(target_group).each do |refined_rbs|
|
1126
|
+
puts "write refined rbs to #{refined_rbs.path}\n"
|
1127
|
+
refined_rbs.write
|
1128
|
+
puts "done.\n\n"
|
1129
|
+
end
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
def infer_template
|
1133
|
+
configuration.infer_template.build_template
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
def fix_error_template
|
1137
|
+
configuration.fix_error_template.build_template
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
attr_reader :configuration
|
1141
|
+
|
1142
|
+
def_delegators :configuration, :infer_instruction, :infer_example_groups, :llm_mode
|
1143
|
+
def_delegator :configuration, :llm_client, :llm
|
1144
|
+
end
|
1145
|
+
end
|
1146
|
+
```
|
1147
|
+
|
1148
|
+
```rbs:sig/rbs_goose.rbs
|
1149
|
+
module RbsGoose
|
1150
|
+
VERSION: "0.1.1"
|
1151
|
+
|
1152
|
+
self.@configuration: untyped
|
1153
|
+
|
1154
|
+
extend Forwardable
|
1155
|
+
|
1156
|
+
def self.configure: () { () -> untyped } -> untyped
|
1157
|
+
|
1158
|
+
def self.reset_configuration: () -> untyped
|
1159
|
+
|
1160
|
+
def self.run: (?code_dir: ::String, ?sig_dir: ::String, ?base_path: untyped) -> untyped
|
1161
|
+
|
1162
|
+
def self.fix_error: (?code_dir: ::String, ?sig_dir: ::String, ?base_path: untyped) -> untyped
|
1163
|
+
|
1164
|
+
def self.infer_template: () -> untyped
|
1165
|
+
|
1166
|
+
def self.fix_error_template: () -> untyped
|
1167
|
+
|
1168
|
+
attr_reader self.configuration: untyped
|
1169
|
+
end
|
1170
|
+
```
|
1171
|
+
|
1172
|
+
|
1173
|
+
========Output========
|
1174
|
+
|
1175
|
+
!!!!!!!! Stats !!!!!!!!
|
1176
|
+
|
1177
|
+
spend: 102.75029300013557[s]
|
1178
|
+
prompt_tokens: 8071
|
1179
|
+
completion_tokens: 1519
|
1180
|
+
!!!!!!!! Result !!!!!!!!
|
1181
|
+
|
1182
|
+
```rbs:sig/rbs_goose/configuration.rbs
|
1183
|
+
class RbsGoose::Configuration
|
1184
|
+
@llm: RbsGoose::Configuration::LLMConfig
|
1185
|
+
extend Forwardable
|
1186
|
+
LLMConfig: RbsGoose::Configuration::LLMConfig
|
1187
|
+
TemplateConfig: RbsGoose::Configuration::TemplateConfig
|
1188
|
+
def initialize: () ?{ () -> void } -> void
|
1189
|
+
attr_accessor llm: RbsGoose::Configuration::LLMConfig
|
1190
|
+
attr_accessor infer_template: RbsGoose::Configuration::TemplateConfig
|
1191
|
+
attr_accessor fix_error_template: RbsGoose::Configuration::TemplateConfig
|
1192
|
+
def use_open_ai: (String open_ai_access_token, ?model_name: ::String, ?mode: ::Symbol, ?llm_options: ::Hash[Symbol, untyped], ?default_options: ::Hash[Symbol, untyped]) -> void
|
1193
|
+
def use_anthropic: (String access_token, ?model_name: ::String, ?mode: ::Symbol, ?default_options: ::Hash[Symbol, untyped]) -> void
|
1194
|
+
def use_ollama: (?url: ::String, ?model_name: ::String, ?mode: ::Symbol, ?default_options: ::Hash[Symbol, untyped]) -> void
|
1195
|
+
private def default_infer_template: () -> RbsGoose::Configuration::TemplateConfig
|
1196
|
+
private def default_fix_error_template: () -> RbsGoose::Configuration::TemplateConfig
|
1197
|
+
private def default_infer_instruction: () -> String
|
1198
|
+
private def default_infer_example_groups: () -> ::Array[RbsGoose::IO::ExampleGroup]
|
1199
|
+
private def default_fix_error_instruction: () -> String
|
1200
|
+
private def default_fix_error_example_groups: () -> ::Array[RbsGoose::IO::ExampleGroup]
|
1201
|
+
end
|
1202
|
+
```
|
1203
|
+
|
1204
|
+
```rbs:sig/rbs_goose/io/example.rbs
|
1205
|
+
class RbsGoose::IO::Example
|
1206
|
+
@typed_ruby: RbsGoose::IO::TypedRuby
|
1207
|
+
@refined_rbs: RbsGoose::IO::File
|
1208
|
+
def self.from_path: (ruby_path: String, rbs_path: String, refined_rbs_dir: String, base_path: String) -> RbsGoose::IO::Example
|
1209
|
+
def initialize: (typed_ruby: RbsGoose::IO::TypedRuby, refined_rbs: RbsGoose::IO::File) -> void
|
1210
|
+
def to_h: () -> { typed_ruby: RbsGoose::IO::TypedRuby, refined_rbs: RbsGoose::IO::File }
|
1211
|
+
attr_reader typed_ruby: RbsGoose::IO::TypedRuby
|
1212
|
+
attr_reader refined_rbs: RbsGoose::IO::File
|
1213
|
+
end
|
1214
|
+
```
|
1215
|
+
|
1216
|
+
```rbs:sig/rbs_goose/io/example_group.rbs
|
1217
|
+
class RbsGoose::IO::ExampleGroup < ::Array[RbsGoose::IO::Example]
|
1218
|
+
self.@default_examples: ::Hash[Symbol, RbsGoose::IO::ExampleGroup]
|
1219
|
+
attr_accessor error_messages: String
|
1220
|
+
def self.load_from: (String base_path, ?code_dir: ::String, ?sig_dir: ::String, ?refined_dir: ::String) -> RbsGoose::IO::ExampleGroup
|
1221
|
+
def self.default_examples: () -> ::Hash[Symbol, RbsGoose::IO::ExampleGroup]
|
1222
|
+
private def self.load_example: (String base_path, String code_dir, String path, String refined_dir, String sig_dir) -> RbsGoose::IO::Example
|
1223
|
+
private def self.to_rbs_path: (String path, String sig_dir) -> String
|
1224
|
+
def to_target_group: () -> RbsGoose::IO::TargetGroup
|
1225
|
+
def to_refined_rbs_list: () -> ::Array[RbsGoose::IO::File]
|
1226
|
+
end
|
1227
|
+
```
|
1228
|
+
|
1229
|
+
```rbs:sig/rbs_goose/io/file.rbs
|
1230
|
+
class RbsGoose::IO::File
|
1231
|
+
@path: String
|
1232
|
+
@base_path: String?
|
1233
|
+
@content: String
|
1234
|
+
@type: Symbol
|
1235
|
+
MARKDOWN_REGEXP: ::Regexp
|
1236
|
+
def self.from_markdown: (String markdown) -> RbsGoose::IO::File
|
1237
|
+
def initialize: (path: String, ?content: String?, ?base_path: String?) -> void
|
1238
|
+
def load_content: () -> void
|
1239
|
+
def absolute_path: () -> String
|
1240
|
+
def type: () -> Symbol
|
1241
|
+
def to_s: () -> String
|
1242
|
+
def content=: (String content) -> void
|
1243
|
+
def write: () -> void
|
1244
|
+
def ==: (RbsGoose::IO::File other) -> bool
|
1245
|
+
attr_reader path: String
|
1246
|
+
attr_reader content: String
|
1247
|
+
attr_reader base_path: String?
|
1248
|
+
end
|
1249
|
+
```
|
1250
|
+
|
1251
|
+
```rbs:sig/rbs_goose/io/target_group.rbs
|
1252
|
+
class RbsGoose::IO::TargetGroup < ::Array[RbsGoose::IO::TypedRuby]
|
1253
|
+
def self.load_from: (String base_path, ?code_dir: ::String, ?sig_dir: ::String) -> RbsGoose::IO::TargetGroup
|
1254
|
+
end
|
1255
|
+
```
|
1256
|
+
|
1257
|
+
```rbs:sig/rbs_goose/io/typed_ruby.rbs
|
1258
|
+
class RbsGoose::IO::TypedRuby
|
1259
|
+
@ruby: RbsGoose::IO::File
|
1260
|
+
@rbs: RbsGoose::IO::File?
|
1261
|
+
def self.from_path: (ruby_path: String, rbs_path: String, base_path: String) -> RbsGoose::IO::TypedRuby
|
1262
|
+
def initialize: (ruby: RbsGoose::IO::File, rbs: RbsGoose::IO::File?) -> void
|
1263
|
+
def to_s: () -> String
|
1264
|
+
attr_reader ruby: RbsGoose::IO::File
|
1265
|
+
attr_reader rbs: RbsGoose::IO::File?
|
1266
|
+
end
|
1267
|
+
```
|
1268
|
+
|
1269
|
+
```rbs:sig/rbs_goose/type_inferrer.rbs
|
1270
|
+
class RbsGoose::TypeInferrer
|
1271
|
+
def infer: (RbsGoose::IO::TargetGroup target_group) -> ::Array[RbsGoose::IO::File]
|
1272
|
+
def fix_error: (RbsGoose::IO::TargetGroup target_group) -> ::Array[RbsGoose::IO::File]
|
1273
|
+
private def call_llm: (RbsGoose::Templates::Base template, ::Hash[Symbol, untyped] format_args) -> ::Array[RbsGoose::IO::File]
|
1274
|
+
private def call_llm_complete: (::Hash[Symbol, untyped] format_args, RbsGoose::Templates::Base template) -> String
|
1275
|
+
private def call_llm_chat: (::Hash[Symbol, untyped] format_args, RbsGoose::Templates::Base template) -> String
|
1276
|
+
private def llm_debug: (String prompt) { () -> untyped } -> untyped
|
1277
|
+
private def steep_check: () -> String
|
1278
|
+
private def io_stubs: () -> ::Array[StringIO]
|
1279
|
+
private def disable_rainbow: () { () -> untyped } -> untyped
|
1280
|
+
end
|
1281
|
+
```
|
1282
|
+
|
1283
|
+
write refined rbs to sig/rbs_goose/configuration.rbs
|
1284
|
+
done.
|
1285
|
+
|
1286
|
+
write refined rbs to sig/rbs_goose/io/example.rbs
|
1287
|
+
done.
|
1288
|
+
|
1289
|
+
write refined rbs to sig/rbs_goose/io/example_group.rbs
|
1290
|
+
done.
|
1291
|
+
|
1292
|
+
write refined rbs to sig/rbs_goose/io/file.rbs
|
1293
|
+
done.
|
1294
|
+
|
1295
|
+
write refined rbs to sig/rbs_goose/io/target_group.rbs
|
1296
|
+
done.
|
1297
|
+
|
1298
|
+
write refined rbs to sig/rbs_goose/io/typed_ruby.rbs
|
1299
|
+
done.
|
1300
|
+
|
1301
|
+
write refined rbs to sig/rbs_goose/type_inferrer.rbs
|
1302
|
+
done.
|
1303
|
+
|
1304
|
+
I, [2024-05-08T16:26:25.992931 #67754] INFO -- : [RbsGoose::Orthoses::Infer]#call end
|