29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# File 'lib/ruby_llm_mesh/router.rb', line 29
def complete(prompt:, providers: nil, fallback: nil, system: nil, model: nil, **options)
raise ArgumentError, "prompt is required" if prompt.nil? || prompt.to_s.strip.empty?
provider_list = Array(providers || @config.default_providers).map(&:to_sym)
raise ArgumentError, "providers list cannot be empty" if provider_list.empty?
use_fallback = fallback.nil? ? @config.fallback : fallback
errors = {}
attempted = 0
provider_list.each_with_index do |provider_name, index|
unless PROVIDER_MAP.key?(provider_name)
errors[provider_name] = ProviderError.new("Unknown provider: #{provider_name}", provider: provider_name)
next
end
unless @circuit_breaker.allow?(provider_name)
errors[provider_name] = CircuitOpenError.new(
"Circuit open for #{provider_name}",
provider: provider_name
)
log(:warn, "Skipping #{provider_name} — circuit open")
next
end
begin
attempted += 1
log(:info, "Routing to #{provider_name}")
provider = PROVIDER_MAP[provider_name].new(@config)
response = provider.complete(prompt: prompt, system: system, model: model, **options)
@circuit_breaker.record_success(provider_name)
return Response.new(
content: response.content,
provider: response.provider,
model: response.model,
usage: response.usage,
raw: response.raw,
latency_ms: response.latency_ms,
fallback_used: index.positive?
)
rescue ProviderError => e
@circuit_breaker.record_failure(provider_name)
errors[provider_name] = e
log(:error, "#{provider_name} failed: #{e.message}")
break unless use_fallback
end
end
raise AllProvidersFailedError, errors
end
|