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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/llmed.rb +109 -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: cc7a4dd77be914e3acf61278c46190acfc65ceec923b6c1041a29b6fd488b623
4
+ data.tar.gz: 6ef9e5985f6635126689d0793be72511c72f598579bfd73955c9d1fcbfc2fd14
5
5
  SHA512:
6
- metadata.gz: 2f9c7c6e0f9949b7a2876051912a645e786bd65eaba09e23bf837f1e52ae1cb8c0d645194028ac5f8b65c5e0a0f8d5d6197d6ac1155753dfa38f887ac57a6fc9
7
- data.tar.gz: 308e33e710860f4ed3b63b007875aa4c339edac3bbb9f8ab6c13452cb48919408e610282991f53fefb38c27a009fb3e9acf0e4035944f40867a9f833825b2e49
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
- ", 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,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
- 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}")
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.output_file(output_dir) do |file|
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
 
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.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jovany Leandro G.C