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
@@ -0,0 +1,118 @@
1
+ require_relative '../../lib/globals'
2
+ require_relative 'data'
3
+
4
+ glim = GlimContext.new(log_name: "ask_all")
5
+
6
+ # Given item_count items, get a list of indices into data
7
+ # that are equidistant and include 0 and item_count-1
8
+ def select_data_point_indices(data_point_count, item_count)
9
+ delta = item_count / (data_point_count - 1.0)
10
+ indices = []
11
+ for i in 0..(data_point_count - 2)
12
+ indices << (i * delta).round
13
+ end
14
+ indices << (item_count - 1)
15
+ end
16
+
17
+ def extract_between_markers(str, start_marker, end_marker)
18
+ str[/#{Regexp.escape(start_marker)}(.*?)#{Regexp.escape(end_marker)}/m, 1]
19
+ end
20
+
21
+ #models = ["claude-instant-1", "gpt-3.5-turbo"]
22
+ #models = ["claude-instant-1"] # , "gpt-3.5-turbo"]
23
+ # models = ["gpt-4"]
24
+
25
+ models = ["gpt-3.5-turbo"]
26
+
27
+ models = ["meta-llama/Llama-2-70b-chat-hf"] # 7,13,70 all 4k
28
+
29
+ models = ["codellama/CodeLlama-34b-Instruct-hf"]
30
+
31
+ # models = ["claude-instant-1"]
32
+
33
+
34
+ srand 0
35
+
36
+ names = raw_names
37
+ fruits = raw_fruits
38
+
39
+ puts "We have #{names.length} names and #{fruits.length} fruits."
40
+
41
+ fruit_counts = [] # name_index -> fruit -> count
42
+ sentences = []
43
+
44
+ index_for_name = {}
45
+
46
+ names.each_with_index do |name, i|
47
+ index_for_name[name] = i
48
+ my_fruits = fruits.sample(10)
49
+ for mf in my_fruits
50
+ count = rand(1000000) + 2
51
+ fruit_counts[i] ||= {}
52
+ fruit_counts[i][mf] = count
53
+ end
54
+ s = "#{name} has: "
55
+ s << my_fruits[0..-2].map { |fruit| "#{fruit_counts[i][fruit]} #{fruit}" }.join(", ")
56
+ s << ", and 42 #{my_fruits[0]}.\n"
57
+ sentences [i] = s
58
+ end
59
+
60
+ question1 = "\n\n\n\nHow many of each fruit did "
61
+ question2 = " have? Respond with a json that maps names to a dictionary with number of each fruit they had."
62
+
63
+ data_point_count = 8
64
+
65
+ for model in models
66
+ request = glim.request(model:)
67
+
68
+ request.prompt = question1 + question2
69
+ question_length = request.prompt_token_count
70
+ reserve = question_length + 100 * data_point_count
71
+
72
+ p = ""
73
+ sentence_count = 0
74
+ for sentence in sentences
75
+ request.prompt = p + sentence
76
+ if (request.prompt_token_count + reserve) > request.context_length
77
+ break
78
+ end
79
+ p += sentence
80
+ sentence_count += 1
81
+ end
82
+
83
+ # get relatively evenly spaced data points
84
+ indices = select_data_point_indices(data_point_count, sentence_count)
85
+ puts "Data point indices: #{indices.inspect}"
86
+
87
+ name_list = indices[0..-2].map { |i| names[i] }.join(", ")
88
+ name_list += ", and #{names[indices.last]}"
89
+ request.prompt = p + question1 + name_list + question2
90
+
91
+ request.prepare!
92
+ puts "Ready to send to API. Cost will be at least $ #{request.min_cost}"
93
+ puts "Tokens: #{request.prompt_token_count}"
94
+
95
+ response = request.response
96
+ r = response.completion
97
+ puts "\n\nResponse from #{model}: \n#{r}"
98
+ # r = extract_between_markers(r, "```json","```")
99
+ puts "\n\JSON from #{model}: \n#{r}"
100
+
101
+ all_counts = JSON.parse(r)
102
+ scores = []
103
+ for name in all_counts.keys
104
+ # name = my_counts["name"]
105
+ my_counts = all_counts[name]
106
+ index = index_for_name[name]
107
+ ground_truth = fruit_counts[index]
108
+ score = 0
109
+ ground_truth.each_pair do |k, v|
110
+ if my_counts[k] == v
111
+ score += 1
112
+ end
113
+ end
114
+ scores << score
115
+ end
116
+ puts "#{model}: score = #{scores}"
117
+
118
+ end
@@ -0,0 +1,56 @@
1
+
2
+ # Given item_count items, get a list of indices into data
3
+ # that are equidistant and include 0 and item_count-1
4
+ def select_data_point_indices(data_point_count, item_count)
5
+ delta = item_count / (data_point_count - 1.0)
6
+ indices = []
7
+ for i in 0..(data_point_count - 2)
8
+ indices << (i * delta).round
9
+ end
10
+ indices << (item_count - 1)
11
+ end
12
+
13
+
14
+
15
+ def make_code(length)
16
+ chars = ([*('0'..'9'), *('a'..'z')])
17
+ (Array.new(length) { chars.sample }).join
18
+ end
19
+
20
+
21
+ # if false
22
+ # pos = rand(len-1)
23
+ # t = s[pos]
24
+ # s[pos] = s[pos+1]
25
+ # s[pos+1] = t
26
+ # end
27
+
28
+ # this generates a string of length len that will be
29
+ # a repeating sequence of an extended alphabet, but with
30
+ # deletions chars missing in random locations.
31
+ # the length will always be the same, but the levenshtein distance
32
+ # can be up to 2*deletions + 2*flips. Of course it can be less,
33
+ # if somehow some of these edits simplify or cancel out.
34
+ # safest to use only deletions or only flips, not both.
35
+ def make_special_code_with_fixed_length(len, deletions: 0, flips: 0)
36
+ raise ArgumentError.new("len must be > 0") unless len > 0
37
+ raise ArgumentError.new("deletions must be >= 0") unless deletions >= 0
38
+ raise ArgumentError.new("flips must be >= 0") unless flips >= 0
39
+ raise ArgumentError.new("deletions + flips must be <= len") unless deletions + flips <= len
40
+
41
+ alphabet = ((0..9).to_a + ("a".."z").to_a + ("A".."Z").to_a).join
42
+ count = (len+deletions+10)/alphabet.length+1
43
+ s = alphabet * count
44
+ deletions.times do
45
+ pos = rand(len-3)
46
+ s[pos] = ""
47
+ end
48
+ flips.times do
49
+ pos = 2+rand(len-5)
50
+ t = s[pos]
51
+ s[pos] = s[pos+1]
52
+ s[pos+1] = t
53
+ end
54
+ s = s[0..len-1]
55
+ return s
56
+ end
@@ -0,0 +1,214 @@
1
+ require_relative '../../lib/globals'
2
+ require_relative 'data'
3
+ require_relative 'tools'
4
+
5
+ glim = GlimContext.new(log_name: "ask_all")
6
+
7
+ # llm_name = "gpt-3.5-turbo"
8
+ # llm_name = "claude-instant-1"
9
+
10
+ llm_scores = []
11
+
12
+ data_point_count = 20
13
+ row_count = 18
14
+
15
+ for run in 0..100
16
+ puts "run = #{run}"
17
+ llm_scores[run] = {}
18
+
19
+ for llm_name in GlimRequest.llama2_llms[1..1]
20
+ puts "llm_name = #{llm_name}"
21
+
22
+ srand run+2000
23
+
24
+ names = raw_names
25
+ sentences = []
26
+ index_for_name = {}
27
+ codes = []
28
+
29
+ code_length = 100
30
+
31
+ names.each_with_index do |name, i|
32
+ index_for_name[name] = i
33
+ if i % 2 == 0
34
+ code = make_special_code_with_fixed_length(code_length,flips: 2)
35
+ else
36
+ code = make_code(code_length)
37
+ end
38
+ codes[i] = code
39
+ sentences[i] = "#{name}'s code: #{code}\n"
40
+ end
41
+
42
+ instructions = "\n\nGenerate a json that maps each name to its code."
43
+
44
+ answer_line = " \"#{names[1]}\": \"#{codes[1]}\",\n"
45
+
46
+ request = glim.request(llm_name:, max_tokens: 4000)
47
+
48
+ long_code = make_code(10000)
49
+ token_length = long_code.length/request.count_tokens(long_code).to_f
50
+ puts "1 token is on average #{token_length} chars long."
51
+
52
+ # estimate how many rows will fit into the context window, including response
53
+ instructions_length = request.count_tokens(instructions)
54
+ answer_line_length = request.count_tokens(answer_line)
55
+ sentence_length = request.count_tokens(sentences[0])
56
+ max_row_count = ((request.context_length - instructions_length - 80) / (sentence_length + answer_line_length + 10)) - 1
57
+
58
+ puts "row_count = #{row_count} < max_row_count = #{max_row_count}"
59
+
60
+ data = sentences[0..row_count-1].join
61
+ request.prompt = data + instructions
62
+
63
+ puts "Prompt:\n#{request.prompt}"
64
+
65
+ answer_length = answer_line_length * row_count
66
+ data_length = request.count_tokens(data)
67
+ puts "Instructions: #{instructions_length} tokens"
68
+ puts "Each sentence: roughly #{sentence_length} tokens, #{data_length} in total"
69
+ puts "Each answer line: #{answer_line_length} tokens, #{answer_length} in total"
70
+ puts "Estimated total: #{instructions_length + answer_length + data_length} tokens"
71
+
72
+ puts "Ready to send #{request.prompt_token_count} tokens to API. Cost will be at least $ #{request.min_cost}"
73
+
74
+ response = request.response
75
+ r = response.completion.strip
76
+ #puts "\n\nResponse from #{model}: \n#{r}"
77
+
78
+ begin
79
+ result = extract_json(r)
80
+ rescue
81
+ puts "ERROR "*10
82
+ puts "Failed to parse JSON:"
83
+ puts r
84
+ next
85
+ end
86
+ scores = []
87
+ result.each_pair do |name, v|
88
+ index = index_for_name[name]
89
+ distance, explanation = levenshtein_distance(codes[index],v)
90
+ scores[index] = (code_length - distance) / code_length.to_f
91
+ puts explanation
92
+ end
93
+ puts "#{llm_name}: run = #{run}, scores = #{scores}"
94
+ llm_scores[run][llm_name] = scores
95
+ end
96
+ puts JSON.pretty_generate(llm_scores)
97
+
98
+ clean_llm_scores = []
99
+ for i in 0..llm_scores.length-1
100
+ if llm_scores[i]["meta-llama/Llama-2-13b-chat-hf"].length == row_count
101
+ clean_llm_scores << llm_scores[i]
102
+ else
103
+ puts "Skipping #{i}"
104
+ end
105
+ end
106
+
107
+ sums = []
108
+ for i in 0..row_count-1
109
+ sums[i] = clean_llm_scores.map { |x| x["meta-llama/Llama-2-13b-chat-hf"][i] }.count { |x| x >= 0.999 }
110
+ end
111
+
112
+ puts sums.inspect
113
+
114
+
115
+ # sums = llm_scores[0]["meta-llama/Llama-2-13b-chat-hf"].map { |x| (x >= 0.999) ? 1 : 0 }
116
+
117
+ # raise "sums.length = #{sums.length}" unless sums.length == row_count
118
+ # for runscores in llm_scores[1..-1]
119
+ # runscores13b = runscores["meta-llama/Llama-2-13b-chat-hf"]
120
+ # raise "runscores13b.length = #{runscores13b.length}" unless runscores13b.length == row_count
121
+ # sums = sums.zip(runscores13b).map do |a,b|
122
+ # (b >= 0.999) ? a : a+1
123
+ # end
124
+ # puts sums.inspect
125
+
126
+ # end
127
+
128
+ end
129
+
130
+
131
+
132
+
133
+ # Instructions: 12 tokens
134
+ # Each sentence: roughly 83 tokens, 1646 in total
135
+ # Each answer line: 83 tokens, 1660 in total
136
+ # Estimated total: 3318 tokens
137
+ # 0.237: Tiktoken doesn't know model meta-llama/Llama-2-70b-chat-hf
138
+ # 0.237: Tiktoken doesn't know model meta-llama/Llama-2-70b-chat-hf
139
+ # Ready to send 1657 tokens to API. Cost will be at least $ 0.0016569999999999998
140
+ # meta-llama/Llama-2-70b-chat-hf: scores = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
141
+ # {
142
+ # "meta-llama/Llama-2-7b-chat-hf": [
143
+ # 1.0,
144
+ # 1.0,
145
+ # 0.8083333333333333,
146
+ # 0.9916666666666667,
147
+ # 0.7583333333333333,
148
+ # 0.9916666666666667,
149
+ # 0.5416666666666666,
150
+ # 0.9083333333333333,
151
+ # 0.7666666666666667,
152
+ # 0.30833333333333335,
153
+ # 0.7083333333333334,
154
+ # 0.75,
155
+ # 0.8166666666666667,
156
+ # 0.75,
157
+ # 0.825,
158
+ # 0.75,
159
+ # 0.7916666666666666
160
+ # ],
161
+ # "meta-llama/Llama-2-13b-chat-hf": [
162
+ # 1.0,
163
+ # 1.0,
164
+ # 1.0,
165
+ # 1.0,
166
+ # 1.0,
167
+ # 1.0,
168
+ # 1.0,
169
+ # 1.0,
170
+ # 1.0,
171
+ # 1.0,
172
+ # 1.0,
173
+ # 1.0,
174
+ # 1.0,
175
+ # 1.0,
176
+ # 1.0,
177
+ # 1.0,
178
+ # 1.0,
179
+ # 1.0
180
+ # ],
181
+ # "meta-llama/Llama-2-70b-chat-hf": [
182
+ # 1.0,
183
+ # 1.0,
184
+ # 1.0,
185
+ # 1.0,
186
+ # 1.0,
187
+ # 1.0,
188
+ # 1.0,
189
+ # 1.0,
190
+ # 1.0,
191
+ # 1.0,
192
+ # 1.0,
193
+ # 1.0,
194
+ # 1.0,
195
+ # 1.0,
196
+ # 1.0,
197
+ # 1.0,
198
+ # 1.0,
199
+ # 1.0,
200
+ # 1.0,
201
+ # 1.0
202
+ # ]
203
+ # }
204
+
205
+ # 13b / 20
206
+ # [38.920000000000016, 41.0, 40.82000000000001, 41.0, 40.839999999999996, 40.980000000000004, 40.809999999999995, 41.0, 40.84, 40.989999999999995, 40.72, 41.0, 40.77, 40.99, 40.88, 40.99, 40.55, 40.98, 40.76]
207
+
208
+
209
+ # srand 1000+
210
+ # [96.21999999999993, 100.99, 100.78, 100.99, 100.65999999999998, 101.0, 100.72, 101.0, 100.64, 100.98, 100.72999999999999, 100.97999999999999, 100.78, 100.99000000000001, 100.74000000000001, 100.99000000000001, 100.6, 100.99000000000001]
211
+
212
+ # srand 1000+
213
+ # counting pnly
214
+ # [35, 100, 91, 100, 87, 101, 87, 101, 88, 99, 90, 99, 92, 100, 89, 100, 83, 100]
@@ -0,0 +1,44 @@
1
+ # File: make_special_code_with_fixed_length_test.rb
2
+ require 'minitest/autorun'
3
+ require_relative '../lib/globals'
4
+
5
+ class MakeSpecialCodeWithFixedLengthTest < Minitest::Test
6
+ def setup
7
+ @base = "0123456789" + ("a".."z").to_a.join + ("A".."Z").to_a.join
8
+ @base_len = @base.length
9
+ @len = 100
10
+ end
11
+
12
+ def test_basic_function
13
+ test_string = make_special_code_with_fixed_length(@len, deletions: 0, flips: 0)
14
+ # Length should be @len
15
+ assert_equal @len, test_string.length
16
+ # There should be no randomness introduced
17
+ assert_equal @base[0...@len], test_string
18
+ end
19
+
20
+ def test_deletions
21
+ test_string = make_special_code_with_fixed_length(@len, deletions: 10, flips: 0)
22
+ assert_equal @len, test_string.length
23
+ # Deletion is random, but levenshtein distance should be 10
24
+ assert_equal 10, levenshtein_distance(@base[0...@len], test_string).first
25
+ end
26
+
27
+ def test_flips
28
+ test_string = make_special_code_with_fixed_length(@len, deletions: 0, flips: 10)
29
+ assert_equal @len, test_string.length
30
+ # Flips is random, but levenshtein distance should be 20
31
+ assert_equal 20, levenshtein_distance(@base[0...@len], test_string).first
32
+ end
33
+
34
+ def test_argument_errors
35
+ # Raises error for non positive length
36
+ assert_raises(ArgumentError) { make_special_code_with_fixed_length(0, deletions: 0, flips: 0) }
37
+ assert_raises(ArgumentError) { make_special_code_with_fixed_length(-10, deletions: 0, flips: 0) }
38
+ # Raises error for negative deletions and flips
39
+ assert_raises(ArgumentError) { make_special_code_with_fixed_length(@len, deletions: -1, flips: 0) }
40
+ assert_raises(ArgumentError) { make_special_code_with_fixed_length(@len, deletions: 0, flips: -10) }
41
+ # Raises error if deletions + flips > len
42
+ assert_raises(ArgumentError) { make_special_code_with_fixed_length(@len, deletions: 50, flips: 60) }
43
+ end
44
+ end
@@ -0,0 +1 @@
1
+ # File: make_special_code_with_fixed_length_test.rb
@@ -0,0 +1,55 @@
1
+ # File: make_special_code_with_fixed_length_test.rb
2
+ require 'minitest/autorun'
3
+ require_relative "../lib/globals"
4
+
5
+ class MakeSpecialCodeWithFixedLengthTest < Minitest::Test
6
+ def setup
7
+ @alphabet = ((0..9).to_a + ("a".."z").to_a + ("A".."Z").to_a).join
8
+ end
9
+
10
+ def test_normal_case
11
+ result = make_special_code_with_fixed_length(15, 2)
12
+ assert_kind_of String, result
13
+ assert_equal 15, result.length
14
+ assert_equal 2, @alphabet.length*5 - levenshtein_distance(@alphabet*5, result)[0]
15
+ end
16
+
17
+ def test_zero_deletions
18
+ result = make_special_code_with_fixed_length(15, 0)
19
+ assert_kind_of String, result
20
+ assert_equal 15, result.length
21
+ assert_equal 0, @alphabet.length*5 - levenshtein_distance(@alphabet*5, result)[0]
22
+ end
23
+
24
+ def test_string_has_more_than_requested_length
25
+ result = make_special_code_with_fixed_length(50, 2)
26
+ assert_kind_of String, result
27
+ assert_equal 50, result.length
28
+ assert_equal 2, @alphabet.length*2 - levenshtein_distance(@alphabet*2, result)[0]
29
+ end
30
+
31
+ def test_zero_length
32
+ result = make_special_code_with_fixed_length(0, 2)
33
+ assert_kind_of String, result
34
+ assert_equal 0, result.length
35
+ assert_equal 2, @alphabet.length*5 - levenshtein_distance(@alphabet*5, result)[0]
36
+ end
37
+
38
+ def test_negative_length
39
+ assert_raises do
40
+ make_special_code_with_fixed_length(-1, 2)
41
+ end
42
+ end
43
+
44
+ def test_negative_deletions
45
+ assert_raises do
46
+ make_special_code_with_fixed_length(15, -1)
47
+ end
48
+ end
49
+
50
+ def test_length_less_than_deletions
51
+ assert_raises do
52
+ make_special_code_with_fixed_length(2, 15)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,33 @@
1
+ # File: make_special_code_with_fixed_length_test.rb
2
+ require_relative '../lib/globals'
3
+ require 'minitest/autorun'
4
+
5
+ class TestMakeSpecialCodeWithFixedLength < Minitest::Test
6
+ def setup
7
+ @len = 10
8
+ @deletions = 2
9
+ @test_string = make_special_code_with_fixed_length(@len, @deletions)
10
+ end
11
+
12
+ def test_length
13
+ assert_equal @len, @test_string.length, "Output string length does not match requested length."
14
+ end
15
+
16
+ def test_levenshtein_distance
17
+ original_string = make_special_code_with_fixed_length(@len, 0)
18
+ expected_distance, _ = levenshtein_distance(original_string, @test_string)
19
+ assert_equal @deletions, expected_distance, "Levenshtein distance does not match the number of deletions."
20
+ end
21
+
22
+ def test_negative_length
23
+ assert_raises { make_special_code_with_fixed_length(-1, @deletions) }
24
+ end
25
+
26
+ def test_negative_deletions
27
+ assert_raises { make_special_code_with_fixed_length(@len, -1) }
28
+ end
29
+
30
+ def test_deletion_greater_than_length
31
+ assert_raises { make_special_code_with_fixed_length(@len, @len + 1) }
32
+ end
33
+ end
@@ -0,0 +1,58 @@
1
+ # File: test/make_special_code_with_fixed_length_test.rb
2
+ require 'minitest/autorun'
3
+ require_relative '../lib/globals'
4
+
5
+ class TestMakeSpecialCodeWithFixedLength < Minitest::Test
6
+ def setup
7
+ @len = 20
8
+ end
9
+
10
+ def test_normal_case
11
+ code = make_special_code_with_fixed_length(@len, deletions: 2, flips: 3)
12
+ assert_equal @len, code.length, "Failed on test where code length != len"
13
+ orig_code = make_special_code_with_fixed_length(@len, deletions: 0, flips: 0)
14
+ distance, _ = levenshtein_distance(orig_code, code)
15
+ assert_equal 4, distance, "Failed on test where levenshtein distance != 2*deletions"
16
+ end
17
+
18
+ def test_zero_len
19
+ code = make_special_code_with_fixed_length(0, deletions: 2, flips: 2)
20
+ assert_equal "", code, "Failed on test where len = 0"
21
+ end
22
+
23
+ def test_large_len
24
+ @len = 1_000_000
25
+ code = make_special_code_with_fixed_length(@len, deletions: 2, flips: 2)
26
+ assert_equal @len, code.length, "Failed on test where len is very large"
27
+ end
28
+
29
+ def test_negative_len
30
+ assert_raises(ArgumentError) do
31
+ make_special_code_with_fixed_length(-1, deletions: 2, flips: 2)
32
+ end
33
+ end
34
+
35
+ def test_negative_deletions
36
+ assert_raises(ArgumentError) do
37
+ make_special_code_with_fixed_length(@len, deletions: -2, flips: 2)
38
+ end
39
+ end
40
+
41
+ def test_negative_flips
42
+ assert_raises(ArgumentError) do
43
+ make_special_code_with_fixed_length(@len, deletions: 2, flips: -2)
44
+ end
45
+ end
46
+
47
+ def test_deletions_plus_flips_greater_than_len
48
+ assert_raises(ArgumentError) do
49
+ make_special_code_with_fixed_length(@len, deletions: @len + 1, flips: 1)
50
+ end
51
+ end
52
+
53
+ def test_fractional_len
54
+ assert_raises(ArgumentError) do
55
+ make_special_code_with_fixed_length(1.5, deletions: 2, flips: 2)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,37 @@
1
+ require 'tiktoken_ruby' # TODO only for token counting while anthropic doesn't support it
2
+
3
+ require 'anthropic'
4
+ #require_relative 'globals'
5
+
6
+ Anthropic.configure do |config|
7
+ config.access_token = ENV.fetch('ANTHROPIC_API_KEY')
8
+ config.request_timeout = 480 # Optional
9
+ end
10
+
11
+ # GlimRequest delegates to this
12
+ class AnthropicRequestDetails # only for requests that involve a message array, like OpenAI
13
+
14
+ def initialize(req)
15
+ @req = req
16
+ end
17
+
18
+ attr_accessor :req
19
+
20
+ def response_class
21
+ AnthropicResponse
22
+ end
23
+
24
+ def llm_class_changed
25
+ update_request_hash
26
+ end
27
+
28
+ def update_request_hash
29
+ req.request_hash[:max_tokens_to_sample] = req.max_tokens || 2000
30
+ req.request_hash[:prompt] = req.prompt
31
+ req.request_hash[:temperature] = req.temperature
32
+ req.request_hash[:model] = req.llm_name
33
+ # deeply remove keys for any values that are nil
34
+ req.request_hash.delete_if { |k, v| v.nil? }
35
+ end
36
+
37
+ end