lex-llm 0.7.6 → 0.8.4
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 +4 -4
- data/CHANGELOG.md +139 -0
- data/RULES.md +97 -0
- data/lib/legion/extensions/llm/auto_registration.rb +4 -13
- data/lib/legion/extensions/llm/canonical/chunk.rb +107 -94
- data/lib/legion/extensions/llm/canonical/content_block.rb +65 -75
- data/lib/legion/extensions/llm/canonical/message.rb +53 -69
- data/lib/legion/extensions/llm/canonical/params.rb +58 -34
- data/lib/legion/extensions/llm/canonical/request.rb +68 -56
- data/lib/legion/extensions/llm/canonical/response.rb +62 -78
- data/lib/legion/extensions/llm/canonical/strict.rb +105 -0
- data/lib/legion/extensions/llm/canonical/thinking.rb +36 -84
- data/lib/legion/extensions/llm/canonical/thinking_config.rb +149 -0
- data/lib/legion/extensions/llm/canonical/tool_call.rb +43 -53
- data/lib/legion/extensions/llm/canonical/tool_definition.rb +56 -46
- data/lib/legion/extensions/llm/canonical/tool_schema.rb +15 -22
- data/lib/legion/extensions/llm/canonical/usage.rb +47 -40
- data/lib/legion/extensions/llm/canonical.rb +7 -5
- data/lib/legion/extensions/llm/configuration.rb +40 -10
- data/lib/legion/extensions/llm/connection.rb +8 -30
- data/lib/legion/extensions/llm/credential_sources.rb +32 -49
- data/lib/legion/extensions/llm/discovery/actor.rb +92 -0
- data/lib/legion/extensions/llm/discovery/pipeline.rb +604 -0
- data/lib/legion/extensions/llm/error.rb +0 -14
- data/lib/legion/extensions/llm/fleet/contract_error.rb +15 -0
- data/lib/legion/extensions/llm/fleet/envelope_validation.rb +7 -6
- data/lib/legion/extensions/llm/fleet/fleet_envelope.rb +66 -0
- data/lib/legion/extensions/llm/fleet/protocol.rb +18 -5
- data/lib/legion/extensions/llm/fleet/provider_responder.rb +58 -156
- data/lib/legion/extensions/llm/fleet/token_validator.rb +15 -21
- data/lib/legion/extensions/llm/fleet/worker_execution.rb +107 -154
- data/lib/legion/extensions/llm/inventory/errors.rb +0 -1
- data/lib/legion/extensions/llm/inventory/evidence.rb +1 -1
- data/lib/legion/extensions/llm/inventory/identity.rb +55 -39
- data/lib/legion/extensions/llm/inventory/probe_token.rb +9 -12
- data/lib/legion/extensions/llm/inventory/publisher.rb +15 -53
- data/lib/legion/extensions/llm/inventory/records.rb +36 -100
- data/lib/legion/extensions/llm/inventory/registry.rb +54 -48
- data/lib/legion/extensions/llm/inventory/snapshot.rb +7 -21
- data/lib/legion/extensions/llm/inventory/weight_reconciler.rb +13 -6
- data/lib/legion/extensions/llm/inventory/weight_schema.rb +47 -8
- data/lib/legion/extensions/llm/provider/open_ai_compatible.rb +178 -182
- data/lib/legion/extensions/llm/provider.rb +192 -315
- data/lib/legion/extensions/llm/provider_contract.rb +25 -8
- data/lib/legion/extensions/llm/provider_settings.rb +5 -26
- data/lib/legion/extensions/llm/responses/thinking_extractor.rb +8 -1
- data/lib/legion/extensions/llm/responses/tool_arguments.rb +48 -0
- data/lib/legion/extensions/llm/routing/provider_outcome.rb +25 -0
- data/lib/legion/extensions/llm/routing/records.rb +34 -17
- data/lib/legion/extensions/llm/stream_accumulator.rb +186 -270
- data/lib/legion/extensions/llm/streaming.rb +50 -35
- data/lib/legion/extensions/llm/taxonomies.rb +14 -26
- data/lib/legion/extensions/llm/transport/fleet_lane.rb +8 -10
- data/lib/legion/extensions/llm/transport/messages/fleet_error.rb +3 -2
- data/lib/legion/extensions/llm/transport/messages/fleet_request.rb +6 -8
- data/lib/legion/extensions/llm/transport/messages/fleet_response.rb +10 -9
- data/lib/legion/extensions/llm/utils.rb +23 -5
- data/lib/legion/extensions/llm/version.rb +1 -1
- data/lib/legion/extensions/llm.rb +8 -98
- data/spec/legion/extensions/llm/auto_registration_spec.rb +4 -9
- data/spec/legion/extensions/llm/canonical/chunk_spec.rb +66 -252
- data/spec/legion/extensions/llm/canonical/content_block_spec.rb +52 -197
- data/spec/legion/extensions/llm/canonical/message_spec.rb +89 -204
- data/spec/legion/extensions/llm/canonical/params_spec.rb +55 -136
- data/spec/legion/extensions/llm/canonical/request_spec.rb +81 -143
- data/spec/legion/extensions/llm/canonical/response_spec.rb +68 -204
- data/spec/legion/extensions/llm/canonical/thinking/config_spec.rb +222 -0
- data/spec/legion/extensions/llm/canonical/thinking_spec.rb +23 -171
- data/spec/legion/extensions/llm/canonical/tool_call_spec.rb +59 -162
- data/spec/legion/extensions/llm/canonical/tool_definition_spec.rb +55 -191
- data/spec/legion/extensions/llm/canonical/tool_schema_spec.rb +26 -67
- data/spec/legion/extensions/llm/canonical/usage_spec.rb +46 -155
- data/spec/legion/extensions/llm/configuration_spec.rb +31 -5
- data/spec/legion/extensions/llm/conformance/canonical_type_examples.rb +106 -0
- data/spec/legion/extensions/llm/conformance/client_translator_examples.rb +1 -2
- data/spec/legion/extensions/llm/conformance/conformance.rb +10 -2
- data/spec/legion/extensions/llm/conformance/fixtures/canonical_fleet_round_trip.json +1 -1
- data/spec/legion/extensions/llm/conformance/fixtures/canonical_thinking_request.json +2 -2
- data/spec/legion/extensions/llm/conformance/provider_translator_examples.rb +1 -1
- data/spec/legion/extensions/llm/conformance/ssot_contract_conformance_spec.rb +128 -0
- data/spec/legion/extensions/llm/conformance/ssot_contract_examples.rb +505 -0
- data/spec/legion/extensions/llm/conformance/ssot_provider_examples.rb +11 -10
- data/spec/legion/extensions/llm/credential_sources_spec.rb +12 -13
- data/spec/legion/extensions/llm/error_spec.rb +2 -12
- data/spec/legion/extensions/llm/fleet/exact_offering_spec.rb +59 -45
- data/spec/legion/extensions/llm/fleet/provider_responder_spec.rb +173 -95
- data/spec/legion/extensions/llm/fleet/token_validator_spec.rb +7 -2
- data/spec/legion/extensions/llm/fleet/worker_execution_spec.rb +93 -74
- data/spec/legion/extensions/llm/fleet_messages_spec.rb +119 -125
- data/spec/legion/extensions/llm/gemspec_spec.rb +1 -2
- data/spec/legion/extensions/llm/inventory/boot_spec.rb +4 -4
- data/spec/legion/extensions/llm/inventory/identity_spec.rb +127 -110
- data/spec/legion/extensions/llm/inventory/probe_token_spec.rb +4 -4
- data/spec/legion/extensions/llm/inventory/publisher_spec.rb +8 -61
- data/spec/legion/extensions/llm/inventory/records_spec.rb +82 -47
- data/spec/legion/extensions/llm/inventory/registry_activation_spec.rb +49 -16
- data/spec/legion/extensions/llm/inventory/registry_replacement_spec.rb +5 -4
- data/spec/legion/extensions/llm/inventory/snapshot_spec.rb +11 -5
- data/spec/legion/extensions/llm/inventory/weight_reconciler_spec.rb +7 -3
- data/spec/legion/extensions/llm/inventory/weight_schema_spec.rb +60 -10
- data/spec/legion/extensions/llm/provider/open_ai_compatible_spec.rb +128 -68
- data/spec/legion/extensions/llm/provider/open_ai_compatible_tool_calls_array_spec.rb +7 -31
- data/spec/legion/extensions/llm/provider_contract_spec.rb +10 -15
- data/spec/legion/extensions/llm/provider_spec.rb +97 -78
- data/spec/legion/extensions/llm/routing/records_spec.rb +38 -5
- data/spec/legion/extensions/llm/stream_accumulator_spec.rb +174 -144
- data/spec/legion/extensions/llm/streaming_spec.rb +27 -0
- data/spec/legion/extensions/llm/taxonomies_spec.rb +43 -43
- data/spec/legion/extensions/llm/transport/fleet_lane_spec.rb +1 -1
- data/spec/legion/extensions/llm/utils_spec.rb +26 -7
- data/spec/legion/extensions/llm_base_contract_spec.rb +55 -90
- data/spec/legion/extensions/llm_extension_spec.rb +5 -5
- data/spec/support/fake_llm_provider.rb +45 -39
- data/spec/support/fake_ssot_harness.rb +7 -2
- metadata +13 -54
- data/lib/legion/extensions/llm/agent.rb +0 -366
- data/lib/legion/extensions/llm/aliases.json +0 -436
- data/lib/legion/extensions/llm/aliases.rb +0 -67
- data/lib/legion/extensions/llm/attachment.rb +0 -229
- data/lib/legion/extensions/llm/chat.rb +0 -354
- data/lib/legion/extensions/llm/chunk.rb +0 -10
- data/lib/legion/extensions/llm/content.rb +0 -81
- data/lib/legion/extensions/llm/context.rb +0 -33
- data/lib/legion/extensions/llm/embedding.rb +0 -33
- data/lib/legion/extensions/llm/image.rb +0 -109
- data/lib/legion/extensions/llm/inventory/capabilities.rb +0 -40
- data/lib/legion/extensions/llm/inventory/scoped_refresher.rb +0 -310
- data/lib/legion/extensions/llm/message.rb +0 -118
- data/lib/legion/extensions/llm/mime_type.rb +0 -75
- data/lib/legion/extensions/llm/model/info.rb +0 -286
- data/lib/legion/extensions/llm/model/modalities.rb +0 -26
- data/lib/legion/extensions/llm/model/pricing.rb +0 -52
- data/lib/legion/extensions/llm/model/pricing_category.rb +0 -50
- data/lib/legion/extensions/llm/model/pricing_tier.rb +0 -37
- data/lib/legion/extensions/llm/model.rb +0 -11
- data/lib/legion/extensions/llm/models.json +0 -57313
- data/lib/legion/extensions/llm/models.rb +0 -530
- data/lib/legion/extensions/llm/models_schema.json +0 -168
- data/lib/legion/extensions/llm/moderation.rb +0 -60
- data/lib/legion/extensions/llm/registry_event_builder.rb +0 -141
- data/lib/legion/extensions/llm/registry_publisher.rb +0 -107
- data/lib/legion/extensions/llm/responses/chat_response.rb +0 -43
- data/lib/legion/extensions/llm/responses/embedding_response.rb +0 -38
- data/lib/legion/extensions/llm/responses/stream_chunk.rb +0 -43
- data/lib/legion/extensions/llm/routing/lane_key.rb +0 -66
- data/lib/legion/extensions/llm/routing/model_offering.rb +0 -241
- data/lib/legion/extensions/llm/routing/offering_registry.rb +0 -101
- data/lib/legion/extensions/llm/routing/registry_event.rb +0 -167
- data/lib/legion/extensions/llm/thinking.rb +0 -53
- data/lib/legion/extensions/llm/tokens.rb +0 -51
- data/lib/legion/extensions/llm/tool_call.rb +0 -34
- data/lib/legion/extensions/llm/transcription.rb +0 -39
- data/lib/legion/extensions/llm/transport/messages/registry_event.rb +0 -44
- data/spec/legion/extensions/llm/agent_spec.rb +0 -179
- data/spec/legion/extensions/llm/attachment_spec.rb +0 -25
- data/spec/legion/extensions/llm/conformance/fixtures/ssot_identity_vectors.json +0 -84
- data/spec/legion/extensions/llm/context_spec.rb +0 -127
- data/spec/legion/extensions/llm/inventory/capabilities_spec.rb +0 -43
- data/spec/legion/extensions/llm/inventory/scoped_refresher_spec.rb +0 -340
- data/spec/legion/extensions/llm/message_spec.rb +0 -64
- data/spec/legion/extensions/llm/model/info_spec.rb +0 -222
- data/spec/legion/extensions/llm/models_spec.rb +0 -104
- data/spec/legion/extensions/llm/registry_event_builder_spec.rb +0 -68
- data/spec/legion/extensions/llm/registry_publisher_spec.rb +0 -22
- data/spec/legion/extensions/llm/responses/response_objects_spec.rb +0 -75
- data/spec/legion/extensions/llm/routing/model_offering_spec.rb +0 -281
- data/spec/legion/extensions/llm/routing/offering_registry_spec.rb +0 -50
- data/spec/legion/extensions/llm/routing/registry_event_spec.rb +0 -120
|
@@ -5,106 +5,71 @@ require 'spec_helper'
|
|
|
5
5
|
RSpec.describe Legion::Extensions::Llm do
|
|
6
6
|
include_context 'with fake llm provider'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def execute(value:)
|
|
16
|
-
"echo #{value}"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it 'loads and discovers provider classes from the namespace' do
|
|
23
|
-
provider_classes = Legion::Extensions::Llm::Models.scan_provider_classes
|
|
24
|
-
expect(provider_classes).to include(fake_llm: SpecSupport::FakeLLMProvider)
|
|
25
|
-
expect(Legion::Extensions::Llm::Routing::ModelOffering).to be_a(Class)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it 'lets a provider gem register options and satisfy chat through the shared API' do
|
|
29
|
-
chat = described_class.chat(model: 'fake-chat-model', provider: :fake_llm, assume_model_exists: true)
|
|
30
|
-
response = chat.ask('hello')
|
|
31
|
-
|
|
32
|
-
expect(response).to have_attributes(
|
|
33
|
-
role: :assistant,
|
|
34
|
-
content: 'fake response to hello',
|
|
35
|
-
model_id: 'fake-chat-model',
|
|
36
|
-
input_tokens: 10,
|
|
37
|
-
output_tokens: 5
|
|
38
|
-
)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it 'runs shared tool orchestration without provider-specific payload code' do
|
|
42
|
-
response = described_class.chat(model: 'fake-chat-model', provider: :fake_llm, assume_model_exists: true)
|
|
43
|
-
.with_tool(SpecSupport::EchoTool)
|
|
44
|
-
.ask('use the tool')
|
|
45
|
-
|
|
46
|
-
expect(response.content).to eq('tool result: echo 21')
|
|
8
|
+
it 'exposes the AutoRegistration discovery contract from the provider namespace' do
|
|
9
|
+
# Registration into Legion::LLM is owned by legion-llm (R12); lex-llm owns
|
|
10
|
+
# the namespace contract the scanner reads: PROVIDER_FAMILY (Symbol) plus
|
|
11
|
+
# a provider_class singleton returning the Provider subclass.
|
|
12
|
+
expect(Legion::Extensions::Llm::FakeLlmProvider.const_get(:PROVIDER_FAMILY)).to eq(:fake_llm)
|
|
13
|
+
expect(Legion::Extensions::Llm::FakeLlmProvider.provider_class).to be < Legion::Extensions::Llm::Provider
|
|
47
14
|
end
|
|
48
15
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
response = described_class.chat(model: 'fake-chat-model', provider: :fake_llm, assume_model_exists: true)
|
|
60
|
-
.with_schema(schema)
|
|
61
|
-
.ask('structured')
|
|
62
|
-
|
|
63
|
-
expect(response.content).to eq({ 'answer' => 42 })
|
|
64
|
-
end
|
|
16
|
+
describe 'provider funnel contract (0.8.0)' do
|
|
17
|
+
let(:provider) do
|
|
18
|
+
SpecSupport::FakeLLMProvider.new(
|
|
19
|
+
Legion::Extensions::Llm::Configuration.new.tap do |config|
|
|
20
|
+
config.fake_llm_api_key = 'fake-key'
|
|
21
|
+
end
|
|
22
|
+
)
|
|
23
|
+
end
|
|
65
24
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
25
|
+
let(:user_message) do
|
|
26
|
+
Legion::Extensions::Llm::Canonical::Message.build(role: :user, content: 'hello')
|
|
27
|
+
end
|
|
69
28
|
|
|
70
|
-
|
|
71
|
-
.
|
|
29
|
+
it 'completes with canonical input and returns a Canonical::Response' do
|
|
30
|
+
response = provider.complete([user_message], model: 'fake-chat-model')
|
|
31
|
+
expect(response).to be_a(Legion::Extensions::Llm::Canonical::Response)
|
|
32
|
+
expect(response.text).to eq('fake response to hello')
|
|
33
|
+
expect(response.usage).to be_a(Legion::Extensions::Llm::Canonical::Usage)
|
|
34
|
+
end
|
|
72
35
|
|
|
73
|
-
|
|
74
|
-
|
|
36
|
+
it 'streams canonical chunks ending in exactly one done chunk (05 O5)' do
|
|
37
|
+
chunks = []
|
|
38
|
+
provider.complete([user_message], model: 'fake-chat-model') { |chunk| chunks << chunk }
|
|
75
39
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.to
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
it 'connects model offerings to Legion fleet queue construction end to end' do
|
|
82
|
-
offering = Legion::Extensions::Llm::Routing::ModelOffering.new(
|
|
83
|
-
provider_family: :fake_llm,
|
|
84
|
-
instance_id: :worker_one,
|
|
85
|
-
transport: :rabbitmq,
|
|
86
|
-
model: 'fake-chat-model',
|
|
87
|
-
limits: { context_window: 16_384 }
|
|
88
|
-
)
|
|
89
|
-
exchange_class = Class.new
|
|
90
|
-
base_queue = Class.new do
|
|
91
|
-
attr_reader :bindings
|
|
40
|
+
expect(chunks).to all(be_a(Legion::Extensions::Llm::Canonical::Chunk))
|
|
41
|
+
expect(chunks.count(&:done?)).to eq(1)
|
|
42
|
+
expect(chunks.last).to be_done
|
|
43
|
+
expect(chunks.filter_map(&:delta).join).to eq('streamed response')
|
|
44
|
+
end
|
|
92
45
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
46
|
+
it 'rejects non-canonical messages at the boundary with a typed ArgumentError (08 F2)' do
|
|
47
|
+
expect { provider.complete([{ role: 'user', content: 'hash' }], model: 'fake-chat-model') }
|
|
48
|
+
.to raise_error(ArgumentError, /Canonical::Message/)
|
|
49
|
+
expect { provider.complete([nil], model: 'fake-chat-model') }
|
|
50
|
+
.to raise_error(ArgumentError, /Canonical::Message/)
|
|
51
|
+
expect { provider.complete(['string'], model: 'fake-chat-model') }
|
|
52
|
+
.to raise_error(ArgumentError, /Canonical::Message/)
|
|
53
|
+
end
|
|
96
54
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
55
|
+
it 'keeps the canonical conversation operations on the base' do
|
|
56
|
+
expect(provider).to respond_to(:chat)
|
|
57
|
+
expect(provider).to respond_to(:stream_chat)
|
|
58
|
+
expect(provider).to respond_to(:count_tokens)
|
|
59
|
+
expect(provider).to respond_to(:discover_offerings)
|
|
60
|
+
expect(provider).to respond_to(:health)
|
|
61
|
+
expect(provider).to respond_to(:embed)
|
|
62
|
+
expect(provider).to respond_to(:moderate)
|
|
63
|
+
expect(provider).to respond_to(:image)
|
|
64
|
+
expect(provider).to respond_to(:transcribe)
|
|
100
65
|
end
|
|
101
66
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
base_queue_class: base_queue
|
|
106
|
-
)
|
|
67
|
+
it 'serves discover_offerings from the registry snapshot (07 C5)' do
|
|
68
|
+
expect(provider.discover_offerings).to eq([])
|
|
69
|
+
end
|
|
107
70
|
|
|
108
|
-
|
|
71
|
+
it 'counts tokens for canonical messages' do
|
|
72
|
+
expect(provider.count_tokens(messages: [user_message], model: 'fake-chat-model')).to be_a(Integer)
|
|
73
|
+
end
|
|
109
74
|
end
|
|
110
75
|
end
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
require 'legion/extensions/llm'
|
|
4
4
|
|
|
5
5
|
RSpec.describe Legion::Extensions::Llm do
|
|
6
|
-
it 'exposes
|
|
7
|
-
expect(described_class
|
|
8
|
-
expect(
|
|
9
|
-
expect(
|
|
10
|
-
expect(
|
|
6
|
+
it 'exposes no legacy dual-constant paths after the 0.8.0 rip' do
|
|
7
|
+
expect(described_class.const_defined?(:Types, false)).to be(false)
|
|
8
|
+
expect(Legion::Extensions::Llm::Routing.const_defined?(:ModelOffering, false)).to be(false)
|
|
9
|
+
expect(Legion::Extensions::Llm::Routing.const_defined?(:OfferingRegistry, false)).to be(false)
|
|
10
|
+
expect(Legion::Extensions::Llm::Routing.const_defined?(:LaneKey, false)).to be(false)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
it 'provides complete default fleet settings' do
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SpecSupport
|
|
4
|
+
# Contract-conformant fake provider (0.8.0): canonical in, canonical out.
|
|
5
|
+
# No HTTP — complete is overridden to return canned Canonical::Response
|
|
6
|
+
# objects (and yield Canonical::Chunk for streams).
|
|
4
7
|
class FakeLLMProvider < Legion::Extensions::Llm::Provider
|
|
5
8
|
class << self
|
|
6
9
|
def configuration_options
|
|
@@ -29,26 +32,28 @@ module SpecSupport
|
|
|
29
32
|
config.fake_llm_api_base || 'https://fake-llm.invalid/v1'
|
|
30
33
|
end
|
|
31
34
|
|
|
32
|
-
# rubocop:disable Metrics/ParameterLists, Lint/UnusedMethodArgument
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
def complete(messages, tools: [], model:, params: nil, headers: {}, schema: nil, thinking: nil, # rubocop:disable Metrics/ParameterLists, Lint/UnusedMethodArgument, Style/KeywordParametersOrder
|
|
36
|
+
tool_prefs: nil, &) # rubocop:disable Lint/UnusedMethodArgument
|
|
37
|
+
enforce_canonical_messages!(messages)
|
|
38
|
+
|
|
35
39
|
if block_given?
|
|
36
|
-
yield Legion::Extensions::Llm::Chunk.
|
|
37
|
-
yield Legion::Extensions::Llm::Chunk.
|
|
40
|
+
yield Legion::Extensions::Llm::Canonical::Chunk.text_delta(delta: 'streamed ', request_id: nil)
|
|
41
|
+
yield Legion::Extensions::Llm::Canonical::Chunk.text_delta(delta: 'response', request_id: nil)
|
|
42
|
+
yield Legion::Extensions::Llm::Canonical::Chunk.done(request_id: nil, stop_reason: :end_turn)
|
|
38
43
|
end
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
has_tool_result = messages.any?(&:tool_call_id)
|
|
46
|
+
if tools.any? && !has_tool_result
|
|
41
47
|
tool = tools.values.first
|
|
42
|
-
return Legion::Extensions::Llm::
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
input_tokens: 12,
|
|
51
|
-
output_tokens: 3
|
|
48
|
+
return Legion::Extensions::Llm::Canonical::Response.build(
|
|
49
|
+
text: nil,
|
|
50
|
+
tool_calls: [
|
|
51
|
+
Legion::Extensions::Llm::Canonical::ToolCall.build(name: tool.name, id: 'tool-call-1',
|
|
52
|
+
arguments: { value: 21 })
|
|
53
|
+
],
|
|
54
|
+
stop_reason: :tool_use,
|
|
55
|
+
model: model,
|
|
56
|
+
usage: Legion::Extensions::Llm::Canonical::Usage.build(input_tokens: 12, output_tokens: 3)
|
|
52
57
|
)
|
|
53
58
|
end
|
|
54
59
|
|
|
@@ -56,45 +61,46 @@ module SpecSupport
|
|
|
56
61
|
Legion::JSON.generate({ answer: 42 })
|
|
57
62
|
elsif thinking
|
|
58
63
|
'fake response with thinking enabled'
|
|
59
|
-
elsif
|
|
60
|
-
"tool result: #{messages.last.
|
|
64
|
+
elsif has_tool_result
|
|
65
|
+
"tool result: #{messages.last.text}"
|
|
61
66
|
else
|
|
62
|
-
"fake response to #{messages.last.
|
|
67
|
+
"fake response to #{messages.last.text}"
|
|
63
68
|
end
|
|
64
69
|
|
|
65
|
-
Legion::Extensions::Llm::
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
input_tokens: 10,
|
|
70
|
-
output_tokens: 5
|
|
70
|
+
Legion::Extensions::Llm::Canonical::Response.build(
|
|
71
|
+
text: content,
|
|
72
|
+
model: model,
|
|
73
|
+
stop_reason: :end_turn,
|
|
74
|
+
usage: Legion::Extensions::Llm::Canonical::Usage.build(input_tokens: 10, output_tokens: 5)
|
|
71
75
|
)
|
|
72
76
|
end
|
|
73
|
-
# rubocop:enable Metrics/ParameterLists, Lint/UnusedMethodArgument
|
|
74
77
|
|
|
75
|
-
def embed(text:, model:, dimensions: nil)
|
|
78
|
+
def embed(text:, model:, dimensions: nil, params: nil, headers: {})
|
|
79
|
+
_ = [params, headers]
|
|
76
80
|
size = dimensions || 3
|
|
77
81
|
vectors = Array(text).map { Array.new(size, 0.5) }
|
|
78
82
|
vectors = vectors.first unless text.is_a?(Array)
|
|
79
83
|
|
|
80
|
-
|
|
84
|
+
{
|
|
85
|
+
text: text,
|
|
86
|
+
model: model,
|
|
87
|
+
embedding: vectors,
|
|
88
|
+
usage: Legion::Extensions::Llm::Canonical::Usage.build(input_tokens: Array(text).size)
|
|
89
|
+
}
|
|
81
90
|
end
|
|
82
91
|
|
|
83
|
-
def moderate(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
model: model,
|
|
87
|
-
results: [{ 'flagged' => false, 'categories' => {}, 'category_scores' => {} }]
|
|
88
|
-
)
|
|
92
|
+
def moderate(input:, model:)
|
|
93
|
+
_ = input
|
|
94
|
+
{ model: model, result: { flagged: false, categories: {} } }
|
|
89
95
|
end
|
|
90
96
|
|
|
91
|
-
def
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
def image(prompt:, model:, size:, with: nil, mask: nil, params: {}) # rubocop:disable Metrics/ParameterLists
|
|
98
|
+
_ = [prompt, with, mask, params]
|
|
99
|
+
{ model: model, image: Base64.strict_encode64('fake-image'), size: }
|
|
94
100
|
end
|
|
95
101
|
|
|
96
102
|
def transcribe(_audio_file, model:, language:, **)
|
|
97
|
-
|
|
103
|
+
{ model: model, text: 'fake transcript', language: }
|
|
98
104
|
end
|
|
99
105
|
|
|
100
106
|
# Records exactly-once disconnect for the callable-lifecycle contract; does
|
|
@@ -123,7 +129,7 @@ module SpecSupport
|
|
|
123
129
|
end
|
|
124
130
|
|
|
125
131
|
# Namespace module that mimics a real lex-llm-* provider extension.
|
|
126
|
-
#
|
|
132
|
+
# legion-llm's Call::Providers scanner discovers providers via this pattern.
|
|
127
133
|
module Legion
|
|
128
134
|
module Extensions
|
|
129
135
|
module Llm
|
|
@@ -28,9 +28,14 @@ module SpecSupport
|
|
|
28
28
|
true
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
# L11: the fake mimics the 0.8.0 callable contract — chat returns a
|
|
32
|
+
# Canonical::Response (the raw-Hash return violated the contract the
|
|
33
|
+
# harness impersonates; conformance B2 asserts exactly this shape).
|
|
34
|
+
def chat(messages, model:, **rest)
|
|
32
35
|
@inference_calls += 1
|
|
33
|
-
|
|
36
|
+
Legion::Extensions::Llm::Canonical::Response.build(
|
|
37
|
+
text: "fake chat #{model} #{messages.size} #{rest.size}", model: model, stop_reason: :end_turn
|
|
38
|
+
)
|
|
34
39
|
end
|
|
35
40
|
|
|
36
41
|
# Runtime error normalization on the provider callable itself, mirroring the
|
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.
|
|
4
|
+
version: 0.8.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LegionIO
|
|
@@ -253,13 +253,10 @@ files:
|
|
|
253
253
|
- Gemfile
|
|
254
254
|
- LICENSE
|
|
255
255
|
- README.md
|
|
256
|
+
- RULES.md
|
|
256
257
|
- lex-llm.gemspec
|
|
257
258
|
- lib/legion/extensions/llm.rb
|
|
258
259
|
- lib/legion/extensions/llm/actors/metering_flush.rb
|
|
259
|
-
- lib/legion/extensions/llm/agent.rb
|
|
260
|
-
- lib/legion/extensions/llm/aliases.json
|
|
261
|
-
- lib/legion/extensions/llm/aliases.rb
|
|
262
|
-
- lib/legion/extensions/llm/attachment.rb
|
|
263
260
|
- lib/legion/extensions/llm/auto_registration.rb
|
|
264
261
|
- lib/legion/extensions/llm/canonical.rb
|
|
265
262
|
- lib/legion/extensions/llm/canonical/chunk.rb
|
|
@@ -268,25 +265,26 @@ files:
|
|
|
268
265
|
- lib/legion/extensions/llm/canonical/params.rb
|
|
269
266
|
- lib/legion/extensions/llm/canonical/request.rb
|
|
270
267
|
- lib/legion/extensions/llm/canonical/response.rb
|
|
268
|
+
- lib/legion/extensions/llm/canonical/strict.rb
|
|
271
269
|
- lib/legion/extensions/llm/canonical/thinking.rb
|
|
270
|
+
- lib/legion/extensions/llm/canonical/thinking_config.rb
|
|
272
271
|
- lib/legion/extensions/llm/canonical/tool_call.rb
|
|
273
272
|
- lib/legion/extensions/llm/canonical/tool_definition.rb
|
|
274
273
|
- lib/legion/extensions/llm/canonical/tool_schema.rb
|
|
275
274
|
- lib/legion/extensions/llm/canonical/usage.rb
|
|
276
275
|
- lib/legion/extensions/llm/capabilities.rb
|
|
277
276
|
- lib/legion/extensions/llm/capability_policy.rb
|
|
278
|
-
- lib/legion/extensions/llm/chat.rb
|
|
279
|
-
- lib/legion/extensions/llm/chunk.rb
|
|
280
277
|
- lib/legion/extensions/llm/configuration.rb
|
|
281
278
|
- lib/legion/extensions/llm/connection.rb
|
|
282
|
-
- lib/legion/extensions/llm/content.rb
|
|
283
|
-
- lib/legion/extensions/llm/context.rb
|
|
284
279
|
- lib/legion/extensions/llm/credential_sources.rb
|
|
285
|
-
- lib/legion/extensions/llm/
|
|
280
|
+
- lib/legion/extensions/llm/discovery/actor.rb
|
|
281
|
+
- lib/legion/extensions/llm/discovery/pipeline.rb
|
|
286
282
|
- lib/legion/extensions/llm/error.rb
|
|
287
283
|
- lib/legion/extensions/llm/errors/unsupported_capability.rb
|
|
284
|
+
- lib/legion/extensions/llm/fleet/contract_error.rb
|
|
288
285
|
- lib/legion/extensions/llm/fleet/default_exchange_reply.rb
|
|
289
286
|
- lib/legion/extensions/llm/fleet/envelope_validation.rb
|
|
287
|
+
- lib/legion/extensions/llm/fleet/fleet_envelope.rb
|
|
290
288
|
- lib/legion/extensions/llm/fleet/protocol.rb
|
|
291
289
|
- lib/legion/extensions/llm/fleet/provider_responder.rb
|
|
292
290
|
- lib/legion/extensions/llm/fleet/publish_safety.rb
|
|
@@ -294,9 +292,7 @@ files:
|
|
|
294
292
|
- lib/legion/extensions/llm/fleet/token_error.rb
|
|
295
293
|
- lib/legion/extensions/llm/fleet/token_validator.rb
|
|
296
294
|
- lib/legion/extensions/llm/fleet/worker_execution.rb
|
|
297
|
-
- lib/legion/extensions/llm/image.rb
|
|
298
295
|
- lib/legion/extensions/llm/inventory/callable_handle.rb
|
|
299
|
-
- lib/legion/extensions/llm/inventory/capabilities.rb
|
|
300
296
|
- lib/legion/extensions/llm/inventory/errors.rb
|
|
301
297
|
- lib/legion/extensions/llm/inventory/evidence.rb
|
|
302
298
|
- lib/legion/extensions/llm/inventory/identity.rb
|
|
@@ -306,56 +302,30 @@ files:
|
|
|
306
302
|
- lib/legion/extensions/llm/inventory/publisher.rb
|
|
307
303
|
- lib/legion/extensions/llm/inventory/records.rb
|
|
308
304
|
- lib/legion/extensions/llm/inventory/registry.rb
|
|
309
|
-
- lib/legion/extensions/llm/inventory/scoped_refresher.rb
|
|
310
305
|
- lib/legion/extensions/llm/inventory/snapshot.rb
|
|
311
306
|
- lib/legion/extensions/llm/inventory/weight_reconciler.rb
|
|
312
307
|
- lib/legion/extensions/llm/inventory/weight_schema.rb
|
|
313
|
-
- lib/legion/extensions/llm/message.rb
|
|
314
|
-
- lib/legion/extensions/llm/mime_type.rb
|
|
315
|
-
- lib/legion/extensions/llm/model.rb
|
|
316
|
-
- lib/legion/extensions/llm/model/info.rb
|
|
317
|
-
- lib/legion/extensions/llm/model/modalities.rb
|
|
318
|
-
- lib/legion/extensions/llm/model/pricing.rb
|
|
319
|
-
- lib/legion/extensions/llm/model/pricing_category.rb
|
|
320
|
-
- lib/legion/extensions/llm/model/pricing_tier.rb
|
|
321
|
-
- lib/legion/extensions/llm/models.json
|
|
322
|
-
- lib/legion/extensions/llm/models.rb
|
|
323
|
-
- lib/legion/extensions/llm/models_schema.json
|
|
324
|
-
- lib/legion/extensions/llm/moderation.rb
|
|
325
308
|
- lib/legion/extensions/llm/provider.rb
|
|
326
309
|
- lib/legion/extensions/llm/provider/open_ai_compatible.rb
|
|
327
310
|
- lib/legion/extensions/llm/provider_contract.rb
|
|
328
311
|
- lib/legion/extensions/llm/provider_settings.rb
|
|
329
|
-
- lib/legion/extensions/llm/registry_event_builder.rb
|
|
330
|
-
- lib/legion/extensions/llm/registry_publisher.rb
|
|
331
|
-
- lib/legion/extensions/llm/responses/chat_response.rb
|
|
332
|
-
- lib/legion/extensions/llm/responses/embedding_response.rb
|
|
333
|
-
- lib/legion/extensions/llm/responses/stream_chunk.rb
|
|
334
312
|
- lib/legion/extensions/llm/responses/thinking_extractor.rb
|
|
313
|
+
- lib/legion/extensions/llm/responses/tool_arguments.rb
|
|
335
314
|
- lib/legion/extensions/llm/routing.rb
|
|
336
|
-
- lib/legion/extensions/llm/routing/lane_key.rb
|
|
337
|
-
- lib/legion/extensions/llm/routing/model_offering.rb
|
|
338
|
-
- lib/legion/extensions/llm/routing/offering_registry.rb
|
|
339
315
|
- lib/legion/extensions/llm/routing/provider_outcome.rb
|
|
340
316
|
- lib/legion/extensions/llm/routing/records.rb
|
|
341
|
-
- lib/legion/extensions/llm/routing/registry_event.rb
|
|
342
317
|
- lib/legion/extensions/llm/settings_cascade.rb
|
|
343
318
|
- lib/legion/extensions/llm/stop_reason_mapping.rb
|
|
344
319
|
- lib/legion/extensions/llm/stream_accumulator.rb
|
|
345
320
|
- lib/legion/extensions/llm/streaming.rb
|
|
346
321
|
- lib/legion/extensions/llm/taxonomies.rb
|
|
347
|
-
- lib/legion/extensions/llm/thinking.rb
|
|
348
|
-
- lib/legion/extensions/llm/tokens.rb
|
|
349
322
|
- lib/legion/extensions/llm/tool.rb
|
|
350
|
-
- lib/legion/extensions/llm/tool_call.rb
|
|
351
|
-
- lib/legion/extensions/llm/transcription.rb
|
|
352
323
|
- lib/legion/extensions/llm/transport/exchanges/fleet.rb
|
|
353
324
|
- lib/legion/extensions/llm/transport/exchanges/llm_registry.rb
|
|
354
325
|
- lib/legion/extensions/llm/transport/fleet_lane.rb
|
|
355
326
|
- lib/legion/extensions/llm/transport/messages/fleet_error.rb
|
|
356
327
|
- lib/legion/extensions/llm/transport/messages/fleet_request.rb
|
|
357
328
|
- lib/legion/extensions/llm/transport/messages/fleet_response.rb
|
|
358
|
-
- lib/legion/extensions/llm/transport/messages/registry_event.rb
|
|
359
329
|
- lib/legion/extensions/llm/utils.rb
|
|
360
330
|
- lib/legion/extensions/llm/version.rb
|
|
361
331
|
- spec/fixtures/ruby.mp3
|
|
@@ -366,8 +336,6 @@ files:
|
|
|
366
336
|
- spec/fixtures/ruby.xml
|
|
367
337
|
- spec/fixtures/sample.pdf
|
|
368
338
|
- spec/legion/extensions/llm/actor/metering_flush_spec.rb
|
|
369
|
-
- spec/legion/extensions/llm/agent_spec.rb
|
|
370
|
-
- spec/legion/extensions/llm/attachment_spec.rb
|
|
371
339
|
- spec/legion/extensions/llm/auto_registration_spec.rb
|
|
372
340
|
- spec/legion/extensions/llm/canonical/chunk_spec.rb
|
|
373
341
|
- spec/legion/extensions/llm/canonical/content_block_spec.rb
|
|
@@ -375,6 +343,7 @@ files:
|
|
|
375
343
|
- spec/legion/extensions/llm/canonical/params_spec.rb
|
|
376
344
|
- spec/legion/extensions/llm/canonical/request_spec.rb
|
|
377
345
|
- spec/legion/extensions/llm/canonical/response_spec.rb
|
|
346
|
+
- spec/legion/extensions/llm/canonical/thinking/config_spec.rb
|
|
378
347
|
- spec/legion/extensions/llm/canonical/thinking_spec.rb
|
|
379
348
|
- spec/legion/extensions/llm/canonical/tool_call_spec.rb
|
|
380
349
|
- spec/legion/extensions/llm/canonical/tool_definition_spec.rb
|
|
@@ -383,6 +352,7 @@ files:
|
|
|
383
352
|
- spec/legion/extensions/llm/capabilities_spec.rb
|
|
384
353
|
- spec/legion/extensions/llm/capability_policy_spec.rb
|
|
385
354
|
- spec/legion/extensions/llm/configuration_spec.rb
|
|
355
|
+
- spec/legion/extensions/llm/conformance/canonical_type_examples.rb
|
|
386
356
|
- spec/legion/extensions/llm/conformance/client_translator_examples.rb
|
|
387
357
|
- spec/legion/extensions/llm/conformance/conformance.rb
|
|
388
358
|
- spec/legion/extensions/llm/conformance/echo_translator.rb
|
|
@@ -409,15 +379,15 @@ files:
|
|
|
409
379
|
- spec/legion/extensions/llm/conformance/fixtures/canonical_tool_results_continuation_request.json
|
|
410
380
|
- spec/legion/extensions/llm/conformance/fixtures/canonical_tool_use_response.json
|
|
411
381
|
- spec/legion/extensions/llm/conformance/fixtures/canonical_tools_request.json
|
|
412
|
-
- spec/legion/extensions/llm/conformance/fixtures/ssot_identity_vectors.json
|
|
413
382
|
- spec/legion/extensions/llm/conformance/provider_tool_rendering_examples.rb
|
|
414
383
|
- spec/legion/extensions/llm/conformance/provider_translator_examples.rb
|
|
384
|
+
- spec/legion/extensions/llm/conformance/ssot_contract_conformance_spec.rb
|
|
385
|
+
- spec/legion/extensions/llm/conformance/ssot_contract_examples.rb
|
|
415
386
|
- spec/legion/extensions/llm/conformance/ssot_provider_conformance_spec.rb
|
|
416
387
|
- spec/legion/extensions/llm/conformance/ssot_provider_examples.rb
|
|
417
388
|
- spec/legion/extensions/llm/connection_close_spec.rb
|
|
418
389
|
- spec/legion/extensions/llm/connection_logging_spec.rb
|
|
419
390
|
- spec/legion/extensions/llm/connection_retry_spec.rb
|
|
420
|
-
- spec/legion/extensions/llm/context_spec.rb
|
|
421
391
|
- spec/legion/extensions/llm/credential_sources_spec.rb
|
|
422
392
|
- spec/legion/extensions/llm/error_middleware_spec.rb
|
|
423
393
|
- spec/legion/extensions/llm/error_spec.rb
|
|
@@ -429,7 +399,6 @@ files:
|
|
|
429
399
|
- spec/legion/extensions/llm/gemspec_spec.rb
|
|
430
400
|
- spec/legion/extensions/llm/inventory/boot_spec.rb
|
|
431
401
|
- spec/legion/extensions/llm/inventory/callable_handle_spec.rb
|
|
432
|
-
- spec/legion/extensions/llm/inventory/capabilities_spec.rb
|
|
433
402
|
- spec/legion/extensions/llm/inventory/evidence_spec.rb
|
|
434
403
|
- spec/legion/extensions/llm/inventory/identity_spec.rb
|
|
435
404
|
- spec/legion/extensions/llm/inventory/immutable_value_spec.rb
|
|
@@ -441,27 +410,17 @@ files:
|
|
|
441
410
|
- spec/legion/extensions/llm/inventory/registry_availability_spec.rb
|
|
442
411
|
- spec/legion/extensions/llm/inventory/registry_callable_spec.rb
|
|
443
412
|
- spec/legion/extensions/llm/inventory/registry_replacement_spec.rb
|
|
444
|
-
- spec/legion/extensions/llm/inventory/scoped_refresher_spec.rb
|
|
445
413
|
- spec/legion/extensions/llm/inventory/snapshot_spec.rb
|
|
446
414
|
- spec/legion/extensions/llm/inventory/weight_reconciler_spec.rb
|
|
447
415
|
- spec/legion/extensions/llm/inventory/weight_schema_spec.rb
|
|
448
|
-
- spec/legion/extensions/llm/message_spec.rb
|
|
449
|
-
- spec/legion/extensions/llm/model/info_spec.rb
|
|
450
|
-
- spec/legion/extensions/llm/models_spec.rb
|
|
451
416
|
- spec/legion/extensions/llm/provider/open_ai_compatible_spec.rb
|
|
452
417
|
- spec/legion/extensions/llm/provider/open_ai_compatible_tool_calls_array_spec.rb
|
|
453
418
|
- spec/legion/extensions/llm/provider_contract_spec.rb
|
|
454
419
|
- spec/legion/extensions/llm/provider_settings_spec.rb
|
|
455
420
|
- spec/legion/extensions/llm/provider_spec.rb
|
|
456
|
-
- spec/legion/extensions/llm/registry_event_builder_spec.rb
|
|
457
|
-
- spec/legion/extensions/llm/registry_publisher_spec.rb
|
|
458
|
-
- spec/legion/extensions/llm/responses/response_objects_spec.rb
|
|
459
421
|
- spec/legion/extensions/llm/responses/thinking_extractor_spec.rb
|
|
460
|
-
- spec/legion/extensions/llm/routing/model_offering_spec.rb
|
|
461
|
-
- spec/legion/extensions/llm/routing/offering_registry_spec.rb
|
|
462
422
|
- spec/legion/extensions/llm/routing/provider_outcome_spec.rb
|
|
463
423
|
- spec/legion/extensions/llm/routing/records_spec.rb
|
|
464
|
-
- spec/legion/extensions/llm/routing/registry_event_spec.rb
|
|
465
424
|
- spec/legion/extensions/llm/settings_cascade_spec.rb
|
|
466
425
|
- spec/legion/extensions/llm/stop_reason_mapping_spec.rb
|
|
467
426
|
- spec/legion/extensions/llm/stream_accumulator_spec.rb
|