lemans 1.0.0.pre.1 → 1.0.0.rc.2

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -5
  3. data/README.md +47 -28
  4. data/exe/lemans-remote +43 -41
  5. data/lib/lemans/agents/miniswen_installed.rb +4 -4
  6. data/lib/lemans/cli/board_reporter.rb +8 -8
  7. data/lib/lemans/cli/progress_reporter.rb +1 -1
  8. data/lib/lemans/cli/report/aggregate.rb +2 -2
  9. data/lib/lemans/cli/report.rb +6 -6
  10. data/lib/lemans/cli/templates/bench/README.md +41 -0
  11. data/lib/lemans/cli/templates/bench/bench.yml +108 -0
  12. data/lib/lemans/cli/templates/bench/environment/Dockerfile +10 -0
  13. data/lib/lemans/cli/templates/bench/tasks/example-task/environment.patch +7 -0
  14. data/lib/lemans/cli/templates/bench/tasks/example-task/instruction.md +32 -0
  15. data/lib/lemans/cli/templates/bench/tasks/example-task/solution.patch +7 -0
  16. data/lib/lemans/cli/templates/bench/tasks/example-task/verification_test.rb +34 -0
  17. data/lib/lemans/cli/templates/bench/tasks/hello-world/environment.patch +7 -0
  18. data/lib/lemans/cli/templates/bench/tasks/hello-world/instruction.md +11 -0
  19. data/lib/lemans/cli/templates/bench/tasks/hello-world/solution.patch +7 -0
  20. data/lib/lemans/cli/templates/bench/tasks/hello-world/verification_test.rb +10 -0
  21. data/lib/lemans/cli.rb +14 -3
  22. data/lib/lemans/config/environment.rb +5 -1
  23. data/lib/lemans/config/revision.rb +2 -0
  24. data/lib/lemans/config/setup.rb +1 -15
  25. data/lib/lemans/config/tree_digest.rb +1 -1
  26. data/lib/lemans/config/verifier.rb +1 -1
  27. data/lib/lemans/config.rb +8 -0
  28. data/lib/lemans/environments/daytona/faraday_transfer.rb +1 -1
  29. data/lib/lemans/environments/daytona/retries.rb +1 -1
  30. data/lib/lemans/environments/daytona/sdk_tweaks.rb +2 -2
  31. data/lib/lemans/environments/daytona/shell.rb +1 -1
  32. data/lib/lemans/environments/daytona/snapshot_store.rb +2 -2
  33. data/lib/lemans/environments/daytona.rb +1 -1
  34. data/lib/lemans/result.rb +1 -1
  35. data/lib/lemans/secrets_filter.rb +25 -0
  36. data/lib/lemans/stores/fs.rb +17 -4
  37. data/lib/lemans/task_definition.rb +10 -8
  38. data/lib/lemans/trial/verifier/assets/eport-lemans.rb +9 -1
  39. data/lib/lemans/trial/verifier/assets/lemans_minitest_reporter.rb +2 -2
  40. data/lib/lemans/trial/verifier.rb +3 -3
  41. data/lib/lemans/trial.rb +1 -1
  42. data/lib/lemans/version.rb +1 -1
  43. data/lib/lemans.rb +2 -0
  44. data/lib/miniswen/agent.rb +11 -11
  45. data/lib/miniswen/ruby_llm.rb +7 -1
  46. data/lib/miniswen/testing.rb +7 -7
  47. data/lib/miniswen/trajectory.rb +1 -1
  48. data/lib/miniswen/version.rb +1 -1
  49. metadata +15 -1
@@ -64,8 +64,8 @@ module Lemans
64
64
  def upload_tests!
65
65
  environment.exec!("rm -rf #{TESTS_DIR} && mkdir -p #{TESTS_DIR}")
66
66
 
67
- uploads = task.verifier.files.to_h { |local, remote| [remote, local] }
68
- .merge(task.test_files.to_h { |local, remote| [remote, local] })
67
+ uploads = task.verifier.files.to_h { |local, remote| [ remote, local ] }
68
+ .merge(task.test_files.to_h { |local, remote| [ remote, local ] })
69
69
 
70
70
  uploads.each { |remote, local| environment.upload(local, "#{TESTS_DIR}/#{remote}") }
