glim_ai 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 (73) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +25 -0
  3. data/Gemfile.lock +49 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +125 -0
  6. data/Rakefile +31 -0
  7. data/examples/autocode/autocode.rb +166 -0
  8. data/examples/autocode/solargraph_test.rb +59 -0
  9. data/examples/autocode/templates/changed_files_now_evaluate_output.erb +29 -0
  10. data/examples/autocode/templates/task.erb +16 -0
  11. data/examples/calc/calc.rb +50 -0
  12. data/examples/code_competition/code_competition.rb +78 -0
  13. data/examples/code_competition/output/python_claude-2.rb +33 -0
  14. data/examples/code_competition/output/python_claude-instant-1.rb +18 -0
  15. data/examples/code_competition/output/python_gpt-3.5-turbo-16k.rb +69 -0
  16. data/examples/code_competition/output/python_gpt-3.5-turbo.rb +43 -0
  17. data/examples/code_competition/output/python_gpt-4.rb +34 -0
  18. data/examples/code_competition/output/ruby_claude-2.rb +22 -0
  19. data/examples/code_competition/output/ruby_claude-instant-1.rb +20 -0
  20. data/examples/code_competition/output/ruby_gpt-3.5-turbo-16k.rb +27 -0
  21. data/examples/code_competition/output/ruby_gpt-3.5-turbo.rb +30 -0
  22. data/examples/code_competition/output/ruby_gpt-4.rb +31 -0
  23. data/examples/code_competition/output/ruby_human.rb +41 -0
  24. data/examples/code_competition/templates/analyze_code.erb +33 -0
  25. data/examples/code_competition/templates/write_code.erb +26 -0
  26. data/examples/glim_demo/ask_all.rb +35 -0
  27. data/examples/glim_demo/templates/rate_all.erb +24 -0
  28. data/examples/improve_prompt/improve_prompt.rb +62 -0
  29. data/examples/improve_prompt/templates/stashed/prompt_attempt_explicit_steps.erb +15 -0
  30. data/examples/improve_prompt/templates/stashed/prompt_attempt_explicit_steps_user_message.erb +15 -0
  31. data/examples/improve_prompt/templates/stashed/prompt_attempt_initial.erb +8 -0
  32. data/examples/improve_prompt/templates/stashed/prompt_attempt_nothing.erb +19 -0
  33. data/examples/improve_prompt/templates/try_code_first.erb +13 -0
  34. data/examples/improve_prompt/templates/try_code_first_system.erb +22 -0
  35. data/examples/old/econ/discounting.rb +27 -0
  36. data/examples/old/econ/templates/discounting.erb +10 -0
  37. data/examples/old/generate_glim_code/generate_glim_code.rb +34 -0
  38. data/examples/old/generate_glim_code/templates/generate_glim_code.erb +17 -0
  39. data/examples/old/generate_glim_code/templates/improve_code.erb +27 -0
  40. data/examples/old/glim_dev_tools/ask_code_question.rb +38 -0
  41. data/examples/old/glim_dev_tools/templates/ask_code_question.erb +12 -0
  42. data/examples/old/glim_dev_tools/templates/write_globals_test.erb +28 -0
  43. data/examples/old/glim_dev_tools/write_globals_test.rb +20 -0
  44. data/examples/old/linguistics/nine.rb +0 -0
  45. data/examples/old/rewrite_code/input/hello.py +1 -0
  46. data/examples/old/rewrite_code/input/subdir/hello.py +1 -0
  47. data/examples/old/rewrite_code/input/world.py +1 -0
  48. data/examples/old/rewrite_code/rewrite_code.rb +18 -0
  49. data/examples/old/rewrite_code/templates/rewrite_code.erb +32 -0
  50. data/examples/window_check/data.rb +1260 -0
  51. data/examples/window_check/fruits.rb +118 -0
  52. data/examples/window_check/tools.rb +56 -0
  53. data/examples/window_check/window_check.rb +214 -0
  54. data/glim_generated_tests/make_special_code_with_fixed_length_test.rb +44 -0
  55. data/glim_generated_tests/old-20230831120513-make_special_code_with_fixed_length_test.rb +1 -0
  56. data/glim_generated_tests/old-20230831121222-make_special_code_with_fixed_length_test.rb +55 -0
  57. data/glim_generated_tests/old-20230831124501-make_special_code_with_fixed_length_test.rb +33 -0
  58. data/glim_generated_tests/test/make_special_code_with_fixed_length_test.rb +58 -0
  59. data/lib/anthropic_request_details.rb +37 -0
  60. data/lib/anthropic_response.rb +101 -0
  61. data/lib/chat_request_details.rb +140 -0
  62. data/lib/chat_response.rb +303 -0
  63. data/lib/glim_ai/version.rb +5 -0
  64. data/lib/glim_ai.rb +8 -0
  65. data/lib/glim_ai_callable.rb +151 -0
  66. data/lib/glim_context.rb +62 -0
  67. data/lib/glim_helpers.rb +54 -0
  68. data/lib/glim_request.rb +266 -0
  69. data/lib/glim_response.rb +155 -0
  70. data/lib/globals.rb +255 -0
  71. data/lib/html_templates/chat_request.erb +86 -0
  72. data/sample.env +9 -0
  73. metadata +131 -0
