legion-llm 0.8.27 → 0.8.28

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2cccf9351fd9f4db59b1548197bf7b78c5947e85183535e86ede5c3359d71b89
4
- data.tar.gz: 14e3a1b5b6648bea618941f84473e63aeccee7edc9520366d09dde8d27b00a7b
3
+ metadata.gz: 523afac32d76644a92db4f6af5228c9ff9856521ccffb7cff0a0e8194570a432
4
+ data.tar.gz: b58073ec104d42eb18436fd708a33bea881931386dfab1e578f0570d891a6f55
5
5
  SHA512:
6
- metadata.gz: 31ec279fcb498e5cc3308bcefcb6adc94915c36867967fd08aa3f0422d4c583f83bc0ace1db9a47ecd45371a0ce0c82542fea7015c1474f5b7386409d789e5e0
7
- data.tar.gz: '083bd8e581399a574b313a31784eacca4424fadb131f82cd17a5a7e840420da14ec3e5f589efa88b7be6be8a76916439c88bbf936a6d13c9a9435bd8fd04245c'
6
+ metadata.gz: 205d3a1ef6f1c9e8712bc61e2d88382b88a91560343ab6be7e5c863f2b839ea3d384f5a5642240f7a62fed73ed96aff7b653855c15c1cb5517a43d342306a54b
7
+ data.tar.gz: 8847c3be8580a5c1c62bd61b83c72ef53db974b19a8567fd102827b748e9113bfc3dc0af7aa14e23d25b524453b2465dd8053e55f64ee2798f9ae9b387cac264
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Legion LLM Changelog
2
2
 
3
+ ## [0.8.28] - 2026-04-24
4
+
5
+ ### Fixed
6
+ - Model/provider mismatch when clients send a model name (e.g., `qwen3.5:latest`) without an explicit provider. The fallback paths blindly paired it with `default_provider` (typically `bedrock`), causing `RubyLLM::ModelNotFoundError`. Now infers the correct provider from model naming patterns before falling back to the global default.
7
+ - `arbitrage_fallback` hardcoded `:cloud` tier and `:bedrock` provider when inference failed. Now uses `PROVIDER_TIER` to resolve the correct tier for the inferred provider.
8
+
9
+ ### Added
10
+ - `Router.infer_provider_for_model(model)` — public method that maps model naming patterns to providers. Recognizes Ollama-style models (`:` or `/` in name), Bedrock (`us.*`), OpenAI (`gpt-*`, `o1-*`/`o3-*`/`o4-*`), Anthropic (`claude-*`), and Gemini (`gemini-*`).
11
+
3
12
  ## [0.8.27] - 2026-04-24
4
13
 
5
14
  ### Fixed
@@ -328,7 +328,9 @@ module Legion
328
328
  end
329
329
  end
330
330
 
331
- @resolved_provider = provider || Legion::LLM.settings[:default_provider]
331
+ @resolved_provider = provider ||
332
+ (model && Router.infer_provider_for_model(model)) ||
333
+ Legion::LLM.settings[:default_provider]
332
334
  @resolved_model = model || Legion::LLM.settings[:default_model]
333
335
 
334
336
  log.info "[llm][inference] resolved provider=#{@resolved_provider} model=#{@resolved_model}"
@@ -846,6 +848,8 @@ module Legion
846
848
  duration_ms = started_at ? ((finished_at - started_at) * 1000).round : nil
847
849
 
848
850
  result_str = (raw.is_a?(String) ? raw : raw.to_s)
851
+ result_str = result_str.encode('UTF-8', invalid: :replace, undef: :replace, replace: '�') unless result_str.valid_encoding?
852
+ result_str = result_str.delete("\x00")
849
853
  is_error = raw.is_a?(Hash) && (raw[:error] || raw['error']) ? true : false
850
854
 
851
855
  @pending_tool_history_mutex.synchronize do
@@ -496,7 +496,8 @@ module Legion
496
496
  end
497
497
 
498
498
  model ||= Legion::LLM.settings[:default_model]
499
- provider ||= Legion::LLM.settings[:default_provider]
499
+ provider ||= (model && Router.infer_provider_for_model(model)) ||
500
+ Legion::LLM.settings[:default_provider]
500
501
 
501
502
  opts = {}
502
503
  opts[:model] = model if model
@@ -18,7 +18,22 @@ module Legion
18
18
  gemini: :cloud, azure: :cloud, ollama: :local, vllm: :local }.freeze
19
19
  PROVIDER_ORDER = %i[ollama vllm bedrock azure gemini anthropic openai].freeze
20
20
 
21
+ OLLAMA_MODEL_PATTERN = %r{[:/]}
22
+
21
23
  class << self
24
+ def infer_provider_for_model(model)
25
+ return nil if model.nil? || model.to_s.empty?
26
+
27
+ model_s = model.to_s
28
+ return :bedrock if model_s.start_with?('us.')
29
+ return :openai if model_s.match?(/\Agpt-|\Ao[134]-/)
30
+ return :anthropic if model_s.start_with?('claude-')
31
+ return :gemini if model_s.start_with?('gemini-')
32
+ return :ollama if model_s.match?(OLLAMA_MODEL_PATTERN)
33
+
34
+ nil
35
+ end
36
+
22
37
  # Resolve an LLM routing intent to a tier/provider/model decision.
23
38
  #
24
39
  # @param intent [Hash, nil] routing intent (capability, privacy, etc.)
@@ -95,18 +110,12 @@ module Legion
95
110
  model = Arbitrage.cheapest_for(capability: capability)
96
111
  return nil unless model
97
112
 
98
- provider = Arbitrage.cost_table[model] ? infer_provider(model) : nil
99
- log.debug("Router: arbitrage fallback selected model=#{model}")
100
- Resolution.new(tier: :cloud, provider: provider || :bedrock, model: model, rule: 'arbitrage_fallback')
101
- end
102
-
103
- def infer_provider(model)
104
- return :ollama if model.include?('llama')
105
- return :bedrock if model.start_with?('us.')
106
- return :openai if model.start_with?('gpt')
107
- return :google if model.start_with?('gemini')
113
+ provider = infer_provider_for_model(model)
114
+ return nil unless provider
108
115
 
109
- :anthropic if model.start_with?('claude')
116
+ tier = PROVIDER_TIER.fetch(provider, :cloud)
117
+ log.debug("Router: arbitrage fallback selected model=#{model} provider=#{provider} tier=#{tier}")
118
+ Resolution.new(tier: tier, provider: provider, model: model, rule: 'arbitrage_fallback')
110
119
  end
111
120
 
112
121
  def explicit_resolution(tier, provider, model)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module LLM
5
- VERSION = '0.8.27'
5
+ VERSION = '0.8.28'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.27
4
+ version: 0.8.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity