lex-llm 0.6.16 → 0.7.2

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/lex-llm.gemspec +1 -1
  4. data/lib/legion/extensions/llm/auto_registration.rb +3 -0
  5. data/lib/legion/extensions/llm/canonical/message.rb +5 -1
  6. data/lib/legion/extensions/llm/fleet/protocol.rb +12 -0
  7. data/lib/legion/extensions/llm/fleet/provider_responder.rb +40 -5
  8. data/lib/legion/extensions/llm/fleet/token_validator.rb +15 -0
  9. data/lib/legion/extensions/llm/fleet/worker_execution.rb +166 -2
  10. data/lib/legion/extensions/llm/inventory/callable_handle.rb +144 -0
  11. data/lib/legion/extensions/llm/inventory/errors.rb +34 -0
  12. data/lib/legion/extensions/llm/inventory/evidence.rb +134 -0
  13. data/lib/legion/extensions/llm/inventory/identity.rb +141 -0
  14. data/lib/legion/extensions/llm/inventory/immutable_value.rb +96 -0
  15. data/lib/legion/extensions/llm/inventory/probe_coordinator.rb +117 -0
  16. data/lib/legion/extensions/llm/inventory/probe_token.rb +146 -0
  17. data/lib/legion/extensions/llm/inventory/publisher.rb +116 -0
  18. data/lib/legion/extensions/llm/inventory/records.rb +599 -0
  19. data/lib/legion/extensions/llm/inventory/registry.rb +636 -0
  20. data/lib/legion/extensions/llm/inventory/scoped_refresher.rb +206 -0
  21. data/lib/legion/extensions/llm/inventory/snapshot.rb +91 -0
  22. data/lib/legion/extensions/llm/provider.rb +55 -29
  23. data/lib/legion/extensions/llm/routing/lane_key.rb +4 -0
  24. data/lib/legion/extensions/llm/routing/model_offering.rb +3 -0
  25. data/lib/legion/extensions/llm/routing/offering_registry.rb +2 -0
  26. data/lib/legion/extensions/llm/routing/provider_outcome.rb +54 -0
  27. data/lib/legion/extensions/llm/routing/records.rb +288 -0
  28. data/lib/legion/extensions/llm/settings_cascade.rb +120 -0
  29. data/lib/legion/extensions/llm/taxonomies.rb +90 -0
  30. data/lib/legion/extensions/llm/transport/messages/fleet_error.rb +3 -1
  31. data/lib/legion/extensions/llm/transport/messages/fleet_response.rb +3 -1
  32. data/lib/legion/extensions/llm/version.rb +1 -1
  33. data/lib/legion/extensions/llm.rb +18 -0
  34. data/spec/legion/extensions/llm/canonical/message_spec.rb +24 -0
  35. data/spec/legion/extensions/llm/conformance/fixtures/canonical_params_mapping_request.json +1 -0
  36. data/spec/legion/extensions/llm/conformance/fixtures/canonical_simple_text_request.json +1 -0
  37. data/spec/legion/extensions/llm/conformance/fixtures/canonical_system_prompt_request.json +1 -0
  38. data/spec/legion/extensions/llm/conformance/fixtures/canonical_thinking_request.json +1 -0
  39. data/spec/legion/extensions/llm/conformance/fixtures/canonical_tool_results_continuation_request.json +1 -0
  40. data/spec/legion/extensions/llm/conformance/fixtures/canonical_tools_request.json +1 -0
  41. data/spec/legion/extensions/llm/conformance/fixtures/ssot_identity_vectors.json +84 -0
  42. data/spec/legion/extensions/llm/conformance/ssot_provider_conformance_spec.rb +13 -0
  43. data/spec/legion/extensions/llm/conformance/ssot_provider_examples.rb +196 -0
  44. data/spec/legion/extensions/llm/fleet/exact_offering_spec.rb +179 -0
  45. data/spec/legion/extensions/llm/fleet/provider_responder_spec.rb +26 -0
  46. data/spec/legion/extensions/llm/fleet_messages_spec.rb +14 -0
  47. data/spec/legion/extensions/llm/inventory/boot_spec.rb +80 -0
  48. data/spec/legion/extensions/llm/inventory/callable_handle_spec.rb +127 -0
  49. data/spec/legion/extensions/llm/inventory/evidence_spec.rb +138 -0
  50. data/spec/legion/extensions/llm/inventory/identity_spec.rb +280 -0
  51. data/spec/legion/extensions/llm/inventory/immutable_value_spec.rb +132 -0
  52. data/spec/legion/extensions/llm/inventory/probe_coordinator_spec.rb +107 -0
  53. data/spec/legion/extensions/llm/inventory/probe_token_spec.rb +107 -0
  54. data/spec/legion/extensions/llm/inventory/publisher_spec.rb +144 -0
  55. data/spec/legion/extensions/llm/inventory/records_spec.rb +300 -0
  56. data/spec/legion/extensions/llm/inventory/registry_activation_spec.rb +140 -0
  57. data/spec/legion/extensions/llm/inventory/registry_availability_spec.rb +152 -0
  58. data/spec/legion/extensions/llm/inventory/registry_callable_spec.rb +63 -0
  59. data/spec/legion/extensions/llm/inventory/registry_replacement_spec.rb +69 -0
  60. data/spec/legion/extensions/llm/inventory/scoped_refresher_spec.rb +128 -0
  61. data/spec/legion/extensions/llm/inventory/snapshot_spec.rb +109 -0
  62. data/spec/legion/extensions/llm/provider_contract_spec.rb +20 -0
  63. data/spec/legion/extensions/llm/provider_spec.rb +83 -0
  64. data/spec/legion/extensions/llm/routing/provider_outcome_spec.rb +73 -0
  65. data/spec/legion/extensions/llm/routing/records_spec.rb +173 -0
  66. data/spec/legion/extensions/llm/settings_cascade_spec.rb +208 -0
  67. data/spec/legion/extensions/llm/taxonomies_spec.rb +51 -0
  68. data/spec/support/fake_llm_provider.rb +11 -0
  69. data/spec/support/fake_ssot_harness.rb +137 -0
  70. data/spec/support/ssot_registry_helpers.rb +75 -0
  71. metadata +40 -3
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/llm/inventory/records'
4
+ require 'legion/extensions/llm/inventory/evidence'
5
+ require 'legion/extensions/llm/routing/provider_outcome'
6
+ require 'legion/extensions/llm/taxonomies'
7
+
8
+ module SpecSupport
9
+ # A distinct fake provider callable. Records inference calls and readiness
10
+ # probes separately so the conformance examples can prove that safe readiness
11
+ # performs no inference.
12
+ class FakeSsotCallable
13
+ attr_reader :inference_calls, :readiness_probes, :disconnects
14
+
15
+ def initialize
16
+ @inference_calls = 0
17
+ @readiness_probes = 0
18
+ @disconnects = 0
19
+ end
20
+
21
+ def disconnect
22
+ @disconnects += 1
23
+ end
24
+
25
+ # Non-inference control-plane readiness (e.g. a model-list/status call).
26
+ def control_plane_ready?
27
+ @readiness_probes += 1
28
+ true
29
+ end
30
+
31
+ def chat(messages:, model:, **rest)
32
+ @inference_calls += 1
33
+ { content: "fake chat #{model} #{messages.size} #{rest.size}" }
34
+ end
35
+
36
+ # Runtime error normalization on the provider callable itself, mirroring the
37
+ # conservative Provider#normalize_dispatch_error base contract. Raw 503/529,
38
+ # ServiceUnavailableError, ServerError, and unknown errors return
39
+ # :provider_error, never :instance_unavailable. The reason is the bounded
40
+ # exception class name only.
41
+ def normalize_dispatch_error(error:)
42
+ llm = Legion::Extensions::Llm
43
+ kind = case error
44
+ when llm::OverloadedError then :overloaded
45
+ when llm::RateLimitError then :rate_limited
46
+ when llm::UnauthorizedError then :authentication
47
+ when llm::ForbiddenError then :authorization
48
+ when llm::BadRequestError then :invalid_request
49
+ else :provider_error
50
+ end
51
+ reason = error.class.name
52
+ reason = 'UnknownError' if reason.nil? || reason.empty?
53
+ llm::Routing::ProviderOutcome.new(kind: kind, reason: reason)
54
+ end
55
+ end
56
+
57
+ # Phase 1 fake implementation of the shared SSOT v3 provider-harness interface
58
+ # consumed by 'an SSOT v3 provider adapter'. See phase-1-lex-llm-additive.md
59
+ # Task 12.
60
+ class FakeSsotHarness
61
+ def provider_family
62
+ :vllm
63
+ end
64
+
65
+ def instance_configs
66
+ [{ instance_id: 'h200', model: 'gemma4' }, { instance_id: 'helios1', model: 'gemma4' }].freeze
67
+ end
68
+
69
+ def instance_id(instance_config:)
70
+ instance_config.fetch(:instance_id)
71
+ end
72
+
73
+ def build_callable(instance_config:)
74
+ _ = instance_config
75
+ FakeSsotCallable.new
76
+ end
77
+
78
+ def build_offering_drafts(instance_config:, callable:, tier: :local)
79
+ _ = callable
80
+ [offering_draft(instance_config.fetch(:model), tier)].freeze
81
+ end
82
+
83
+ def safe_readiness(instance_config:, callable:)
84
+ _ = instance_config
85
+ callable.control_plane_ready?
86
+ Legion::Extensions::Llm::Inventory::ReadinessResult.new(ready: true, reason: 'control-plane model list ok')
87
+ end
88
+
89
+ def inference_call_count(callable:)
90
+ callable.inference_calls
91
+ end
92
+
93
+ def normalize_dispatch_error(error:)
94
+ routing = Legion::Extensions::Llm::Routing
95
+ case error[:kind]
96
+ when :instance_unavailable then routing::ProviderOutcome.new(kind: :instance_unavailable, reason: 'explicit service unavailable')
97
+ when :overloaded then routing::ProviderOutcome.new(kind: :overloaded, reason: 'HTTP 503 overloaded')
98
+ when :model_not_ready then routing::ProviderOutcome.new(kind: :model_not_ready, reason: 'HTTP 503 model loading')
99
+ else routing::ProviderOutcome.new(kind: :provider_error, reason: 'unclassified provider error')
100
+ end
101
+ end
102
+
103
+ def instance_unavailable_error
104
+ { kind: :instance_unavailable, http_status: 503 }
105
+ end
106
+
107
+ def overloaded_error
108
+ { kind: :overloaded, http_status: 503 }
109
+ end
110
+
111
+ def model_not_ready_error
112
+ { kind: :model_not_ready, http_status: 503 }
113
+ end
114
+
115
+ private
116
+
117
+ def offering_draft(model, tier)
118
+ Legion::Extensions::Llm::Inventory::OfferingDraft.new(
119
+ provider_native_key: model, model: model, tier: tier, operation_evidence: operation_evidence,
120
+ context_evidence: unknown_value, max_output_evidence: unknown_value,
121
+ embedding_dimensions_evidence: unknown_value, model_revision_evidence: unknown_value,
122
+ tokenizer_evidence: unknown_value, publication_source: :provider_catalog
123
+ )
124
+ end
125
+
126
+ def operation_evidence
127
+ Legion::Extensions::Llm::Taxonomies::OPERATIONS.to_h do |op|
128
+ status, source = op == :chat ? %i[supported provider_implementation] : %i[unknown absent]
129
+ [op, Legion::Extensions::Llm::Inventory::OperationEvidence.new(operation: op, status: status, source: source)]
130
+ end
131
+ end
132
+
133
+ def unknown_value
134
+ Legion::Extensions::Llm::Inventory::ValueEvidence.new(status: :unknown, source: :absent)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/llm/inventory/registry'
4
+ require 'legion/extensions/llm/inventory/probe_coordinator'
5
+
6
+ # Shared builders for the SSOT v3 registry specs. Provides fake callables,
7
+ # instance keys, offering drafts, and a full claim -> probe -> activate helper so
8
+ # each registry spec exercises the public API rather than internal state.
9
+ module SsotRegistryHelpers
10
+ module_function
11
+
12
+ def registry
13
+ Legion::Extensions::Llm::Inventory::Registry
14
+ end
15
+
16
+ def instance_key(family: 'vllm', instance: 'h200')
17
+ Legion::Extensions::Llm::Inventory::Identity::InstanceKey.new(provider_family: family, instance_id: instance)
18
+ end
19
+
20
+ def fake_callable
21
+ Class.new do
22
+ attr_reader :disconnect_count
23
+
24
+ def initialize
25
+ @disconnect_count = 0
26
+ end
27
+
28
+ def disconnect
29
+ @disconnect_count += 1
30
+ end
31
+
32
+ def chat(**)
33
+ { ok: true }
34
+ end
35
+ end.new
36
+ end
37
+
38
+ def probe_coordinator(key, enqueue: ->(**) { true })
39
+ Legion::Extensions::Llm::Inventory::ProbeCoordinator.new(instance_key: key, enqueue: enqueue)
40
+ end
41
+
42
+ def unknown_value
43
+ Legion::Extensions::Llm::Inventory::ValueEvidence.new(status: :unknown, source: :absent)
44
+ end
45
+
46
+ def operation_evidence(supported: %i[chat])
47
+ Legion::Extensions::Llm::Taxonomies::OPERATIONS.to_h do |op|
48
+ status, source = supported.include?(op) ? %i[supported provider_implementation] : %i[unknown absent]
49
+ [op, Legion::Extensions::Llm::Inventory::OperationEvidence.new(operation: op, status: status, source: source)]
50
+ end
51
+ end
52
+
53
+ def drafts(model: 'gemma4', native: 'gemma4', supported: %i[chat], tier: :local)
54
+ [
55
+ Legion::Extensions::Llm::Inventory::OfferingDraft.new(
56
+ provider_native_key: native, model: model, tier: tier,
57
+ operation_evidence: operation_evidence(supported: supported),
58
+ context_evidence: unknown_value, max_output_evidence: unknown_value,
59
+ embedding_dimensions_evidence: unknown_value, model_revision_evidence: unknown_value,
60
+ tokenizer_evidence: unknown_value, publication_source: :provider_catalog
61
+ )
62
+ ]
63
+ end
64
+
65
+ # Full claim -> probe -> activation lifecycle; returns the publisher token.
66
+ # Extra keyword args (model:, native:, supported:, tier:) are forwarded to #drafts.
67
+ def claim_and_activate(key:, callable:, coordinator:, sequence: 0, **draft_opts)
68
+ token = registry.claim_instance(instance_key: key, callable: callable, probe_request_handle: coordinator)
69
+ probe = registry.readiness_probe_started(instance_key: key, publisher_token: token)
70
+ registry.activate_instance_snapshot(
71
+ publisher_token: token, instance_key: key, offerings: drafts(**draft_opts), sequence: sequence, probe_token: probe
72
+ )
73
+ token
74
+ end
75
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.16
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - LegionIO
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - ">="
186
186
  - !ruby/object:Gem::Version
187
- version: 1.3.14
187
+ version: 1.4.2
188
188
  type: :runtime
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - ">="
193
193
  - !ruby/object:Gem::Version
194
- version: 1.3.14
194
+ version: 1.4.2
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: legion-transport
197
197
  requirement: !ruby/object:Gem::Requirement
@@ -295,8 +295,19 @@ files:
295
295
  - lib/legion/extensions/llm/fleet/token_validator.rb
296
296
  - lib/legion/extensions/llm/fleet/worker_execution.rb
297
297
  - lib/legion/extensions/llm/image.rb
298
+ - lib/legion/extensions/llm/inventory/callable_handle.rb
298
299
  - lib/legion/extensions/llm/inventory/capabilities.rb
300
+ - lib/legion/extensions/llm/inventory/errors.rb
301
+ - lib/legion/extensions/llm/inventory/evidence.rb
302
+ - lib/legion/extensions/llm/inventory/identity.rb
303
+ - lib/legion/extensions/llm/inventory/immutable_value.rb
304
+ - lib/legion/extensions/llm/inventory/probe_coordinator.rb
305
+ - lib/legion/extensions/llm/inventory/probe_token.rb
306
+ - lib/legion/extensions/llm/inventory/publisher.rb
307
+ - lib/legion/extensions/llm/inventory/records.rb
308
+ - lib/legion/extensions/llm/inventory/registry.rb
299
309
  - lib/legion/extensions/llm/inventory/scoped_refresher.rb
310
+ - lib/legion/extensions/llm/inventory/snapshot.rb
300
311
  - lib/legion/extensions/llm/message.rb
301
312
  - lib/legion/extensions/llm/mime_type.rb
302
313
  - lib/legion/extensions/llm/model.rb
@@ -323,7 +334,10 @@ files:
323
334
  - lib/legion/extensions/llm/routing/lane_key.rb
324
335
  - lib/legion/extensions/llm/routing/model_offering.rb
325
336
  - lib/legion/extensions/llm/routing/offering_registry.rb
337
+ - lib/legion/extensions/llm/routing/provider_outcome.rb
338
+ - lib/legion/extensions/llm/routing/records.rb
326
339
  - lib/legion/extensions/llm/routing/registry_event.rb
340
+ - lib/legion/extensions/llm/settings_cascade.rb
327
341
  - lib/legion/extensions/llm/stop_reason_mapping.rb
328
342
  - lib/legion/extensions/llm/stream_accumulator.rb
329
343
  - lib/legion/extensions/llm/streaming.rb
@@ -393,8 +407,11 @@ files:
393
407
  - spec/legion/extensions/llm/conformance/fixtures/canonical_tool_results_continuation_request.json
394
408
  - spec/legion/extensions/llm/conformance/fixtures/canonical_tool_use_response.json
