Module: AgentHarness::Providers::Adapter
- Included in:
- Base
- Defined in:
- lib/agent_harness/providers/adapter.rb
Overview
Interface that all providers must implement
This module defines the contract that provider implementations must follow. Include this module in provider classes to ensure they implement the required interface.
Defined Under Namespace
Modules: ClassMethods
Class Method Summary collapse
- .included(base) ⇒ Object
- .metadata_package_name(contract, source) ⇒ Object
- .normalize_metadata_installation(contract, provider_name:, binary_name:) ⇒ Object
- .normalize_metadata_source_type(source) ⇒ Object
- .normalize_metadata_version_requirement(requirement) ⇒ Object
Instance Method Summary collapse
-
#auth_type ⇒ Symbol
Authentication type for this provider.
-
#build_mcp_flags(mcp_servers, working_dir: nil) ⇒ Array<String>
Build provider-specific MCP flags/arguments for CLI invocation.
-
#capabilities ⇒ Hash
Provider capabilities.
-
#configuration_schema ⇒ Hash
Provider configuration schema for app-driven setup UIs.
-
#dangerous_mode_flags ⇒ Array<String>
Get dangerous mode flags.
-
#error_patterns ⇒ Hash<Symbol, Array<Regexp>>
Error patterns for classification.
-
#execution_semantics ⇒ Hash
Execution semantics for this provider.
-
#fetch_mcp_servers ⇒ Array<Hash>
Fetch configured MCP servers.
-
#health_status ⇒ Hash
Health check.
-
#parse_rate_limit_reset(output) ⇒ Time?
Parse a rate-limit reset time from provider output.
-
#send_message(prompt:, **options) ⇒ Response
Send a message/prompt to the provider.
-
#session_flags(session_id) ⇒ Array<String>
Get session flags for continuation.
-
#smoke_test(timeout: nil, provider_runtime: nil) ⇒ Hash
Execute a minimal provider-owned smoke test via the configured executor.
-
#smoke_test_contract ⇒ Hash?
Canonical smoke-test contract for this provider instance.
-
#supported_mcp_transports ⇒ Array<String>
Supported MCP transport types for this provider.
-
#supports_dangerous_mode? ⇒ Boolean
Check if provider supports dangerous mode.
-
#supports_mcp? ⇒ Boolean
Check if provider supports MCP.
-
#supports_sessions? ⇒ Boolean
Check if provider supports session continuation.
-
#validate_config ⇒ Hash
Validate provider configuration.
-
#validate_mcp_servers!(mcp_servers) ⇒ Object
Validate that this provider can handle the given MCP servers.
Class Method Details
.included(base) ⇒ Object
75 76 77 |
# File 'lib/agent_harness/providers/adapter.rb', line 75 def self.included(base) base.extend(ClassMethods) end |
.metadata_package_name(contract, source) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/agent_harness/providers/adapter.rb', line 46 def self.(contract, source) return contract[:package_name] if contract[:package_name] return source[:package] if source.is_a?(Hash) package = contract[:package] return package unless package.is_a?(String) if package.split("@").first == "" package.split("@", 3).first(2).join("@") else package.split("@", 2).first end end |
.normalize_metadata_installation(contract, provider_name:, binary_name:) ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/agent_harness/providers/adapter.rb', line 19 def self.(contract, provider_name:, binary_name:) return nil unless contract.is_a?(Hash) source = contract[:source] install_command = contract[:install_command]&.dup { provider: provider_name.to_sym, source_type: (contract[:source_type] || source), package_name: (contract, source), default_version: contract[:default_version] || contract[:version] || contract[:resolved_version], resolved_version: contract[:resolved_version] || contract[:version] || contract[:default_version], supported_version_requirement: ( contract[:supported_version_requirement] || contract[:version_requirement] ), binary_name: contract[:binary_name] || binary_name, install_command: install_command, install_command_string: contract[:install_command_string] || install_command&.join(" ") } end |
.normalize_metadata_source_type(source) ⇒ Object
40 41 42 43 44 |
# File 'lib/agent_harness/providers/adapter.rb', line 40 def self.(source) return source[:type]&.to_sym if source.is_a?(Hash) source&.to_sym end |
.normalize_metadata_version_requirement(requirement) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/agent_harness/providers/adapter.rb', line 60 def self.(requirement) case requirement when nil nil when Array if requirement.all? { |entry| entry.is_a?(Array) && entry.length == 2 } requirement.map { |operator, version| "#{operator} #{version}" }.join(", ") else requirement.join(", ") end else requirement.to_s end end |
Instance Method Details
#auth_type ⇒ Symbol
Authentication type for this provider
765 766 767 |
# File 'lib/agent_harness/providers/adapter.rb', line 765 def auth_type :api_key end |
#build_mcp_flags(mcp_servers, working_dir: nil) ⇒ Array<String>
Build provider-specific MCP flags/arguments for CLI invocation
795 796 797 |
# File 'lib/agent_harness/providers/adapter.rb', line 795 def build_mcp_flags(mcp_servers, working_dir: nil) [] end |
#capabilities ⇒ Hash
Provider capabilities
742 743 744 745 746 747 748 749 750 751 752 |
# File 'lib/agent_harness/providers/adapter.rb', line 742 def capabilities { streaming: false, file_upload: false, vision: false, tool_use: false, json_mode: false, mcp: false, dangerous_mode: false } end |
#configuration_schema ⇒ Hash
Provider configuration schema for app-driven setup UIs
Returns metadata describing the configurable fields, supported authentication modes, and backend compatibility for this provider. Applications use this to build generic provider-entry forms without hardcoding provider-specific knowledge.
731 732 733 734 735 736 737 |
# File 'lib/agent_harness/providers/adapter.rb', line 731 def configuration_schema { fields: [], auth_modes: [auth_type], openai_compatible: false } end |
#dangerous_mode_flags ⇒ Array<String>
Get dangerous mode flags
845 846 847 |
# File 'lib/agent_harness/providers/adapter.rb', line 845 def dangerous_mode_flags [] end |
#error_patterns ⇒ Hash<Symbol, Array<Regexp>>
Error patterns for classification
757 758 759 |
# File 'lib/agent_harness/providers/adapter.rb', line 757 def error_patterns {} end |
#execution_semantics ⇒ Hash
Execution semantics for this provider
Returns a hash describing provider-specific execution behavior so downstream apps do not need to hardcode CLI quirks. This metadata can be used to select the right flags and interpret output.
950 951 952 953 954 955 956 957 958 959 960 961 |
# File 'lib/agent_harness/providers/adapter.rb', line 950 def execution_semantics { prompt_delivery: :arg, # :arg, :stdin, or :flag output_format: :text, # :text or :json sandbox_aware: false, # adjusts behavior inside containers uses_subcommand: false, # e.g. "codex exec", "opencode run" non_interactive_flag: nil, # flag to suppress interactive prompts legitimate_exit_codes: [0], # exit codes that are NOT errors stderr_is_diagnostic: true, # stderr may contain non-error output parses_rate_limit_reset: false # can extract Retry-After from output } end |
#fetch_mcp_servers ⇒ Array<Hash>
Fetch configured MCP servers
779 780 781 |
# File 'lib/agent_harness/providers/adapter.rb', line 779 def fetch_mcp_servers [] end |
#health_status ⇒ Hash
Health check
874 875 876 |
# File 'lib/agent_harness/providers/adapter.rb', line 874 def health_status {healthy: true, message: "OK"} end |
#parse_rate_limit_reset(output) ⇒ Time?
Parse a rate-limit reset time from provider output
Providers that include rate-limit reset information in their error output can override this to extract it, so the orchestration layer can schedule retries accurately.
971 972 973 |
# File 'lib/agent_harness/providers/adapter.rb', line 971 def parse_rate_limit_reset(output) nil end |
#send_message(prompt:, **options) ⇒ Response
Send a message/prompt to the provider
719 720 721 |
# File 'lib/agent_harness/providers/adapter.rb', line 719 def (prompt:, **) raise NotImplementedError, "#{self.class} must implement #send_message" end |
#session_flags(session_id) ⇒ Array<String>
Get session flags for continuation
860 861 862 |
# File 'lib/agent_harness/providers/adapter.rb', line 860 def session_flags(session_id) [] end |
#smoke_test(timeout: nil, provider_runtime: nil) ⇒ Hash
Execute a minimal provider-owned smoke test via the configured executor.
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 |
# File 'lib/agent_harness/providers/adapter.rb', line 890 def smoke_test(timeout: nil, provider_runtime: nil) contract = smoke_test_contract raise NotImplementedError, "#{self.class} does not implement #smoke_test_contract" unless contract prompt = contract[:prompt] if !prompt.is_a?(String) || prompt.strip.empty? raise ConfigurationError, "#{self.class}.smoke_test_contract must define a non-empty :prompt" end response = ( prompt: prompt, timeout: timeout || contract[:timeout], provider_runtime: provider_runtime ) output = response.output.to_s.strip expected_output = contract[:expected_output]&.strip success = response.success? && (!contract.fetch(:require_output, true) || !output.empty?) success &&= expected_output.nil? || output == expected_output if success return { ok: true, status: "ok", message: contract[:success_message] || "Smoke test passed", error_category: nil, output: output, exit_code: response.exit_code } end = response.error.to_s.strip = output if .empty? = "Smoke test failed with exit code #{response.exit_code}" if .empty? { ok: false, status: "error", message: , error_category: (), output: output, exit_code: response.exit_code } rescue TimeoutError => e failure_smoke_test_result(e., :timeout) rescue AuthenticationError => e failure_smoke_test_result(e., :auth_expired) rescue RateLimitError => e failure_smoke_test_result(e., :rate_limited) rescue ProviderError => e failure_smoke_test_result(e., (e.)) end |
#smoke_test_contract ⇒ Hash?
Canonical smoke-test contract for this provider instance.
881 882 883 |
# File 'lib/agent_harness/providers/adapter.rb', line 881 def smoke_test_contract self.class.smoke_test_contract if self.class.respond_to?(:smoke_test_contract) end |
#supported_mcp_transports ⇒ Array<String>
Supported MCP transport types for this provider
786 787 788 |
# File 'lib/agent_harness/providers/adapter.rb', line 786 def supported_mcp_transports [] end |
#supports_dangerous_mode? ⇒ Boolean
Check if provider supports dangerous mode
838 839 840 |
# File 'lib/agent_harness/providers/adapter.rb', line 838 def supports_dangerous_mode? capabilities[:dangerous_mode] end |
#supports_mcp? ⇒ Boolean
Check if provider supports MCP
772 773 774 |
# File 'lib/agent_harness/providers/adapter.rb', line 772 def supports_mcp? capabilities[:mcp] end |
#supports_sessions? ⇒ Boolean
Check if provider supports session continuation
852 853 854 |
# File 'lib/agent_harness/providers/adapter.rb', line 852 def supports_sessions? false end |
#validate_config ⇒ Hash
Validate provider configuration
867 868 869 |
# File 'lib/agent_harness/providers/adapter.rb', line 867 def validate_config {valid: true, errors: []} end |
#validate_mcp_servers!(mcp_servers) ⇒ Object
Validate that this provider can handle the given MCP servers
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
# File 'lib/agent_harness/providers/adapter.rb', line 804 def validate_mcp_servers!(mcp_servers) return if mcp_servers.nil? || mcp_servers.empty? unless supports_mcp? raise McpUnsupportedError.new( "Provider '#{self.class.provider_name}' does not support MCP servers", provider: self.class.provider_name ) end supported = supported_mcp_transports if supported.empty? raise McpUnsupportedError.new( "Provider '#{self.class.provider_name}' does not support request-time MCP servers", provider: self.class.provider_name ) end mcp_servers.each do |server| next if supported.include?(server.transport) raise McpTransportUnsupportedError.new( "Provider '#{self.class.provider_name}' does not support MCP transport " \ "'#{server.transport}' (server: '#{server.name}'). " \ "Supported transports: #{supported.join(", ")}", provider: self.class.provider_name ) end end |