Module: Aidp::Providers::Adapter

Included in:
Base
Defined in:
lib/aidp/providers/adapter.rb

Overview

ProviderAdapter defines the standardized interface that all provider implementations must conform to. This ensures consistent behavior across different AI model providers while allowing for provider-specific implementations.

Design Philosophy:

  • Adapters are stateless; delegate throttling, retries, and escalation to coordinator

  • Store provider-specific regex matchers adjacent to adapters for maintainability

  • Single semantic flags map to provider-specific equivalents

Instance Method Summary collapse

Instance Method Details

#available?Boolean

Check if the provider is available on this system

Returns:

  • (Boolean)

    true if provider CLI or API is accessible



55
56
57
# File 'lib/aidp/providers/adapter.rb', line 55

def available?
  true
end

#capabilitiesHash

Declare provider capabilities

Examples:

{
  reasoning_tiers: ["mini", "standard", "thinking"],
  context_window: 200_000,
  supports_json_mode: true,
  supports_tool_use: true,
  supports_vision: false,
  supports_file_upload: true
}

Returns:

  • (Hash)

    capabilities hash with feature flags



70
71
72
73
74
75
76
77
78
79
# File 'lib/aidp/providers/adapter.rb', line 70

def capabilities
  {
    reasoning_tiers: [],
    context_window: 100_000,
    supports_json_mode: false,
    supports_tool_use: false,
    supports_vision: false,
    supports_file_upload: false
  }
end

#classify_error(error) ⇒ Symbol

Classify an error into the standardized error taxonomy

Parameters:

  • error (StandardError)

    the error to classify

Returns:

  • (Symbol)

    error category (:rate_limited, :auth_expired, :quota_exceeded, :transient, :permanent)



132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/aidp/providers/adapter.rb', line 132

def classify_error(error)
  message = error.message.to_s

  # First check provider-specific patterns
  error_patterns.each do |category, patterns|
    patterns.each do |pattern|
      return category if message.match?(pattern)
    end
  end

  # Fall back to ErrorTaxonomy for classification
  require_relative "error_taxonomy"
  Aidp::Providers::ErrorTaxonomy.classify_message(message)
end

#dangerous_mode=(enabled) ⇒ void

This method returns an undefined value.

Enable or disable dangerous mode

Parameters:

  • enabled (Boolean)

    whether to enable dangerous mode



109
110
111
# File 'lib/aidp/providers/adapter.rb', line 109

def dangerous_mode=(enabled)
  @dangerous_mode_enabled = enabled
end

#dangerous_mode_enabled?Boolean

Check if dangerous mode is currently enabled

Returns:

  • (Boolean)

    true if dangerous mode is active



102
103
104
# File 'lib/aidp/providers/adapter.rb', line 102

def dangerous_mode_enabled?
  @dangerous_mode_enabled ||= false
end

#dangerous_mode_flagsArray<String>

Get the provider-specific flag(s) for enabling dangerous mode Maps the semantic ‘dangerous: true` flag to provider-specific equivalents

Examples:

Anthropic

["--dangerously-skip-permissions"]

Gemini (hypothetical)

["--yolo"]

Returns:

  • (Array<String>)

    provider-specific CLI flags



96
97
98
# File 'lib/aidp/providers/adapter.rb', line 96

def dangerous_mode_flags
  []
end

#display_nameString

Human-friendly display name for UI

Returns:

  • (String)

    display name (e.g., “Anthropic Claude”, “Cursor AI”)



26
27
28
# File 'lib/aidp/providers/adapter.rb', line 26

def display_name
  name
end

#error_metadata(error) ⇒ Hash

Get normalized error metadata

Parameters:

  • error (StandardError)

    the error to process

Returns:

  • (Hash)

    normalized error information



150
151
152
153
154
155
156
157
158
159
# File 'lib/aidp/providers/adapter.rb', line 150

def (error)
  {
    provider: name,
    error_category: classify_error(error),
    error_class: error.class.name,
    message: redact_secrets(error.message),
    timestamp: Time.now.iso8601,
    retryable: retryable_error?(error)
  }
end

#error_patternsHash<Symbol, Array<Regexp>>

Get error classification regex patterns for this provider

Examples:

{
  rate_limited: [/rate.?limit/i, /quota.*exceeded/i],
  auth_expired: [/authentication.*failed/i, /invalid.*api.*key/i],
  quota_exceeded: [/quota.*exceeded/i, /usage.*limit/i],
  transient: [/timeout/i, /connection.*reset/i, /temporary.*error/i],
  permanent: [/invalid.*model/i, /unsupported.*operation/i]
}

Returns:

  • (Hash<Symbol, Array<Regexp>>)

    mapping of error categories to regex patterns



125
126
127
# File 'lib/aidp/providers/adapter.rb', line 125

def error_patterns
  {}
end

#fetch_mcp_serversArray<Hash>

Fetch MCP servers configured for this provider

Returns:

  • (Array<Hash>)

    array of MCP server configurations



49
50
51
# File 'lib/aidp/providers/adapter.rb', line 49

def fetch_mcp_servers
  []
end

#health_statusHash

Check provider health

Returns:

  • (Hash)

    health status information



229
230
231
232
233
234
235
236
# File 'lib/aidp/providers/adapter.rb', line 229

def health_status
  {
    provider: name,
    available: available?,
    healthy: available?,
    timestamp: Time.now.iso8601
  }
end

#logging_metadataHash

Get logging metadata for this provider

Returns:

  • (Hash)

    metadata for structured logging



173
174
175
176
177
178
179
180
181
# File 'lib/aidp/providers/adapter.rb', line 173

def 
  {
    provider: name,
    display_name: display_name,
    supports_mcp: supports_mcp?,
    available: available?,
    dangerous_mode: dangerous_mode_enabled?
  }
end

#nameString

Provider identifier (e.g., “anthropic”, “cursor”, “gemini”)

Returns:

  • (String)

    unique lowercase identifier for this provider

Raises:

  • (NotImplementedError)


20
21
22
# File 'lib/aidp/providers/adapter.rb', line 20

def name
  raise NotImplementedError, "#{self.class} must implement #name"
end

#redact_secrets(message) ⇒ String

Redact secrets from log messages

Parameters:

  • message (String)

    message potentially containing secrets

Returns:

  • (String)

    message with secrets redacted



186
187
188
189
190
191
192
193
# File 'lib/aidp/providers/adapter.rb', line 186

def redact_secrets(message)
  # Redact common secret patterns
  message = message.gsub(/api[_-]?key[:\s=]+[^\s&]+/i, "api_key=[REDACTED]")
  message = message.gsub(/token[:\s=]+[^\s&]+/i, "token=[REDACTED]")
  message = message.gsub(/password[:\s=]+[^\s&]+/i, "password=[REDACTED]")
  message = message.gsub(/bearer\s+[^\s&]+/i, "bearer [REDACTED]")
  message.gsub(/sk-[a-zA-Z0-9_-]{20,}/i, "sk-[REDACTED]")
end

#retryable_error?(error) ⇒ Boolean

Check if an error is retryable

Parameters:

  • error (StandardError)

    the error to check

Returns:

  • (Boolean)

    true if the error should be retried



164
165
166
167
# File 'lib/aidp/providers/adapter.rb', line 164

def retryable_error?(error)
  category = classify_error(error)
  [:transient].include?(category)
end

#send_message(prompt:, session: nil, **options) ⇒ Hash, String

Send a message to the provider and get a response

Parameters:

  • prompt (String)

    the prompt to send

  • session (String, nil) (defaults to: nil)

    optional session identifier for context

  • options (Hash)

    additional options for the request

Returns:

  • (Hash, String)

    provider response

Raises:

  • (NotImplementedError)


35
36
37
# File 'lib/aidp/providers/adapter.rb', line 35

def send_message(prompt:, session: nil, **options)
  raise NotImplementedError, "#{self.class} must implement #send_message"
end

#supports_dangerous_mode?Boolean

Check if the provider supports dangerous/elevated permissions mode

Returns:

  • (Boolean)

    true if dangerous mode is supported



85
86
87
# File 'lib/aidp/providers/adapter.rb', line 85

def supports_dangerous_mode?
  false
end

#supports_mcp?Boolean

Check if the provider supports Model Context Protocol

Returns:

  • (Boolean)

    true if MCP is supported



43
44
45
# File 'lib/aidp/providers/adapter.rb', line 43

def supports_mcp?
  false
end

#validate_config(config) ⇒ Hash

Validate provider configuration

Parameters:

  • config (Hash)

    configuration to validate

Returns:

  • (Hash)

    validation result with :valid, :errors, :warnings keys



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/aidp/providers/adapter.rb', line 200

def validate_config(config)
  errors = []
  warnings = []

  # Validate required fields
  unless config[:type]
    errors << "Provider type is required"
  end

  unless ["usage_based", "subscription", "passthrough"].include?(config[:type])
    errors << "Provider type must be one of: usage_based, subscription, passthrough"
  end

  # Validate models if present
  if config[:models] && !config[:models].is_a?(Array)
    errors << "Models must be an array"
  end

  {
    valid: errors.empty?,
    errors: errors,
    warnings: warnings
  }
end