llmed 0.1.17 → 0.2.1
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/lib/llmed.rb +114 -16
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c063383b284467008387eb7e8dd154a2e22b75b344301b6acb800b2c5dac94c
|
4
|
+
data.tar.gz: 9d0b2491f94951b1308af3920f5e1ed539414205df67de9753c7f7c82b348381
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: efd313a26bebbf65970f222beaf86179255305328751fdca89700700238b34c13a9a1b56c9ba95776b6ead6b628886e8b1831cbce76c012fe17200ce5bf3b463
|
7
|
+
data.tar.gz: 615989ce41e43a865c8894f5523702595576307dc51696d012b42d7703b95cabd4c36a184ea30b2ce418615f5307292df6749b8fc7056766c9d604a2d1b99692
|
data/lib/llmed.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
require 'pp'
|
5
5
|
require 'csv'
|
6
|
+
require 'digest'
|
6
7
|
require 'json'
|
7
8
|
require 'pathname'
|
8
9
|
require 'fileutils'
|
@@ -24,8 +25,16 @@ class LLMed
|
|
24
25
|
@skip
|
25
26
|
end
|
26
27
|
|
28
|
+
def same_digest?(val)
|
29
|
+
digest == val
|
30
|
+
end
|
31
|
+
|
32
|
+
def digest
|
33
|
+
Digest::SHA256.hexdigest "#{@name}.#{@message}"
|
34
|
+
end
|
35
|
+
|
27
36
|
def message
|
28
|
-
"# #{@name}\n\n#{@message}"
|
37
|
+
"# Context: #{@name} Digest: #{digest}\n\n#{@message}"
|
29
38
|
end
|
30
39
|
|
31
40
|
def llm(message)
|
@@ -67,11 +76,19 @@ Always include the properly escaped comment: LLMED-COMPILED.
|
|
67
76
|
You must only modify the following source code:
|
68
77
|
{source_code}
|
69
78
|
|
70
|
-
|
79
|
+
Only generate source code of the context who digest belongs to {update_context_digests}.
|
80
|
+
|
81
|
+
Wrap with comment every code that belongs to the indicated context, example in ruby:
|
82
|
+
#<llmed-code context='context name' digest='....'>
|
83
|
+
...
|
84
|
+
#</llmed-code>
|
85
|
+
|
86
|
+
", input_variables: %w[language source_code update_context_digests])
|
71
87
|
end
|
72
88
|
|
73
|
-
def prompt(language:, source_code:)
|
74
|
-
@prompt.format(language: language, source_code: source_code
|
89
|
+
def prompt(language:, source_code:, update_context_digests: [])
|
90
|
+
@prompt.format(language: language, source_code: source_code,
|
91
|
+
update_context_digests: update_context_digests.join(','))
|
75
92
|
end
|
76
93
|
|
77
94
|
# Change the default prompt, input variables: language, source_code
|
@@ -164,6 +181,15 @@ You must only modify the following source code:
|
|
164
181
|
File.read(release_source_code)
|
165
182
|
end
|
166
183
|
|
184
|
+
def release_contexts(_output_dir, release_dir)
|
185
|
+
return {} unless @release
|
186
|
+
|
187
|
+
release_source_code = Pathname.new(release_dir) + "#{@output_file}.r#{@release}#{@language}.cache"
|
188
|
+
return {} unless File.exist?(release_source_code)
|
189
|
+
|
190
|
+
File.read(release_source_code).scan(/context='(.+)' digest='(.+)'/).to_h
|
191
|
+
end
|
192
|
+
|
167
193
|
def output_file(output_dir, mode = 'w', &block)
|
168
194
|
if @output_file.respond_to? :write
|
169
195
|
yield @output_file
|
@@ -177,6 +203,73 @@ You must only modify the following source code:
|
|
177
203
|
end
|
178
204
|
end
|
179
205
|
|
206
|
+
def patch_or_create(output_dir, release_dir, output)
|
207
|
+
release_source_code_path = Pathname.new(release_dir) + "#{@output_file}.r#{@release}#{@language}.cache"
|
208
|
+
|
209
|
+
if @release && File.exist?(release_source_code_path)
|
210
|
+
release_source_code = File.read(release_source_code_path)
|
211
|
+
output_contexts = output.scan(%r{<llmed-code context='(.+?)' digest='(.+?)'>(.+?)</llmed-code>}im)
|
212
|
+
output_contexts.each do |match|
|
213
|
+
name, digest, new_code = match
|
214
|
+
@logger.info("APPLICATION #{@name} PATCHING CONTEXT #{name}")
|
215
|
+
release_source_code = release_source_code.sub(%r{(.*?)(<llmed-code context='#{name}' digest='.*?'>)(.+?)(</llmed-code>)(.*?)}m) do
|
216
|
+
"#{::Regexp.last_match(1)}<llmed-code context='#{name}' digest='#{digest}'>#{new_code}#{::Regexp.last_match(4)}#{::Regexp.last_match(5)}"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
output_file(output_dir) do |file|
|
221
|
+
file.write(release_source_code)
|
222
|
+
end
|
223
|
+
else
|
224
|
+
output_file(output_dir) do |file|
|
225
|
+
file.write(output)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def digests_of_context_to_update(output_dir, release_dir)
|
231
|
+
update_context_digest = []
|
232
|
+
release_contexts = release_contexts(output_dir, release_dir)
|
233
|
+
|
234
|
+
unless release_contexts.empty?
|
235
|
+
# rebuild context from top to down
|
236
|
+
# we are expecting:
|
237
|
+
# - top the most stable concepts
|
238
|
+
# - buttom the most inestable concepts
|
239
|
+
update_rest = false
|
240
|
+
@contexts.each do |ctx|
|
241
|
+
release_context_digest = release_contexts[ctx.name]
|
242
|
+
|
243
|
+
# maybe the context is not connected to the source code
|
244
|
+
next if release_context_digest.nil?
|
245
|
+
|
246
|
+
if update_rest
|
247
|
+
update_context_digest << release_context_digest
|
248
|
+
next
|
249
|
+
end
|
250
|
+
next if ctx.same_digest?(release_context_digest)
|
251
|
+
|
252
|
+
update_rest = true
|
253
|
+
update_context_digest << release_context_digest
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
update_context_digest
|
258
|
+
end
|
259
|
+
|
260
|
+
def rebuild?(output_dir, release_dir)
|
261
|
+
return true unless @release
|
262
|
+
|
263
|
+
update_context_digest = digests_of_context_to_update(output_dir, release_dir)
|
264
|
+
release_contexts = release_contexts(output_dir, release_dir)
|
265
|
+
update_context_digest.each do |digest|
|
266
|
+
context_by_digest = release_contexts.invert
|
267
|
+
@logger.info("APPLICATION #{@name} REBUILDING CONTEXT #{context_by_digest[digest]}")
|
268
|
+
end
|
269
|
+
|
270
|
+
!update_context_digest.empty?
|
271
|
+
end
|
272
|
+
|
180
273
|
def write_statistics(release_dir, response)
|
181
274
|
return unless @output_file.is_a?(String)
|
182
275
|
|
@@ -240,33 +333,38 @@ You must only modify the following source code:
|
|
240
333
|
@logger.info("APPLICATION #{app.name} COMPILING")
|
241
334
|
|
242
335
|
llm = @configuration.llm
|
336
|
+
|
337
|
+
app.evaluate
|
338
|
+
|
243
339
|
system_content = @configuration.prompt(language: app.language,
|
244
340
|
source_code: app.source_code(
|
245
341
|
output_dir, release_dir
|
246
|
-
)
|
342
|
+
),
|
343
|
+
update_context_digests: app.digests_of_context_to_update(output_dir,
|
344
|
+
release_dir))
|
247
345
|
messages = [LLMed::LLM::Message::System.new(system_content)]
|
248
|
-
app.evaluate
|
249
346
|
app.contexts.each do |ctx|
|
250
347
|
next if ctx.skip?
|
251
348
|
|
252
349
|
messages << LLMed::LLM::Message::User.new(ctx.message)
|
253
350
|
end
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
351
|
+
if app.rebuild?(output_dir, release_dir)
|
352
|
+
llm_response = llm.chat(messages: messages)
|
353
|
+
@logger.info("APPLICATION #{app.name} TOTAL TOKENS #{llm_response.total_tokens}")
|
354
|
+
write_output(app, output_dir, release_dir, llm_response.source_code)
|
355
|
+
write_statistics(app, release_dir, llm_response)
|
356
|
+
app.notify("COMPILE DONE #{llm_response.duration_seconds}")
|
357
|
+
else
|
358
|
+
@logger.info("APPLICATION #{app.name} NOT CHANGES DETECTED")
|
359
|
+
end
|
260
360
|
end
|
261
361
|
|
262
362
|
def write_statistics(app, release_dir, response)
|
263
363
|
app.write_statistics(release_dir, response)
|
264
364
|
end
|
265
365
|
|
266
|
-
def write_output(app, output_dir, output)
|
267
|
-
app.
|
268
|
-
file.write(output)
|
269
|
-
end
|
366
|
+
def write_output(app, output_dir, release_dir, output)
|
367
|
+
app.patch_or_create(output_dir, release_dir, output)
|
270
368
|
end
|
271
369
|
end
|
272
370
|
|