llmed 0.4.2 → 0.4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dcb6503e5c4ed7cf826c335f2ea016e6f99187b9b2ea9352267881bb5634ccbe
4
- data.tar.gz: 45d1a3538aca0b6e4ed118eec14bfd08fe61445e5229558264d434d0739b34d9
3
+ metadata.gz: 5733bac9f8a6d8c5465f487bda233c8997491d838c778b45194e3c15f9c14734
4
+ data.tar.gz: e606c3e377bc6d0bf75eed59dd6456773c02650e98118a0135814808195e2fb8
5
5
  SHA512:
6
- metadata.gz: 63d03492ef70ede943c6e7b236158962527a4bce1e9ac4015d28c897a7326aa8ae0e016a8a96e52264cc6c2db286ed66c767311f77bdf344baaedf4f32a156c7
7
- data.tar.gz: 8763511711a43d45e0682afb8cbb3352af6bea95baf9c765de259b2ca276fe40faad5647d2ef93a2e0ea747712ab6ec4c254df91553df5659feccefbe4acf67b
6
+ metadata.gz: 3fc77a1b7b7ca6ba6c8bcb528e1ef702277588195c47e538cfa6a8432aef4ae7cb876b10537605866bfa7ea0a61ceacf1308cd52d3b5f0f2a7a4f51ef5b552ed
7
+ data.tar.gz: 94f66750f04e2c97d8e78125ac920c52037fd6bc5e5759cbd2e349402be599619fb5cb2dc92af0c519a0cc557333be022925abb556f09995460a0b0d936386c8
data/exe/llmed.literate CHANGED
@@ -9,12 +9,22 @@ output_dir = './llmed-out'
9
9
  release_dir = output_dir
10
10
  language = 'ruby'
11
11
  provider = :like_openai
12
- provider_api_key = ENV.fetch('LLMED_PROVIDER_API_KEY', nil)
12
+ provider_api_key = ENV.fetch('LLMED_PROVIDER_API_KEY', '')
13
13
  provider_model = ENV.fetch('LLMED_PROVIDER_MODEL', 'Qwen/Qwen2.5-Coder-32B-Instruct')
14
14
  provider_uri_base = ENV.fetch('LLMED_PROVIDER_URI_BASE', 'https://api.together.xyz/v1')
15
15
  output_file = 'a.out.ollmed'
16
16
 
17
17
  template = <<~TMP
18
+ #% Minimal example
19
+ #% llm_provider : like_openai | openai | anthropic
20
+ #!environment llm_provider like_openai
21
+ #% llm_provider_model : depends provider
22
+ #!environment llm_provider_model Qwen/Qwen2.5-Coder-32B-Instruct
23
+ #% llm_provider_model : depends provider
24
+ #!environment llm_provider_uri_base https://api.together.xyz/v1
25
+ #!environment language ruby
26
+ #!environment output_file demo.rb
27
+
18
28
  # Main
19
29
 
20
30
  Show to user 'hi world!'.
@@ -26,7 +36,26 @@ OptionParser.new do |parser|
26
36
  puts parser
27
37
  puts "\n# Website\nhttps://github.com/bit4bit/llm-labs/tree/main/llmed"
28
38
  puts "\n# Examples\nhttps://github.com/bit4bit/llm-labs/tree/main/llmed/examples"
29
- puts "Environment vars:\nLLMED_PROVIDER_API_KEY\nLLMED_PROVIDER_MODEL=#{provider_model}\nLLMED_PROVIDER_URI_BASE=#{provider_uri_base}"
39
+ puts "\n# Current Environment\nLLMED_PROVIDER_API_KEY\nLLMED_PROVIDER_MODEL=#{provider_model}\nLLMED_PROVIDER_URI_BASE=#{provider_uri_base}"
40
+ puts <<~DOC
41
+
42
+ # A minimal example
43
+ <!--
44
+ #% Minimal example
45
+ #% llm_provider : like_openai | openai | anthropic
46
+ #!environment llm_provider like_openai
47
+ #% llm_provider_model : depends provider
48
+ #!environment llm_provider_model Qwen/Qwen2.5-Coder-32B-Instruct
49
+ #% llm_provider_model : depends provider
50
+ #!environment llm_provider_uri_base https://api.together.xyz/v1
51
+ #!environment language ruby
52
+ #!environment output_file fscalls.rb
53
+ -->
54
+
55
+ # Main
56
+
57
+ Show to user 'hi world!'.
58
+ DOC
30
59
  exit
31
60
  end
32
61
 
@@ -47,7 +76,7 @@ OptionParser.new do |parser|
47
76
  release_dir = path
48
77
  end
49
78
 
50
- parser.on('-l', '--language LANGUAGE', String, 'Programming Language: ruby, python') do |lang|
79
+ parser.on('-l', '--language LANGUAGE', String, 'Programming Language: ruby, python (default: ruby)') do |lang|
51
80
  language = lang
52
81
  end
53
82
 
@@ -63,6 +92,34 @@ end
63
92
 
64
93
  llmed = LLMed.new(logger: logger, output_dir: output_dir, release_dir: release_dir)
65
94
  llmed.set_language language
66
- llmed.set_llm provider: provider, api_key: provider_api_key, model: provider_model, options: {uri_base: provider_uri_base}
67
- LLMed::LiterateProgramming.execute(llmed, 'literate_programming', source_code, output_file: output_file)
95
+ LLMed::LiterateProgramming.execute(source_code, output_file: output_file) do |contexts, application_args, environment|
96
+ environment.each do |k, v|
97
+ case k
98
+ when 'llm_provider'
99
+ provider = v
100
+ when 'llm_provider_model'
101
+ provider_model = v
102
+ when 'llm_provider_uri_base'
103
+ provider_uri_base = v
104
+ when 'language'
105
+ language = v
106
+ end
107
+ end
108
+
109
+ if provider_api_key.strip.empty?
110
+ logger.error "LLMED_PROVIDER_API_KEY is not set. Please set the environment variable and try again."
111
+ exit 1
112
+ end
113
+
114
+ logger.info "Compiling for language: #{language}"
115
+ logger.info "LLM Provider: #{provider} Model: #{provider_model} URI Base: #{provider_uri_base}"
116
+
117
+ llmed.set_llm provider: provider, api_key: provider_api_key, model: provider_model, options: {uri_base: provider_uri_base}
118
+ llmed.application('literate_programming', **application_args) do
119
+ contexts.each do |lcontext|
120
+ context(lcontext[:title]) { lcontext[:content] }
121
+ end
122
+ end
123
+ end
124
+
68
125
  llmed.compile()
@@ -2,9 +2,10 @@ require 'open-uri'
2
2
 
3
3
  class LLMed
4
4
  class LiterateProgramming
5
- def self.execute(llmed, application_name, code, **application_args)
5
+ def self.execute(code, **application_args)
6
6
  md = LLMed::LiterateProgramming::Markdown.new()
7
7
  contexts = []
8
+ environment = {}
8
9
 
9
10
  md.parse(code).each do |item|
10
11
  context = {}
@@ -17,12 +18,14 @@ class LLMed
17
18
  value = item_content[:value].strip
18
19
  if [:language, :release, :output_file, :output_dir, :release_dir].include?(name) && !value.empty?
19
20
  application_args[name] = value
21
+ else
22
+ environment[name] = value
20
23
  end
21
24
  end
22
25
  end
23
26
  next
24
27
  end
25
-
28
+
26
29
  case item[:type]
27
30
  when :context
28
31
  context[:title] = item[:title]
@@ -42,11 +45,7 @@ class LLMed
42
45
  end
43
46
  end
44
47
 