395
409
  - spec/legion/extensions/llm/conformance/fixtures/canonical_tools_request.json
410
+ - spec/legion/extensions/llm/conformance/fixtures/ssot_identity_vectors.json
396
411
  - spec/legion/extensions/llm/conformance/provider_tool_rendering_examples.rb
397
412
  - spec/legion/extensions/llm/conformance/provider_translator_examples.rb
413
+ - spec/legion/extensions/llm/conformance/ssot_provider_conformance_spec.rb
414
+ - spec/legion/extensions/llm/conformance/ssot_provider_examples.rb
398
415
  - spec/legion/extensions/llm/connection_close_spec.rb
399
416
  - spec/legion/extensions/llm/connection_logging_spec.rb
400
417
  - spec/legion/extensions/llm/connection_retry_spec.rb
@@ -402,13 +419,28 @@ files:
402
419
  - spec/legion/extensions/llm/credential_sources_spec.rb
403
420
  - spec/legion/extensions/llm/error_middleware_spec.rb
404
421
  - spec/legion/extensions/llm/error_spec.rb
422
+ - spec/legion/extensions/llm/fleet/exact_offering_spec.rb
405
423
  - spec/legion/extensions/llm/fleet/provider_responder_spec.rb
406
424
  - spec/legion/extensions/llm/fleet/token_validator_spec.rb
407
425
  - spec/legion/extensions/llm/fleet/worker_execution_spec.rb
408
426
  - spec/legion/extensions/llm/fleet_messages_spec.rb
409
427
  - spec/legion/extensions/llm/gemspec_spec.rb
428
+ - spec/legion/extensions/llm/inventory/boot_spec.rb
429
+ - spec/legion/extensions/llm/inventory/callable_handle_spec.rb
410
430
  - spec/legion/extensions/llm/inventory/capabilities_spec.rb
431
+ - spec/legion/extensions/llm/inventory/evidence_spec.rb
432
+ - spec/legion/extensions/llm/inventory/identity_spec.rb
433
+ - spec/legion/extensions/llm/inventory/immutable_value_spec.rb
434
+ - spec/legion/extensions/llm/inventory/probe_coordinator_spec.rb
435
+ - spec/legion/extensions/llm/inventory/probe_token_spec.rb
436
+ - spec/legion/extensions/llm/inventory/publisher_spec.rb
437
+ - spec/legion/extensions/llm/inventory/records_spec.rb
438
+ - spec/legion/extensions/llm/inventory/registry_activation_spec.rb
439
+ - spec/legion/extensions/llm/inventory/registry_availability_spec.rb
440
+ - spec/legion/extensions/llm/inventory/registry_callable_spec.rb
441
+ - spec/legion/extensions/llm/inventory/registry_replacement_spec.rb
411
442
  - spec/legion/extensions/llm/inventory/scoped_refresher_spec.rb
443
+ - spec/legion/extensions/llm/inventory/snapshot_spec.rb
412
444
  - spec/legion/extensions/llm/message_spec.rb
413
445
  - spec/legion/extensions/llm/model/info_spec.rb
414
446
  - spec/legion/extensions/llm/models_spec.rb
@@ -423,7 +455,10 @@ files:
423
455
  - spec/legion/extensions/llm/responses/thinking_extractor_spec.rb
424
456
  - spec/legion/extensions/llm/routing/model_offering_spec.rb
425
457
  - spec/legion/extensions/llm/routing/offering_registry_spec.rb
458
+ - spec/legion/extensions/llm/routing/provider_outcome_spec.rb
459
+ - spec/legion/extensions/llm/routing/records_spec.rb
426
460
  - spec/legion/extensions/llm/routing/registry_event_spec.rb
461
+ - spec/legion/extensions/llm/settings_cascade_spec.rb
427
462
  - spec/legion/extensions/llm/stop_reason_mapping_spec.rb
428
463
  - spec/legion/extensions/llm/stream_accumulator_spec.rb
429
464
  - spec/legion/extensions/llm/streaming_spec.rb
@@ -436,9 +471,11 @@ files:
436
471
  - spec/legion/extensions/llm_root_spec.rb
437
472
  - spec/spec_helper.rb
438
473
  - spec/support/fake_llm_provider.rb
474
+ - spec/support/fake_ssot_harness.rb
439
475
  - spec/support/llm_configuration.rb
440
476
  - spec/support/rspec_configuration.rb
441
477
  - spec/support/simplecov_configuration.rb
478
+ - spec/support/ssot_registry_helpers.rb
442
479
  homepage: https://github.com/LegionIO/lex-llm
443
480
  licenses:
444
481
  - MIT