legion-llm 0.8.24 → 0.8.25

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: 1c7a3d39cf4e2e31494ee7e354680e57ae1c6c1feabe2f39e8707a06701c6a15
4
- data.tar.gz: c8733d7f96801aa19c35458f4e527574815909dd87e89ed80d589bf565a8467c
3
+ metadata.gz: 6b37f926f357a862c036b2e3d4676b579ae505ba6f76b7b7da332ba3aeeed8ac
4
+ data.tar.gz: 59ab5db376ac8be2a45cda642dec1d22c8eda3e76782b9f2cd5689233bcc2063
5
5
  SHA512:
6
- metadata.gz: 37049fdb4a5dc838fecc0d3b6c57e48bbcb72d490b8e8460e04cdd7a19728d82e8c2c3f48ab0bfc059bc2245d56f40f279d79b082e31c7f4c85fa57d86270e42
7
- data.tar.gz: 06ae35daf7458e38b4990c2a230a5f72bffd65bc99bc3dd2bbb16be4ff60aa3665c3b807c3b19637f407fc1e55b6f88148c433d39474411bfb222fe9e07d412c
6
+ metadata.gz: 6994e774e6a0551c720ef0a2c7a5aa4681f324c9e8c09a624ca2e227565cd87ca26f1457ed108967ae3e17593e6a5ee8df7fdf20515aa0ccdd277f2eb16db827
7
+ data.tar.gz: 815d14a99f379c2d655341776507e96be1625582b4fd4989597c4c299a1086be394dbad785b4b33e2d1423419a37ea37392ee617fa75fbb88208dac6d6864bce
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Legion LLM Changelog
2
2
 
3
+ ## [0.8.25] - 2026-04-24
4
+
5
+ ### Fixed
6
+ - `StructuredOutput.generate`, `handle_parse_error`, and `retry_with_instruction` used hash-style access (`result[:content]`, `result[:model]`) on the return value of `chat_single`, but `chat_single` returns a `RubyLLM::Message` object which only supports method access (`.content`, `.model_id`). All four access sites now use `respond_to?` duck-typing so both hash and Message objects work. Visible as `undefined method '[]' for an instance of RubyLLM::Message` in Apollo's `llm_detects_conflict?` and any structured output caller using non-schema-capable models (e.g. ollama/qwen).
7
+ - `Call::Embeddings.generate` crashed with `NoMethodError` on `.size` when `response.vectors` was a flat array (`[0.007, ...]`) instead of nested (`[[0.007, ...]]`). RubyLLM's OpenAI provider unwraps single-input embedding responses. Added `normalize_vectors_first` to detect and handle both flat and nested vector formats before dimension enforcement.
8
+
3
9
  ## [0.8.24] - 2026-04-23
4
10
 
5
11
  ### Fixed
@@ -27,7 +27,8 @@ module Legion
27
27
 
28
28
  response = RubyLLM.embed(text, **build_opts(model, provider, dimensions))
29
29
  emit_embedding_metering(provider: provider, model: model, tokens: response.input_tokens)
30
- vector = apply_dimension_enforcement(response.vectors.first, provider)
30
+ vector = normalize_vectors_first(response.vectors)
31
+ vector = apply_dimension_enforcement(vector, provider)
31
32
  return dimension_error(model, provider, vector) if vector.is_a?(String)
32
33
 
33
34
  { vector: vector, model: model, provider: provider, dimensions: vector&.size || 0, tokens: response.input_tokens }
@@ -101,6 +102,16 @@ module Legion
101
102
  opts
102
103
  end
103
104
 
105
+ def normalize_vectors_first(vectors)
106
+ return nil if vectors.nil? || (vectors.is_a?(Array) && vectors.empty?)
107
+
108
+ first = vectors.first
109
+ return first if first.is_a?(Array)
110
+ return vectors if vectors.is_a?(Array) && vectors.first.is_a?(Numeric)
111
+
112
+ first
113
+ end
114
+
104
115
  def apply_dimension_enforcement(vector, provider)
105
116
  return vector unless enforce_dimension? && vector.is_a?(Array)
106
117
 
@@ -15,8 +15,11 @@ module Legion
15
15
  result = call_with_schema(messages, schema, model, provider: provider, **)
16
16
  log.info "[llm][structured_output] model=#{model} provider=#{provider} valid=true"
17
17
 
18
- parsed = Legion::JSON.load(result[:content])
19
- { data: parsed, raw: result[:content], model: result[:model], valid: true }
18
+ content = result.respond_to?(:content) ? result.content : result[:content]
19
+ raw_model = result.respond_to?(:model_id) ? result.model_id : result[:model]
20
+
21
+ parsed = Legion::JSON.load(content)
22
+ { data: parsed, raw: content, model: raw_model, valid: true }
20
23
  rescue ::JSON::ParserError => e
21
24
  log.warn "[llm][structured_output] model=#{model} provider=#{provider} parse_error=#{e.message}"
22
25
  handle_parse_error(e, messages, schema, model, provider, result, **)
@@ -49,7 +52,8 @@ module Legion
49
52
  if retry_enabled? && attempt < max_retries
50
53
  retry_with_instruction(messages, schema, model, provider: provider, attempt: attempt + 1, **opts)
51
54
  else
52
- { data: nil, error: "JSON parse failed: #{error.message}", raw: result&.dig(:content), valid: false }
55
+ raw = result.respond_to?(:content) ? result&.content : result&.dig(:content)
56
+ { data: nil, error: "JSON parse failed: #{error.message}", raw: raw, valid: false }
53
57
  end
54
58
  end
55
59
 
@@ -60,8 +64,11 @@ module Legion
60
64
  model: model, provider: provider, intent: nil, tier: nil,
61
65
  message: user_content, **opts.except(:attempt))
62
66
 
63
- parsed = Legion::JSON.load(result[:content])
64
- { data: parsed, raw: result[:content], model: result[:model], valid: true, retried: true }
67
+ retry_content = result.respond_to?(:content) ? result.content : result[:content]
68
+ retry_model = result.respond_to?(:model_id) ? result.model_id : result[:model]
69
+
70
+ parsed = Legion::JSON.load(retry_content)
71
+ { data: parsed, raw: retry_content, model: retry_model, valid: true, retried: true }
65
72
  rescue StandardError => e
66
73
  handle_exception(e, level: :warn)
67
74
  { data: nil, error: e.message, valid: false }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module LLM
5
- VERSION = '0.8.24'
5
+ VERSION = '0.8.25'
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.24
4
+ version: 0.8.25
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity