agent-harness 0.5.7 → 0.5.9

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.
@@ -431,17 +431,39 @@ module AgentHarness
431
431
  end
432
432
 
433
433
  def build_provider(provider_name, klass, executor:)
434
- config = AgentHarness.configuration.providers[provider_name]
435
- klass.new(
436
- config: config,
437
- executor: executor || AgentHarness.configuration.command_executor,
438
- logger: AgentHarness.logger
439
- )
434
+ canonical_name = Providers::Registry.instance.canonical_name(provider_name)
435
+ config = provider_config_for(provider_name, canonical_name: canonical_name)
436
+ executor ||= AgentHarness.configuration.command_executor
437
+ logger = AgentHarness.logger
438
+
439
+ provider = if klass.respond_to?(:build_provider_instance, true)
440
+ klass.send(:build_provider_instance, config: config, executor: executor, logger: logger)
441
+ else
442
+ klass.new(config: config, executor: executor, logger: logger)
443
+ end
444
+
445
+ # Ensure the executor is available even when the provider constructor
446
+ # accepts only a subset of keywords (e.g. config: only).
447
+ if provider.respond_to?(:executor=) && provider.executor.nil?
448
+ provider.executor = executor
449
+ elsif !provider.respond_to?(:executor)
450
+ provider.define_singleton_method(:executor) { executor }
451
+ end
452
+
453
+ provider
440
454
  end
441
455
 
442
456
  def monotonic_now
443
457
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
444
458
  end
459
+
460
+ def provider_config_for(requested_name, canonical_name:)
461
+ requested_key = requested_name.to_sym
462
+ canonical_key = canonical_name.to_sym
463
+
464
+ AgentHarness.configuration.providers[requested_key] ||
465
+ AgentHarness.configuration.providers[canonical_key]
466
+ end
445
467
  end
446
468
  end
447
469
  end
@@ -35,11 +35,15 @@ module AgentHarness
35
35
  # @param unset_env [Array<String>] environment variable names to remove from inherited env
36
36
  # @param metadata [Hash] arbitrary provider-specific data
37
37
  def initialize(model: nil, base_url: nil, api_provider: nil, env: {}, flags: [], unset_env: [], metadata: {})
38
+ validate_optional_string!(:model, model)
39
+ validate_optional_string!(:base_url, base_url)
40
+ validate_optional_string!(:api_provider, api_provider)
41
+
38
42
  @model = model
39
43
  @base_url = base_url
40
44
  @api_provider = api_provider
41
45
 
42
- env_hash = env || {}
46
+ env_hash = env.nil? ? {} : env
43
47
  unless env_hash.is_a?(Hash)
44
48
  raise ArgumentError, "env must be a Hash (got #{env_hash.class})"
45
49
  end
@@ -52,7 +56,7 @@ module AgentHarness
52
56
  end
53
57
  @env = normalized_env.freeze
54
58
 
55
- normalized_flags = flags || []
59
+ normalized_flags = flags.nil? ? [] : flags
56
60
  unless normalized_flags.is_a?(Array)
57
61
  raise ArgumentError, "flags must be an Array (got #{normalized_flags.class})"
58
62
  end
@@ -65,7 +69,7 @@ module AgentHarness
65
69
  end
66
70
  @flags = normalized_flags.freeze
67
71
 
68
- metadata_hash = metadata || {}
72
+ metadata_hash = metadata.nil? ? {} : metadata
69
73
  unless metadata_hash.is_a?(Hash)
70
74
  raise ArgumentError, "metadata must be a Hash (got #{metadata_hash.class})"
71
75
  end
@@ -74,7 +78,7 @@ module AgentHarness
74
78
  # Unset environment variables for the request. These are variable names that
75
79
  # should be removed from the inherited environment before the provider
76
80
  # command runs.
77
- unset_array = unset_env || []
81
+ unset_array = unset_env.nil? ? [] : unset_env
78
82
  unless unset_array.is_a?(Array)
79
83
  raise ArgumentError, "unset_env must be an Array (got #{unset_array.class})"
80
84
  end
@@ -96,14 +100,19 @@ module AgentHarness
96
100
  def self.from_hash(hash)
97
101
  raise ArgumentError, "expected a Hash, got #{hash.class}" unless hash.is_a?(Hash)
98
102
 
103
+ env_val = hash_value(hash, :env)
104
+ flags_val = hash_value(hash, :flags)
105
+ unset_env_val = hash_value(hash, :unset_env)
106
+ metadata_val = hash_value(hash, :metadata)
107
+
99
108
  new(
100
- model: hash[:model] || hash["model"],
101
- base_url: hash[:base_url] || hash["base_url"],
102
- api_provider: hash[:api_provider] || hash["api_provider"],
103
- env: hash[:env] || hash["env"] || {},
104
- flags: hash[:flags] || hash["flags"] || [],
105
- unset_env: hash[:unset_env] || hash["unset_env"] || [],
106
- metadata: hash[:metadata] || hash["metadata"] || {}
109
+ model: hash_value(hash, :model),
110
+ base_url: hash_value(hash, :base_url),
111
+ api_provider: hash_value(hash, :api_provider),
112
+ env: env_val.nil? ? {} : env_val,
113
+ flags: flags_val.nil? ? [] : flags_val,
114
+ unset_env: unset_env_val.nil? ? [] : unset_env_val,
115
+ metadata: metadata_val.nil? ? {} : metadata_val
107
116
  )
108
117
  end
109
118
 
@@ -128,5 +137,23 @@ module AgentHarness
128
137
  model.nil? && base_url.nil? && api_provider.nil? &&
129
138
  env.empty? && flags.empty? && metadata.empty? && unset_env.empty?
130
139
  end
140
+
141
+ private_class_method def self.hash_value(hash, key)
142
+ sym_value = hash[key]
143
+ str_value = hash[key.to_s]
144
+ # Prefer the symbol key; fall back to the string key only when the
145
+ # symbol key is nil (not just falsy) so that an explicit `false` is
146
+ # not silently discarded.
147
+ sym_value.nil? ? str_value : sym_value
148
+ end
149
+
150
+ private
151
+
152
+ def validate_optional_string!(name, value)
153
+ return if value.nil?
154
+ return if value.is_a?(String)
155
+
156
+ raise ArgumentError, "#{name} must be a String or nil (got #{value.class})"
157
+ end
131
158
  end
132
159
  end