45
- llmed.application(application_name, **application_args) do
46
- contexts.each do |lcontext|
47
- context(lcontext[:title]) { lcontext[:content] }
48
- end
49
- end
48
+ yield contexts, application_args, environment
50
49
  end
51
50
  end
52
51
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: llmed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jovany Leandro G.C
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-07-01 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: langchainrb
@@ -94,34 +93,23 @@ files:
94
93
  - README.md
95
94
  - exe/llmed
96
95
  - exe/llmed.literate
97
- - lib/literate_programming.llmed~
98
96
  - lib/llm.rb
99
- - lib/llm.rb~
100
97
  - lib/llmed.rb
101
- - lib/llmed.rb~
102
98
  - lib/llmed/application.rb
103
- - lib/llmed/application.rb~
104
99
  - lib/llmed/configuration.rb
105
- - lib/llmed/configuration.rb~
106
100
  - lib/llmed/context.rb
107
- - lib/llmed/context.rb~
108
101
  - lib/llmed/deployment.rb
109
- - lib/llmed/deployment.rb~
110
102
  - lib/llmed/literate_programming.rb
111
- - lib/llmed/literate_programming.rb~
112
103
  - lib/llmed/literate_programming/markdown.rb
113
- - lib/llmed/literate_programming/markdown.rb.r1ruby.cache
114
104
  - lib/llmed/literate_programming/markdown.rb.release
115
105
  - lib/llmed/literate_programming/markdown.rb.statistics
116
106
  - lib/llmed/release.rb
117
- - lib/llmed/release.rb~
118
107
  homepage: https://github.com/bit4bit/llmed
119
108
  licenses:
120
109
  - GPL-3.0
121
110
  metadata:
122
111
  source_code_uri: https://github.com/bit4bit/llmed
123
112
  allowed_push_host: https://rubygems.org
124
- post_install_message:
125
113
  rdoc_options: []
126
114
  require_paths:
127
115
  - lib
@@ -136,8 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
124
  - !ruby/object:Gem::Version
137
125
  version: 1.3.7
138
126
  requirements: []
139
- rubygems_version: 3.3.15
140
- signing_key:
127
+ rubygems_version: 3.6.7
141
128
  specification_version: 4
142
129
  summary: LLM Execution Development
143
130
  test_files: []