71
71
  ASSETS.glob("*.rb").each { |asset| environment.upload(asset, "#{TESTS_DIR}/#{asset.basename}") }
@@ -102,7 +102,7 @@ module Lemans
102
102
  end
103
103
 
104
104
  def verifier_script
105
- [task.verifier.preverify, task.verifier.command].compact.map { "( #{it} )" }.join(" && ")
105
+ [ task.verifier.preverify, task.verifier.command ].compact.map { "( #{it} )" }.join(" && ")
106
106
  end
107
107
 
108
108
  def read_reward(command_result)
data/lib/lemans/trial.rb CHANGED
@@ -118,7 +118,7 @@ module Lemans
118
118
  raise
119
119
  rescue StandardError => e
120
120
  # A harness bug must leave evidence.
121
- result.failed!(:harness_crash, ["#{e.class}: #{e.message}", *Array(e.backtrace).first(5)].join("\n"))
121
+ result.failed!(:harness_crash, [ "#{e.class}: #{e.message}", *Array(e.backtrace).first(5) ].join("\n"))
122
122
  ensure
123
123
  environment&.stop
124
124
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lemans
4
- VERSION = "1.0.0.pre.1"
4
+ VERSION = "1.0.0.rc.2"
5
5
  end
data/lib/lemans.rb CHANGED
@@ -9,6 +9,8 @@ loader.inflector.inflect("cli" => "CLI", "atif" => "ATIF", "fs" => "FS",
9
9
  loader.ignore("#{__dir__}/miniswen.rb", "#{__dir__}/miniswen")
10
10
  # assets/ holds files uploaded into sandboxes, not Ruby the harness loads.
11
11
  loader.ignore("#{__dir__}/lemans/trial/verifier/assets")
12
+ # templates/ holds the files `lemans init` scaffolds, not Ruby the harness loads.
13
+ loader.ignore("#{__dir__}/lemans/cli/templates")
12
14
  loader.setup
13
15
 
14
16
  require "miniswen"
@@ -215,9 +215,9 @@ module Miniswen
215
215
  value.to_h do |key, item|
216
216
  key = key.to_sym
217
217
  # Tool-call arguments and reasoning details keep their provider-style string keys.
218
- [key, %i[arguments reasoning_details].include?(key) ? item : deep_symbolize(item)]
218
+ [ key, %i[arguments reasoning_details].include?(key) ? item : deep_symbolize(item) ]
219
219
  end
220
- when Array then value.map { deep_symbolize(_1) }
220
+ when Array then value.map { deep_symbolize(it) }
221
221
  else value
222
222
  end
223
223
  end
@@ -313,7 +313,7 @@ module Miniswen
313
313
  # them back from ENV on boot (the option upcased).
314
314
  def provider_env
315
315
  _, provider = resolved
316
- env = provider.configuration_requirements.to_h { [_1.to_s.upcase, RubyLLM.config.public_send(_1)] }.compact
316
+ env = provider.configuration_requirements.to_h { [ it.to_s.upcase, RubyLLM.config.public_send(it) ] }.compact
317
317
 
318
318
  order = ENV["LEMANS_PROVIDER_ORDER"]
319
319
  env["LEMANS_PROVIDER_ORDER"] = order if order
@@ -340,7 +340,7 @@ module Miniswen
340
340
  def next_actions
341
341
  response = complete(@messages)
342
342
  @steps += 1
343
- @totals.each_key { @totals[_1] += response[_1].to_i }
343
+ @totals.each_key { @totals[it] += response[it].to_i }
344
344
  track_cost(response)
345
345
 
346
346
  entry = { role: "assistant", timestamp: Time.now.utc.iso8601, content: response[:content].to_s,
@@ -496,7 +496,7 @@ module Miniswen
496
496
  def complete(messages)
497
497
  model_info, provider = resolved
498
498
  response = provider.complete(
499
- with_cache_breakpoints(messages.map { as_ruby_llm(_1) }),
499
+ with_cache_breakpoints(messages.map { as_ruby_llm(it) }),
500
500
  tools: { bash: @bash_tool },
501
501
  temperature: nil,
502
502
  model: model_info,
@@ -518,13 +518,13 @@ module Miniswen
518
518
  def with_cache_breakpoints(messages)
519
519
  return messages unless explicit_cache?
520
520
 
521
- system = messages.find { _1.role == :system }
522
- [system, messages.last].compact.uniq.each do |message|
521
+ system = messages.find { it.role == :system }
522
+ [ system, messages.last ].compact.uniq.each do |message|
523
523
  text = message.content
524
524
  next unless text.is_a?(String) && !text.empty?
525
525
 
526
526
  message.content = RubyLLM::Content::Raw.new(
527
- [{ type: "text", text: text, cache_control: CACHE_CONTROL }]
527
+ [ { type: "text", text: text, cache_control: CACHE_CONTROL } ]
528
528
  )
529
529
  end
530
530
  messages
@@ -580,8 +580,8 @@ module Miniswen
580
580
  return nil if tool_calls.nil? || tool_calls.empty?
581
581
 
582
582
  tool_calls.to_h do |call|
583
- [call[:id], RubyLLM::ToolCall.new(id: call[:id], name: call[:name], arguments: call[:arguments],
584
- thought_signature: call[:thought_signature])]
583
+ [ call[:id], RubyLLM::ToolCall.new(id: call[:id], name: call[:name], arguments: call[:arguments],
584
+ thought_signature: call[:thought_signature]) ]
585
585
  end
586
586
  end
587
587
 
@@ -668,7 +668,7 @@ module Miniswen
668
668
  cache_write = info.cache_write_input_price_per_million || input
669
669
  # Providers usually fold thinking into output_tokens; max() bills the
670
670
  # larger count once and can never double-bill.
671
- generated = [response.output_tokens.to_i, tokens&.thinking.to_i].max
671
+ generated = [ response.output_tokens.to_i, tokens&.thinking.to_i ].max
672
672
  ((response.input_tokens.to_i * input) +
673
673
  (tokens&.cached.to_i * cache_read) +
674
674
  (tokens&.cache_creation.to_i * cache_write) +
@@ -8,6 +8,12 @@ RubyLLM.configure do |config|
8
8
  config.logger = Logger.new(IO::NULL) unless ENV["MINISWEN_DEBUG"] == "1"
9
9
  end
10
10
 
11
+ # Increase retry window to handle egress network issues
12
+ RubyLLM.configure do |config|
13
+ config.max_retries = 3
14
+ config.retry_interval = 1
15
+ end
16
+
11
17
  # ruby_llm reads no API keys from ENV on its own; the conventional variable is the provider's
12
18
  # config option upcased.
13
19
  RubyLLM.configure do |config|
@@ -34,7 +40,7 @@ module Miniswen
34
40
  # ConnectionFailed retries ruby_llm already does; it only lists SSL errors
35
41
  # as fatal.
36
42
  module RetryTransientSSL
37
- def retry_exceptions = super + [Faraday::SSLError]
43
+ def retry_exceptions = super + [ Faraday::SSLError ]
38
44
  end
39
45
  end
40
46
 
@@ -60,14 +60,14 @@ module Miniswen
60
60
  end
61
61
 
62
62
  def on(command, output, exit_code: 0)
63
- @canned[command] = [exit_code, output]
63
+ @canned[command] = [ exit_code, output ]
64
64
  end
65
65
 
66
66
  def exec(command, timeout: nil, env: nil) # rubocop:disable Lint/UnusedMethodArgument
67
67
  @commands << command
68
68
  return result(0, command.delete_prefix("echo ")) if command.start_with?("echo ")
69
69
 
70
- exit_code, output = @canned.fetch(command, [0, ""])
70
+ exit_code, output = @canned.fetch(command, [ 0, "" ])
71
71
  result(exit_code, output)
72
72
  end
73
73
 
@@ -87,7 +87,7 @@ module Miniswen
87
87
  # and/or :content, plus optional overrides (:tool_calls, :finish_reason,
88
88
  # :thinking, token counts). Overrides given here apply to every answer.
89
89
  def stub_llm(*answers, **overrides)
90
- RubyLLM::Test.stub_responses(*answers.map { llm_answer(_1, **overrides) })
90
+ RubyLLM::Test.stub_responses(*answers.map { llm_answer(it, **overrides) })
91
91
  end
92
92
 
93
93
  def llm_answer(answer, **overrides)
@@ -115,15 +115,15 @@ module Miniswen
115
115
  def raw_body_for(answer, calls)
116
116
  choice = { "finish_reason" => answer.fetch(:finish_reason) { calls.empty? ? "stop" : "tool_calls" } }
117
117
  choice["message"] = { "reasoning_details" => answer[:reasoning_details] } if answer[:reasoning_details]
118
- { "choices" => [choice] }
118
+ { "choices" => [ choice ] }
119
119
  end
120
120
 
121
121
  def tool_calls_for(answer)
122
- calls = answer[:tool_calls] || Array(answer[:cmd]).map { { name: "bash", arguments: { "command" => _1 } } }
122
+ calls = answer[:tool_calls] || Array(answer[:cmd]).map { { name: "bash", arguments: { "command" => it } } }
123
123
  calls.to_h do |call|
124
124
  id = call[:id] || "call_#{@llm_answer_ids = @llm_answer_ids.to_i + 1}"
125
- [id, RubyLLM::ToolCall.new(id: id, name: call[:name], arguments: call[:arguments],
126
- thought_signature: call[:thought_signature])]
125
+ [ id, RubyLLM::ToolCall.new(id: id, name: call[:name], arguments: call[:arguments],
126
+ thought_signature: call[:thought_signature]) ]
127
127
  end
128
128
  end
129
129
  end
@@ -83,7 +83,7 @@ module Miniswen
83
83
  end
84
84
 
85
85
  def tool_calls_for(calls)
86
- calls.map { { tool_call_id: _1[:id], function_name: _1[:name], arguments: _1[:arguments] } }
86
+ calls.map { { tool_call_id: it[:id], function_name: it[:name], arguments: it[:arguments] } }
87
87
  end
88
88
 
89
89
  def metrics_for(metrics)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Miniswen
4
- VERSION = "0.1.1"
4
+ VERSION = "1.0.0.rc.1"
5
5
  end
metadata CHANGED
@@ -1,11 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lemans
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.1
4
+ version: 1.0.0.rc.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svyatoslav Kryukov
8
8
  - Artur Petrov
9
+ - Vladimir Dementyev
9
10
  bindir: exe
10
11
  cert_chain: []
11
12
  date: 1980-01-02 00:00:00.000000000 Z
@@ -140,6 +141,7 @@ description: Lemans runs tasks against a coding agent in a sandbox.
140
141
  email:
141
142
  - me@skryukov.dev
142
143
  - ardecvz@gmail.com
144
+ - dementiev.vm@gmail.com
143
145
  executables:
144
146
  - lemans
145
147
  - lemans-remote
@@ -163,6 +165,17 @@ files:
163
165
  - lib/lemans/cli/progress_reporter.rb
164
166
  - lib/lemans/cli/report.rb
165
167
  - lib/lemans/cli/report/aggregate.rb
168
+ - lib/lemans/cli/templates/bench/README.md
169
+ - lib/lemans/cli/templates/bench/bench.yml
170
+ - lib/lemans/cli/templates/bench/environment/Dockerfile
171
+ - lib/lemans/cli/templates/bench/tasks/example-task/environment.patch
172
+ - lib/lemans/cli/templates/bench/tasks/example-task/instruction.md
173
+ - lib/lemans/cli/templates/bench/tasks/example-task/solution.patch
174
+ - lib/lemans/cli/templates/bench/tasks/example-task/verification_test.rb
175
+ - lib/lemans/cli/templates/bench/tasks/hello-world/environment.patch
176
+ - lib/lemans/cli/templates/bench/tasks/hello-world/instruction.md
177
+ - lib/lemans/cli/templates/bench/tasks/hello-world/solution.patch
178
+ - lib/lemans/cli/templates/bench/tasks/hello-world/verification_test.rb
166
179
  - lib/lemans/clobber.rb
167
180
  - lib/lemans/config.rb
168
181
  - lib/lemans/config/agent.rb
@@ -186,6 +199,7 @@ files:
186
199
  - lib/lemans/runner.rb
187
200
  - lib/lemans/runner/executor.rb
188
201
  - lib/lemans/runner/task.rb
202
+ - lib/lemans/secrets_filter.rb
189
203
  - lib/lemans/store.rb
190
204
  - lib/lemans/stores/fs.rb
191
205
  - lib/lemans/task_definition.rb