llmed 0.1.17 → 0.2.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/lib/llmed.rb +109 -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: cc7a4dd77be914e3acf61278c46190acfc65ceec923b6c1041a29b6fd488b623
|
4
|
+
data.tar.gz: 6ef9e5985f6635126689d0793be72511c72f598579bfd73955c9d1fcbfc2fd14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfab97f3a34b02fad0bc1e7416031c2e2dbc8c63b4d53e225eae40addbeed057ec90e7e2dfcc9ed02a923382ea29fe96e640c16af35baa73b6ffcb3dbe6e2caf
|
7
|
+
data.tar.gz: e64afcb33ce327c3099a103afb09f65591c2b343f4d98a6b818a6e93d371053e44162933f8f709e47effae01cd856c865b4060391d20b9d8ed1696ed137ee509
|
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,68 @@ 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
|
+
unless release_contexts.empty?
|
234
|
+
# rebuild context from top to down
|
235
|
+
# we are expecting:
|
236
|
+
# - top the most stable concepts
|
237
|
+
# - buttom the most inestable concepts
|
238
|
+
update_rest = false
|
239
|
+
@contexts.each do |ctx|
|
240
|
+
release_context_digest = release_contexts[ctx.name]
|
241
|
+
if update_rest
|
242
|
+
update_context_digest << release_context_digest
|
243
|
+
next
|
244
|
+
end
|
245
|
+
next if ctx.same_digest?(release_context_digest)
|
246
|
+
|
247
|
+
update_rest = true
|
248
|
+
update_context_digest << release_context_digest
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
update_context_digest
|
253
|
+
end
|
254
|
+
|
255
|
+
def rebuild?(output_dir, release_dir)
|
256
|
+
return true unless @release
|
257
|
+
|
258
|
+
update_context_digest = digests_of_context_to_update(output_dir, release_dir)
|
259
|
+
release_contexts = release_contexts(output_dir, release_dir)
|
260
|
+
update_context_digest.each do |digest|
|
261
|
+
context_by_digest = release_contexts.invert
|
262
|
+
@logger.info("APPLICATION #{@name} REBUILDING CONTEXT #{context_by_digest[digest]}")
|
263
|
+
end
|
264
|
+
|
265
|
+
!update_context_digest.empty?
|
266
|
+
end
|
267
|
+
|
180
268
|
def write_statistics(release_dir, response)
|
181
269
|
return unless @output_file.is_a?(String)
|
182
270
|
|
@@ -240,33 +328,38 @@ You must only modify the following source code:
|
|
240
328
|
@logger.info("APPLICATION #{app.name} COMPILING")
|
241
329
|
|
242
330
|
llm = @configuration.llm
|
331
|
+
|
332
|
+
app.evaluate
|
333
|
+
|
243
334
|
system_content = @configuration.prompt(language: app.language,
|
244
335
|
source_code: app.source_code(
|
245
336
|
output_dir, release_dir
|
246
|
-
)
|
337
|
+
),
|
338
|
+
update_context_digests: app.digests_of_context_to_update(output_dir,
|
339
|
+
release_dir))
|
247
340
|
messages = [LLMed::LLM::Message::System.new(system_content)]
|
248
|
-
app.evaluate
|
249
341
|
app.contexts.each do |ctx|
|
250
342
|
next if ctx.skip?
|
251
343
|
|
252
344
|
messages << LLMed::LLM::Message::User.new(ctx.message)
|
253
345
|
end
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
346
|
+
if app.rebuild?(output_dir, release_dir)
|
347
|
+
llm_response = llm.chat(messages: messages)
|
348
|
+
@logger.info("APPLICATION #{app.name} TOTAL TOKENS #{llm_response.total_tokens}")
|
349
|
+
write_output(app, output_dir, release_dir, llm_response.source_code)
|
350
|
+
write_statistics(app, release_dir, llm_response)
|
351
|
+
app.notify("COMPILE DONE #{llm_response.duration_seconds}")
|
352
|
+
else
|
353
|
+
@logger.info("APPLICATION #{app.name} NOT CHANGES DETECTED")
|
354
|
+
end
|
260
355
|
end
|
261
356
|
|
262
357
|
def write_statistics(app, release_dir, response)
|
263
358
|
app.write_statistics(release_dir, response)
|
264
359
|
end
|
265
360
|
|
266
|
-
def write_output(app, output_dir, output)
|
267
|
-
app.
|
268
|
-
file.write(output)
|
269
|
-
end
|
361
|
+
def write_output(app, output_dir, release_dir, output)
|
362
|
+
app.patch_or_create(output_dir, release_dir, output)
|
270
363
|
end
|
271
364
|
end
|
272
365
|
|