@@ -1,39 +0,0 @@
1
- set_llm provider: :like_openai, api_key: ENV['TOGETHERAI_API_KEY'], model: 'Qwen/Qwen2.5-Coder-32B-Instruct', options: {uri_base: 'https://api.together.xyz/v1'}
2
-
3
- application "Literate Programming Markdown", release: nil, language: :ruby, output_file: "markdown.rb", output_dir: "literate_programming" do
4
- context "Library LLmed::LiterateProgramming::Markdown" do
5
- <<-LLM
6
- Exports a function call `parse(input: String)`.
7
-
8
- ### Example of usage
9
-
10
- ```ruby
11
- md = LLmed::LiteratePrograming::Markdown.new()
12
- md.parse("
13
- # Context A
14
- Contenido
15
- [link](http://link)
16
- ## SubContexto A
17
- SubContenido
18
-
19
- # Contexto 3
20
- Contenido 3
21
-
22
- ") == [{type: :context,
23
- title: "Context A",
24
- content: [
25
- {type: :string, content: "Contenido\n"},
26
- {type: :link, content: "link", reference: "http://link"},
27
- {type: :string, content: "##SubContexto A\nSubContenido\n\n"}
28
- ]},
29
- {type: :context,
30
- title: "Contexto 3",
31
- content: [
32
- {type: :string, content: "Contenido 3\n\n"}
33
- ]}]
34
- ```
35
-
36
-
37
- LLM
38
- end
39
- end
data/lib/llm.rb~ DELETED
File without changes
@@ -1,182 +0,0 @@
1
- # Copyright 2025 Jovany Leandro G.C <bit4bit@riseup.net>
2
- # frozen_string_literal: true
3
-
4
- class LLMed
5
- class Application
6
- attr_reader :contexts, :name, :language
7
-
8
- def initialize(name:, language:, output_file:, block:, logger:, release:, release_dir:, output_dir:)
9
- raise 'required language' if language.nil?
10
-
11
- @name = name
12
- @output_file = output_file
13
- @language = language
14
- @block = block
15
- @contexts = []
16
- @logger = logger
17
- @release = release
18
- @release_dir = release_dir
19
- @output_dir = output_dir
20
- end
21
-
22
- # Example:
23
- # application { context "demo" { "content" } }
24
- def context(name, **opts, &block)
25
- opts[:release_dir] = @release_dir
26
- ctx = Context.new(name: name, options: opts)
27
- output = ctx.instance_eval(&block)
28
- ctx.llm(output) unless ctx.message?
29
-
30
- @contexts << ctx
31
- end
32
-
33
- def evaluate
34
- instance_eval(&@block)
35
- end
36
-
37
- def source_code
38
- return unless @output_file.is_a?(String)
39
- return unless @release
40
-
41
- output_file = Pathname.new(@output_dir) + @output_file
42
- if @release && !File.exist?(release_source_code)
43
- FileUtils.cp(output_file, release_source_code)
44
- FileUtils.cp(output_file, release_main_source_code)
45
- @logger.info("APPLICATION #{@name} RELEASE FILE #{release_source_code}")
46
- end
47
- @logger.info("APPLICATION #{@name} INPUT RELEASE FILE #{release_main_source_code}")
48
- File.read(release_source_code)
49
- end
50
-
51
- def output_file(output_dir, mode = 'w', &block)
52
- if @output_file.respond_to? :write
53
- yield @output_file
54
- else
55
- path = Pathname.new(output_dir) + @output_file
56
- FileUtils.mkdir_p(File.dirname(path))
57
-
58
- @logger.info("APPLICATION #{@name} OUTPUT FILE #{path}")
59
-
60
- File.open(path, mode, &block)
61
- end
62
- end
63
-
64
- def patch_or_create(output)
65
- release_source_code_path = Pathname.new(@release_dir) + "#{@output_file}.r#{@release}#{@language}.cache"
66
- output_content = output
67
- if @release && File.exist?(release_source_code_path)
68
- release_source_code = File.read(release_source_code_path)
69
- output_contexts = output.scan(%r{<llmed-code context='(.+?)' digest='(.+?)'>(.+?)</llmed-code>}im)
70
-
71
- output_contexts.each do |match|
72
- name, digest, new_code = match
73
- new_digest = digest
74
- @contexts.each do |ctx|
75
- if ctx.name == name
76
- new_digest = ctx.digest
77
- break
78
- end
79
- end
80
-
81
- @logger.info("APPLICATION #{@name} PATCHING CONTEXT #{name} \n\tFROM #{digest}\n\tTO DIGEST #{new_digest}")
82
- release_source_code = release_source_code.sub(%r{(.*?)(<llmed-code context='#{name}' digest='.*?'>)(.+?)(</llmed-code>)(.*?)}m) do
83
- "#{::Regexp.last_match(1)}<llmed-code context='#{name}' digest='#{new_digest}'>#{new_code}#{::Regexp.last_match(4)}#{::Regexp.last_match(5)}"
84
- end
85
- end
86
-
87
- output_content = release_source_code
88
- end
89
-
90
- output_file(@output_dir) do |file|
91
- file.write(output_content)
92
- end
93
- end
94
-
95
- def system_prompt(configuration)
96
- configuration.prompt(language: language,
97
- source_code: source_code,
98
- update_context_digests: digests_of_context_to_update)
99
- end
100
-
101
- def rebuild?
102
- return true unless @release
103
-
104
- !digests_of_context_to_update.tap do |digests|
105
- digests.each do |digest|
106
- context_by_digest = release_contexts.invert
107
- @logger.info("APPLICATION #{@name} REBUILDING CONTEXT #{context_by_digest[digest]}")
108
- end
109
- end.empty?
110
- end
111
-
112
- def write_statistics(response)
113
- return unless @output_file.is_a?(String)
114
-
115
- statistics_file = Pathname.new(@release_dir) + "#{@output_file}.statistics"
116
-
117
- File.open(statistics_file, 'a') do |file|
118
- stat = {
119
- inserted_at: Time.now.to_i,
120
- name: @name,
121
- provider: response.provider,
122
- model: response.model,
123
- release: @release,
124
- total_tokens: response.total_tokens,
125
- duration_seconds: response.duration_seconds
126
- }
127
- file.puts stat.to_json
128
- end
129
- @logger.info("APPLICATION #{@name} WROTE STATISTICS FILE #{statistics_file}")
130
- end
131
-
132
- def notify(message)
133
- Notify.notify("APPLICATION #{@name}", message)
134
- end
135
-
136
- private
137
-
138
- def digests_of_context_to_update
139
- update_context_digest = []
140
-
141
- unless release_contexts.empty?
142
- # rebuild context from top to down
143
- # we are expecting:
144
- # - top the most stable concepts
145
- # - buttom the most inestable concepts
146
- update_rest = false
147
- @contexts.each do |ctx|
148
- release_context_digest = release_contexts[ctx.name]
149
- # maybe the context is not connected to the source code
150
- next if release_context_digest.nil?
151
-
152
- if update_rest
153
- update_context_digest << release_context_digest
154
- next
155
- end
156
- next if ctx.same_digest?(release_context_digest)
157
-
158
- update_rest = true
159
- update_context_digest << release_context_digest
160
- end
161
- end
162
-
163
- update_context_digest
164
- end
165
-
166
- def release_source_code
167
- Pathname.new(@release_dir) + "#{@output_file}.r#{@release}#{@language}.cache"
168
- end
169
-
170
- def release_main_source_code
171
- Pathname.new(@release_dir) + "#{@output_file}.release"
172
- end
173
-
174
- def release_contexts
175
- return {} unless @release
176
-
177
- return {} unless File.exist?(release_source_code)
178
-
179
- File.read(release_source_code).scan(/context='(.+)' digest='(.+)'/).to_h
180
- end
181
- end
182
- end
@@ -1,76 +0,0 @@
1
- class LLMed
2
- module LLM
3
- class Configuration
4
- def initialize
5
- @prompt = LLMed::LLM::Template.build(template: "
6
- You are a software developer with knowledge only of the programming language {language}. Follow the SOLID principles strictly, you must use only imperative and functional programming, and design highly isolated components.
7
- Your response must contain only the generated source code, with no additional text.
8
- All source code must be written in a single file, and you must ensure it runs correctly on the first attempt.
9
- Always include the properly escaped comment: LLMED-COMPILED.
10
-
11
- You must only modify the following source code:
12
- {source_code}
13
-
14
- Only generate source code of the context who digest belongs to {update_context_digests}.
15
-
16
- Wrap with comment every code that belongs to the indicated context, example in ruby:
17
- #<llmed-code context='context name' digest='....'>
18
- ...
19
- #</llmed-code>
20
-
21
- ", input_variables: %w[language source_code update_context_digests])
22
- end
23
-
24
- def prompt(language:, source_code:, update_context_digests: [])
25
- @prompt.format(language: language, source_code: source_code,
26
- update_context_digests: update_context_digests.join(','))
27
- end
28
-
29
- # Change the default prompt, input variables: language, source_code
30
- # Example:
31
- # set_prompt "my new prompt"
32
- def set_prompt(*arg, input_variables: %w[language source_code], **args)
33
- input_variables = {} if args[:file]
34
- prompt = File.read(args[:file]) if args[:file]
35
- prompt ||= arg.first
36
- @prompt = LLMed::LLM::Template.build(template: prompt, input_variables: input_variables)
37
- end
38
-
39
- # Set default language used for all applications.
40
- # Example:
41
- # set_langugage :ruby
42
- def set_language(language)
43
- @language = language
44
- end
45
-
46
- def set_llm(provider:, api_key:, model:)
47
- @provider = provider
48
- @provider_api_key = api_key
49
- @provider_model = model
50
- end
51
-
52
- def language(main)
53
- lang = main || @language
54
- raise 'Please assign a language to the application or general with the function set_languag' if lang.nil?
55
-
56
- lang
57
- end
58
-
59
- def llm
60
- case @provider
61
- when :openai
62
- LLMed::LLM::OpenAI.new(
63
- api_key: @provider_api_key,
64
- default_options: { temperature: 0.7, chat_model: @provider_model }
65
- )
66
- when :test
67
- LLMed::LLM::Test.new
68
- when nil
69
- raise 'Please set the provider with `set_llm(provider, api_key, model)`'
70
- else
71
- raise "not implemented provider #{@provider}"
72
- end
73
- end
74
- end
75
- end
76
- end
@@ -1,62 +0,0 @@
1
- # Copyright 2025 Jovany Leandro G.C <bit4bit@riseup.net>
2
- class LLMed
3
- class Context
4
- attr_reader :name
5
-
6
- def initialize(name:, options: {})
7
- @name = name
8
- @skip = options[:skip] || false
9
- @release_dir = options[:release_dir]
10
- end
11
-
12
- def skip?
13
- @skip
14
- end
15
-
16
- def same_digest?(val)
17
- digest == val
18
- end
19
-
20
- def digest
21
- Digest::SHA256.hexdigest "#{@name}.#{@message}"
22
- end
23
-
24
- def message
25
- "# Context: #{@name} Digest: #{digest}\n\n#{@message}"
26
- end
27
-
28
- def llm(message)
29
- @message = message
30
- end
31
-
32
- def message?
33
- !(@message.nil? || @message.empty?)
34
- end
35
-
36
- # Example:
37
- # context("files") { sh "ls /etc" }
38
- def sh(cmd)
39
- `#{cmd}`
40
- end
41
-
42
- # Example:
43
- # context("application") { from_file("application.cllmed") }
44
- def from_file(path)
45
- File.read(path)
46
- end
47
-
48
- # Example:
49
- # context("source") { from_source_code("sourcepathtoinclude") }
50
- def from_source_code(path)
51
- code = File.read(path)
52
- " Given the following source code: #{code}\n\n\n"
53
- end
54
-
55
- # Example:
56
- # context("source") { from_release("file in release dir") }
57
- def from_release(path)
58
- code = File.read(Pathname.new(@release_dir) + path)
59
- " Given the following source code: #{code}\n\n\n"
60
- end
61
- end
62
- end
@@ -1,16 +0,0 @@
1
- # Copyright 2025 Jovany Leandro G.C <bit4bit@riseup.net>
2
- # frozen_string_literal: true
3
-
4
- class LLMed
5
- class Deployment
6
- def initialize(name:, output_dir:, block:)
7
- @name = name
8
- @output_dir = output_dir
9
- @block = block
10
- end
11
-
12
- def execute()
13
- self.instance_eval(@block)
14
- end
15
- end
16
- end
@@ -1,24 +0,0 @@
1
- #<llmed-code context='Library LLMed::LiterateProgramming::Markdown' digest='18aa5391a8a334f24a80542620a277bb9c085762cb88b7dbcafa26a532a48027' after=''>
2
- class LLMed::LiterateProgramming::Markdown
3
- def parse(input)
4
- contexts = []
5
- current_context = { type: :context, title: "_default", content: [] }
6
-
7
- input.each_line do |line|
8
- if line.strip =~ /^# (.+)$/
9
- contexts << current_context unless current_context[:content].empty?
10
- current_context = { type: :context, title: Regexp.last_match(1), content: [] }
11
- elsif line.strip =~ /^\[(.+)\]\((.+)\)$/
12
- current_context[:content] << { type: :link, content: Regexp.last_match(1), reference: Regexp.last_match(2) }
13
- elsif line.strip =~ /^#% (.+)$/
14
- current_context[:content] << { type: :comment, content: Regexp.last_match(1) + "\n" }
15
- else
16
- current_context[:content] << { type: :string, content: line }
17
- end
18
- end
19
-
20
- contexts << current_context unless current_context[:content].empty?
21
- contexts
22
- end
23
- end
24
- #</llmed-code>
@@ -1 +0,0 @@
1
- require_relative 'literater_programming/markdown'
@@ -1,31 +0,0 @@
1
- # Copyright 2025 Jovany Leandro G.C <bit4bit@riseup.net>
2
- # frozen_string_literal: true
3
-
4
- class LLMed
5
- class Release
6
-
7
- ContextCode = Struct.new(:name, :digest, :code)
8
-
9
- def read(path)
10
- @origin = File.read(path)
11
- @changes = []
12
- end
13
-
14
- def contexts
15
- contexts = []
16
- @origin.scan(%r{<llmed-code context='(.+?)' digest='(.+?)'>(.+?)</llmed-code>}im).each do |match|
17
- name, digest, code = match
18
- contexts << ContextCode.new(name, digest, code)
19
- end
20
- end
21
-
22
- def merge(release)
23
- content = ""
24
- release.contexts.each do |ctx|
25
- content = content.sub(%r{(.*?)(<llmed-code context='#{ctx.name}' digest='.*?'>)(.+?)(</llmed-code>)(.*?)}m) do
26
- "#{::Regexp.last_match(1)}<llmed-code context='#{ctx.name}' digest='#{ctx.digest}'>#{ctx.code}#{::Regexp.last_match(4)}#{::Regexp.last_match(5)}"
27
- end
28
- end
29
- end
30
- end
31
- end
data/lib/llmed.rb~ DELETED
@@ -1,190 +0,0 @@
1
- require 'pp'
2
- require 'langchain'
3
- require 'pathname'
4
- require 'fileutils'
5
- require 'forwardable'
6
-
7
- Langchain.logger.level = Logger::ERROR
8
-
9
- class LLMed
10
- extend Forwardable
11
-
12
- class Context
13
- attr_reader :message, :name
14
-
15
- def initialize(name:, options: {})
16
- @name = name
17
- @skip = options[:skip] || false
18
- end
19
-
20
- def skip?
21
- @skip
22
- end
23
-
24
- def llm(message)
25
- @message = message
26
- end
27
-
28
- def message?
29
- not (@message.nil? || @message.empty?)
30
- end
31
-
32
-
33
- def from_file(path)
34
- File.read(path)
35
- end
36
-
37
- def from_source_code(path)
38
- code = File.read(path)
39
- "Dado el codigo fuente: #{code}\n\n\n"
40
- end
41
- end
42
-
43
- class Configuration
44
- def initialize
45
- @prompt = Langchain::Prompt::PromptTemplate.new(template: "
46
- Eres desarrollador de software y solo conoces del lenguage de programacion {language}.
47
- La respuesta no debe contener texto adicional al codigo fuente generado.
48
- Todo el codigo fuente se genera en un unico archivo.
49
- Siempre adicionas el comentario de codigo correctamente escapado LLMED-COMPILED.
50
-
51
- ", input_variables: ["language"])
52
- end
53
-
54
- def prompt(language:)
55
- @prompt.format(language: language)
56
- end
57
-
58
- def set_prompt(prompt)
59
- @prompt = Langchain::Prompt::PromptTemplate.new(template: prompt, input_variables: ["language"])
60
- end
61
-
62
- def set_language(language)
63
- @language = language
64
- end
65
-
66
- def set_llm(provider:, api_key:, model:)
67
- @provider = provider
68
- @provider_api_key = api_key
69
- @provider_model = model
70
- end
71
-
72
- def language(main)
73
- lang = main || @language
74
- raise "Please assign a language to the application or general with the function set_languag" if lang.nil?
75
- lang
76
- end
77
-
78
- def llm()
79
- case @provider
80
- when :openai
81
- Langchain::LLM::OpenAI.new(
82
- api_key: @provider_api_key,
83
- default_options: { temperature: 0.7, chat_model: @provider_model}
84
- )
85
- when nil
86
- raise "Please set the provider with `set_llm(provider, api_key, model)`"
87
- else
88
- raise "not implemented provider #{@provider}"
89
- end
90
- end
91
- end
92
-
93
- class Application
94
- attr_reader :contexts, :name, :language
95
-
96
- def initialize(name:, language:, output_file:, block:, logger:)
97
- raise "required language" if language.nil?
98
-
99
- @name = name
100
- @output_file = output_file
101
- @language = language
102
- @block = block
103
- @contexts = []
104
- @logger = logger
105
- end
106
-
107
- def context(name, **opts, &block)
108
- ctx = Context.new(name: name, options: opts)
109
- output = ctx.instance_eval(&block)
110
- unless ctx.message?
111
- ctx.llm(output)
112
- end
113
-
114
- @contexts << ctx
115
- end
116
-
117
- def evaluate
118
- self.instance_eval(&@block)
119
- end
120
-
121
- def output_file(output_dir)
122
- if @output_file.respond_to? :write
123
- yield @output_file
124
- else
125
- path = Pathname.new(output_dir) + @output_file
126
- FileUtils.mkdir_p(File.dirname(path))
127
-
128
- @logger.info("APPLICATION #{@name} OUTPUT FILE #{@output_file}")
129
-
130
- File.open(path, 'w') do |file|
131
- yield file
132
- end
133
- end
134
- end
135
- end
136
-
137
- def initialize(logger:)
138
- @logger = logger
139
- @applications = []
140
- @configuration = Configuration.new()
141
- end
142
-
143
- def eval_source(code)
144
- self.instance_eval(code)
145
- end
146
-
147
- # changes default language
148
- def_delegator :@configuration, :set_language, :set_language
149
- # changes default llm
150
- def_delegator :@configuration, :set_llm, :set_llm
151
- # changes default prompt
152
- def_delegator :@configuration, :set_prompt, :set_prompt
153
-
154
- def application(name, language: nil, output_file:, &block)
155
- @app = Application.new(name: name, language: @configuration.language(language), output_file: output_file, block: block, logger: @logger)
156
- @applications << @app
157
- end
158
-
159
- def compile(output_dir:)
160
- @applications.each do |app|
161
- llm = @configuration.llm()
162
-
163
- messages = [
164
- {role: "system", content: @configuration.prompt(language: app.language)},
165
- ]
166
- app.evaluate
167
- app.contexts.each do |ctx|
168
- next if ctx.skip?
169
- messages << {role: "user", content: ctx.message}
170
- end
171
-
172
- llm_response = llm.chat(messages: messages)
173
- response = llm_response.chat_completion
174
- @logger.info("APPLICATION #{app.name} TOTAL TOKENS #{llm_response.total_tokens}")
175
- write_output(app, output_dir, source_code(response))
176
- end
177
- end
178
-
179
- private
180
- def source_code(content)
181
- # TODO: by provider?
182
- content.gsub('```', '').sub(/^(ruby|python(\d*)|elixir|c(pp)?)/, '')
183
- end
184
-
185
- def write_output(app, output_dir, output)
186
- app.output_file(output_dir) do |file|
187
- file.write(output)
188
- end
189
- end
190
- end