data/lib/globals.rb ADDED
@@ -0,0 +1,255 @@
1
+ require 'thread'
2
+ require 'fileutils'
3
+
4
+ require 'active_support/core_ext/hash/indifferent_access'
5
+ require_relative 'glim_ai/version'
6
+
7
+ require 'dotenv'
8
+ require 'must_be'
9
+ Dotenv.load
10
+
11
+ require_relative 'glim_context'
12
+ require_relative 'glim_response'
13
+ require_relative 'glim_request'
14
+ require_relative 'glim_ai_callable'
15
+
16
+ require_relative 'chat_request_details'
17
+ require_relative 'chat_response'
18
+ require_relative 'anthropic_request_details'
19
+ require_relative 'anthropic_response'
20
+
21
+ require 'bundler'
22
+ PROJECT_HOME = Bundler.root.to_s
23
+
24
+ CACHE_PATH = ENV['LLM_CACHE_DIRECTORY'] || 'glim_cache'
25
+
26
+ $putt_timestamp = Time.now
27
+
28
+ class RateLimitExceededError < RuntimeError; end
29
+ class LLMError < StandardError; end
30
+
31
+ class APILimiter
32
+ def initialize(max_concurrent_requests:)
33
+ @max_concurrent_requests = max_concurrent_requests.must_be_a Integer
34
+ @mutex = Mutex.new
35
+ @condition = ConditionVariable.new
36
+ @counter = 0
37
+ end
38
+
39
+ attr_reader :max_concurrent_requests
40
+
41
+ def with_limit
42
+ @mutex.synchronize do
43
+ @condition.wait(@mutex) while @counter >= @max_concurrent_requests
44
+ @counter += 1
45
+ end
46
+ begin
47
+ yield # Execute the provided block
48
+ rescue RateLimitExceededError => e
49
+ retries += 1
50
+ if retries <= @max_retries
51
+ putt :rpc, "Rate limit exceeded. Retrying in #{2 ** retries} seconds."
52
+ sleep(2 ** retries + rand) # Exponential backoff with some random jitter
53
+ retry
54
+ else
55
+ raise "Max retries reached. Original error: #{e.message}"
56
+ end
57
+ ensure
58
+ @mutex.synchronize do
59
+ @counter -= 1
60
+ @condition.signal
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def extract_and_save_files(input_string, base_path=nil)
67
+ files = {}
68
+ info_text = ""
69
+ parts = input_string.split(/(<file pathname="[^"]+">|<\/file>)/)
70
+
71
+ in_file = false
72
+ parts.each_with_index do |part, idx|
73
+ if part =~ /<file pathname="([^"]+)">/
74
+ pathname = $1.strip
75
+ content = parts[idx + 1]
76
+ files[pathname] = content.strip
77
+ if base_path
78
+ save_file(pathname, content, base_path)
79
+ end
80
+ in_file = true
81
+ info_text += part # append the opening tag to info_text
82
+ elsif part == "</file>"
83
+ in_file = false # reset when encountering the closing tag
84
+ else
85
+ info_text += part unless in_file
86
+ end
87
+ end
88
+ return info_text, files
89
+ end
90
+
91
+ # Another way to do it, if the above breaks
92
+ #
93
+ # require 'strscan'
94
+
95
+ # text = <<~XML
96
+ # <file pathname="f1">hello</file>
97
+ # <file pathname="f2">world</file>
98
+ # XML
99
+
100
+ # files = []
101
+
102
+ # scanner = StringScanner.new(text)
103
+
104
+ # while !scanner.eos?
105
+ # if scanner.scan_until(/<file /)
106
+ # path = scanner.scan(/"(.*?)"/)
107
+ # scanner.scan_until(/>/)
108
+ # content = scanner.scan_until(/<\/file>/)
109
+ # files << {pathname: path, content: content}
110
+ # end
111
+ # end
112
+
113
+ # puts files
114
+
115
+ def save_file(filename, content, base_path)
116
+ putt :file_helper, "Saving file #{filename} to #{base_path}, #{content.length} chars"
117
+ relative_path = filename
118
+ path = File.join(base_path, relative_path) # Append relative_path under base_path
119
+ directory = File.dirname(path)
120
+ begin
121
+ if path && (File.exist?(path) || Dir.exist?(path))
122
+ timestamp = Time.now.strftime('%Y%m%d%H%M%S')
123
+ #FileUtils.mv(path, "old-#{timestamp}-#{File.basename(path)}")
124
+ FileUtils.mv(path, File.join(directory, "old-#{timestamp}-#{File.basename(path)}"))
125
+ end
126
+ FileUtils.mkdir_p(directory) unless Dir.exist?(directory)
127
+ File.open(path, 'w') { |file| file.write(content) } unless content.to_s.empty?
128
+ rescue StandardError => e
129
+ puts "An error occurred while processing file at #{path}: #{e.message} - #{e.backtrace.join("\n")}"
130
+ end
131
+ end
132
+
133
+
134
+ def levenshtein_distance(str1, str2)
135
+ raise ArgumentError, "str1 must be a String" unless str1.is_a?(String)
136
+ raise ArgumentError, "str2 must be a String" unless str2.is_a?(String)
137
+ matrix = Array.new(str1.length + 1) { Array.new(str2.length + 1, 0) }
138
+ edits = ""
139
+
140
+ (1..str1.length).each { |i| matrix[i][0] = i }
141
+ (1..str2.length).each { |j| matrix[0][j] = j }
142
+
143
+ (1..str1.length).each do |i|
144
+ (1..str2.length).each do |j|
145
+ cost = str1[i - 1] == str2[j - 1] ? 0 : 1
146
+ matrix[i][j] = [
147
+ matrix[i - 1][j] + 1,
148
+ matrix[i][j - 1] + 1,
149
+ matrix[i - 1][j - 1] + cost
150
+ ].min
151
+ end
152
+ end
153
+
154
+ i, j = str1.length, str2.length
155
+ while i > 0 && j > 0
156
+ min_val = [matrix[i-1][j], matrix[i][j-1], matrix[i-1][j-1]].min
157
+ if min_val == matrix[i-1][j-1]
158
+ edits << (str1[i - 1] == str2[j - 1] ? "M" : "S")
159
+ i -= 1
160
+ j -= 1
161
+ elsif min_val == matrix[i-1][j]
162
+ edits << "D"
163
+ i -= 1
164
+ else
165
+ edits << "I"
166
+ j -= 1
167
+ end
168
+ end
169
+
170
+ while i > 0
171
+ edits << "D"
172
+ i -= 1
173
+ end
174
+
175
+ while j > 0
176
+ edits << "I"
177
+ j -= 1
178
+ end
179
+
180
+ explanation = "#{str1}\n#{str2}\n#{edits.reverse}\n}"
181
+ putt :levenshtein_distance, explanation
182
+ [matrix[str1.length][str2.length], edits.reverse] #, explanation]
183
+ end
184
+
185
+
186
+ def putt(topic, s, options = [])
187
+ return unless [:warning, :include_files].include?(topic)
188
+ #return unless [:log, :rpc, :cache].include?(topic)
189
+ t = Time.now - $putt_timestamp
190
+ line = "#{t.round(3)}: " + "#{s}"
191
+ puts(line)
192
+ options = []#[:trace]
193
+ if options.include?(:trace)
194
+ for i in 1..([caller.length, 20].min)
195
+ puts "\t"+caller[i]
196
+ end
197
+ end
198
+ end
199
+
200
+ require 'digest'
201
+
202
+ def deep_copy_with_mods(object, string_cutoff=80, array_cutoff=10)
203
+ case object
204
+ when Hash
205
+ object.each_with_object({}) do |(key, value), result|
206
+ result[key] = deep_copy_with_mods(value, string_cutoff, array_cutoff)
207
+ end
208
+ when Array
209
+ if object.length > array_cutoff
210
+ object[0...array_cutoff].map do |value|
211
+ deep_copy_with_mods(value, string_cutoff, array_cutoff)
212
+ end << ["... (#{object.length - array_cutoff} more)"]
213
+ else
214
+ object.dup
215
+ end
216
+ when String
217
+ if object.length > string_cutoff
218
+ truncated_string = object[0...string_cutoff]
219
+ digest = Digest::SHA1.hexdigest(object)[0..7]
220
+ "#{truncated_string}..#{digest} #{object.length}b}"
221
+ else
222
+ object
223
+ end
224
+ else
225
+ object
226
+ end
227
+ end
228
+
229
+
230
+ def extract_between_markers(str, start_marker, end_marker)
231
+ str[/#{Regexp.escape(start_marker)}(.*?)#{Regexp.escape(end_marker)}/m, 1]
232
+ end
233
+
234
+ def extract_with_markers(str, start_marker, end_marker)
235
+ str[/#{Regexp.escape(start_marker)}.*?#{Regexp.escape(end_marker)}/m]
236
+ end
237
+
238
+
239
+ def extract_json(json_string)
240
+ str = json_string.dup
241
+ begin
242
+ return JSON.parse(str)
243
+ rescue JSON::ParserError => e
244
+ putt :extract_json, "JSON parse error 1: #{e}"
245
+ end
246
+ str = extract_with_markers(str, "{","}")
247
+ begin
248
+ return JSON.parse(str)
249
+ rescue JSON::ParserError => e
250
+ putt :extract_json, "JSON parse error 2: #{e}"
251
+ end
252
+ str.gsub!(/,\s*}/, "}")
253
+ return JSON.parse(str)
254
+ end
255
+
@@ -0,0 +1,86 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Request Details</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ padding: 20px;
11
+ }
12
+ pre {
13
+ background-color: #f5f5f5;
14
+ padding: 15px;
15
+ border-radius: 5px;
16
+ }
17
+ h2, h3 {
18
+ border-bottom: 1px solid #ddd;
19
+ padding-bottom: 10px;
20
+ }
21
+ </style>
22
+ </head>
23
+ <body>
24
+
25
+ <h2>Request Information</h2>
26
+
27
+ <% if request_hash[:max_tokens] || request_hash[:model] %>
28
+ <h3>General Settings</h3>
29
+ <% if request_hash[:max_tokens] %>
30
+ <p><strong>Max Tokens:</strong> <%= request_hash[:max_tokens] %></p>
31
+ <% end %>
32
+ <% if request_hash[:model] %>
33
+ <p><strong>Model:</strong> <%= request_hash[:model] %></p>
34
+ <% end %>
35
+ <% end %>
36
+
37
+ <% if request_hash[:messages] && !request_hash[:messages].empty? %>
38
+ <h3>Messages</h3>
39
+ <% request_hash[:messages].each_with_index do |message, index| %>
40
+ <p><strong>Message <%= index + 1 %>:</strong></p>
41
+ <pre>
42
+ Role: <%= message[:role] %>
43
+ Content: <%= message[:content] %>
44
+ </pre>
45
+ <% end %>
46
+ <% end %>
47
+
48
+ <% if request_hash[:functions] && !request_hash[:functions].empty? %>
49
+ <h3>Functions</h3>
50
+ <% request_hash[:functions].each_with_index do |function, index| %>
51
+ <p><strong>Function <%= index + 1 %>:</strong></p>
52
+ <pre>
53
+ <% if function[:name] %>
54
+ Name: <%= function[:name] %>
55
+ <% end %>
56
+ <% if function[:description] %>
57
+ Description: <%= function[:description] %>
58
+ <% end %>
59
+ <% if function[:parameters] %>
60
+ Parameters:
61
+ <% if function[:parameters][:type] %>
62
+ Type: <%= function[:parameters][:type] %>
63
+ <% end %>
64
+ <% if function[:parameters][:properties] %>
65
+ Properties:
66
+ <% function[:parameters][:properties].each do |key, property| %>
67
+ <%= key.capitalize %>:
68
+ <% if property[:type] %>
69
+ Type: <%= property[:type] %>
70
+ <% end %>
71
+ <% if property[:description] %>
72
+ Description: <%= property[:description] %>
73
+ <% end %>
74
+ <% end %>
75
+ <% end %>
76
+ <% if function[:parameters][:required] %>
77
+ Required:
78
+ <%= function[:parameters][:required].join(", ") %>
79
+ <% end %>
80
+ <% end %>
81
+ </pre>
82
+ <% end %>
83
+ <% end %>
84
+
85
+ </body>
86
+ </html>
data/sample.env ADDED
@@ -0,0 +1,9 @@
1
+ # copy this file to ".env" and fill in your API keys
2
+
3
+ ANTHROPIC_API_KEY=
4
+ OPENAI_API_KEY=
5
+ ANYSCALE_API_KEY=
6
+
7
+ GLIM_LOG_DIRECTORY="glim_logs"
8
+ GLIM_CACHE_DIRECTORY="glim_cache"
9
+ GLIM_GENERATED_CODE_DIRECTORY="glim_generated_code"
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: glim_ai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Ulrich Gall
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-09-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dotenv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: An easy way to access multiple large language models.
28
+ email:
29
+ - uhgall@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - Gemfile.lock
36
+ - LICENSE.txt
37
+ - README.md
38
+ - Rakefile
39
+ - examples/autocode/autocode.rb
40
+ - examples/autocode/solargraph_test.rb
41
+ - examples/autocode/templates/changed_files_now_evaluate_output.erb
42
+ - examples/autocode/templates/task.erb
43
+ - examples/calc/calc.rb
44
+ - examples/code_competition/code_competition.rb
45
+ - examples/code_competition/output/python_claude-2.rb
46
+ - examples/code_competition/output/python_claude-instant-1.rb
47
+ - examples/code_competition/output/python_gpt-3.5-turbo-16k.rb
48
+ - examples/code_competition/output/python_gpt-3.5-turbo.rb
49
+ - examples/code_competition/output/python_gpt-4.rb
50
+ - examples/code_competition/output/ruby_claude-2.rb
51
+ - examples/code_competition/output/ruby_claude-instant-1.rb
52
+ - examples/code_competition/output/ruby_gpt-3.5-turbo-16k.rb
53
+ - examples/code_competition/output/ruby_gpt-3.5-turbo.rb
54
+ - examples/code_competition/output/ruby_gpt-4.rb
55
+ - examples/code_competition/output/ruby_human.rb
56
+ - examples/code_competition/templates/analyze_code.erb
57
+ - examples/code_competition/templates/write_code.erb
58
+ - examples/glim_demo/ask_all.rb
59
+ - examples/glim_demo/templates/rate_all.erb
60
+ - examples/improve_prompt/improve_prompt.rb
61
+ - examples/improve_prompt/templates/stashed/prompt_attempt_explicit_steps.erb
62
+ - examples/improve_prompt/templates/stashed/prompt_attempt_explicit_steps_user_message.erb
63
+ - examples/improve_prompt/templates/stashed/prompt_attempt_initial.erb
64
+ - examples/improve_prompt/templates/stashed/prompt_attempt_nothing.erb
65
+ - examples/improve_prompt/templates/try_code_first.erb
66
+ - examples/improve_prompt/templates/try_code_first_system.erb
67
+ - examples/old/econ/discounting.rb
68
+ - examples/old/econ/templates/discounting.erb
69
+ - examples/old/generate_glim_code/generate_glim_code.rb
70
+ - examples/old/generate_glim_code/templates/generate_glim_code.erb
71
+ - examples/old/generate_glim_code/templates/improve_code.erb
72
+ - examples/old/glim_dev_tools/ask_code_question.rb
73
+ - examples/old/glim_dev_tools/templates/ask_code_question.erb
74
+ - examples/old/glim_dev_tools/templates/write_globals_test.erb
75
+ - examples/old/glim_dev_tools/write_globals_test.rb
76
+ - examples/old/linguistics/nine.rb
77
+ - examples/old/rewrite_code/input/hello.py
78
+ - examples/old/rewrite_code/input/subdir/hello.py
79
+ - examples/old/rewrite_code/input/world.py
80
+ - examples/old/rewrite_code/rewrite_code.rb
81
+ - examples/old/rewrite_code/templates/rewrite_code.erb
82
+ - examples/window_check/data.rb
83
+ - examples/window_check/fruits.rb
84
+ - examples/window_check/tools.rb
85
+ - examples/window_check/window_check.rb
86
+ - glim_generated_tests/make_special_code_with_fixed_length_test.rb
87
+ - glim_generated_tests/old-20230831120513-make_special_code_with_fixed_length_test.rb
88
+ - glim_generated_tests/old-20230831121222-make_special_code_with_fixed_length_test.rb
89
+ - glim_generated_tests/old-20230831124501-make_special_code_with_fixed_length_test.rb
90
+ - glim_generated_tests/test/make_special_code_with_fixed_length_test.rb
91
+ - lib/anthropic_request_details.rb
92
+ - lib/anthropic_response.rb
93
+ - lib/chat_request_details.rb
94
+ - lib/chat_response.rb
95
+ - lib/glim_ai.rb
96
+ - lib/glim_ai/version.rb
97
+ - lib/glim_ai_callable.rb
98
+ - lib/glim_context.rb
99
+ - lib/glim_helpers.rb
100
+ - lib/glim_request.rb
101
+ - lib/glim_response.rb
102
+ - lib/globals.rb
103
+ - lib/html_templates/chat_request.erb
104
+ - sample.env
105
+ homepage: https://github.com/uhgall/glim
106
+ licenses:
107
+ - MIT
108
+ metadata:
109
+ homepage_uri: https://github.com/uhgall/glim
110
+ source_code_uri: https://github.com/uhgall/glim
111
+ changelog_uri: https://github.com/uhgall/glim
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 2.6.0
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubygems_version: 3.4.1
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Convenient access to multiple large language models.
131
+ test_files: []