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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/llmed.rb +114 -16
  3. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66603fc5fdc0f63f5c066ca085a80dee2a259e4a1d30f315f52e018f5bfec0de
4
- data.tar.gz: 244a8d4938c85cd395a541eb18491e007b8e116d4dbf997d4f71a05050d5b5a9
3
+ metadata.gz: 2c063383b284467008387eb7e8dd154a2e22b75b344301b6acb800b2c5dac94c
4
+ data.tar.gz: 9d0b2491f94951b1308af3920f5e1ed539414205df67de9753c7f7c82b348381
5
5
  SHA512:
6
- metadata.gz: 2f9c7c6e0f9949b7a2876051912a645e786bd65eaba09e23bf837f1e52ae1cb8c0d645194028ac5f8b65c5e0a0f8d5d6197d6ac1155753dfa38f887ac57a6fc9
7
- data.tar.gz: 308e33e710860f4ed3b63b007875aa4c339edac3bbb9f8ab6c13452cb48919408e610282991f53fefb38c27a009fb3e9acf0e4035944f40867a9f833825b2e49
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
- ", input_variables: %w[language source_code])
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
- llm_response = llm.chat(messages: messages)
256
- @logger.info("APPLICATION #{app.name} TOTAL TOKENS #{llm_response.total_tokens}")
257
- write_output(app, output_dir, llm_response.source_code)
258
- write_statistics(app, release_dir, llm_response)
259
- app.notify("COMPILE DONE #{llm_response.duration_seconds}")
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.output_file(output_dir) do |file|
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
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: llmed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.17
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jovany Leandro G.C