Class: Legion::LLM::Call::LexLLMAdapter
- Inherits:
-
Object
- Object
- Legion::LLM::Call::LexLLMAdapter
- Includes:
- Legion::Logging::Helper
- Defined in:
- lib/legion/llm/call/lex_llm_adapter.rb
Overview
Adapts a lex-llm provider class to legion-llm's native dispatch contract.
Constant Summary collapse
- METADATA_KEYS =
%i[tier capabilities enabled].freeze
- RESPONSES_PROVIDER_FAMILIES =
Only providers that natively expose /v1/responses (OpenAI API proper). All other providers (vLLM, Ollama, MLX, Anthropic, Bedrock, Gemini, Vertex, Azure Foundry) use /v1/chat/completions and must declare :responses in their instance capabilities explicitly.
%i[openai].freeze
- CANONICAL_ROLES =
Canonical roles are exactly system/user/assistant/tool. Dialect roles (developer/critic/discriminator) fold to :system at this edge.
%i[system user assistant tool].freeze
Instance Method Summary collapse
-
#chat(model:, messages:, **opts) ⇒ Object
0.8.0 provider funnel (08): positional canonical messages, and the return value is a Canonical::Response — the adapter returns it directly (with offering metadata merged), never a re-projected hash.
-
#completion_params(opts) ⇒ Object
Canonical Params keys only (03 O03a): temperature folds into params at the adapter edge; the 0.8.0 funnel has no temperature kwarg.
- #count_tokens(model:, messages:) ⇒ Object
-
#discover_offerings(live: false, **filters) ⇒ Object
SSOT writer contract: every lex-llm-* provider actor discovers its catalog through
adapter.discover_offerings(forwarded to the per-instance Provider's catalog method — same body as #offerings), then publishes the result into the shared inventory registry (lex-llm Inventory::Registry) on its own discovery cadence, reconciling write-time weights from current settings (lex-llm Inventory::WeightReconciler) before each publish. -
#embed(model:, text:, dimensions: nil, **opts) ⇒ Object
0.8.0 embed artifact (05 §3 / O07): a documented Hash
{ text:, model:, embedding: Array<Float>, usage: Canonical::Usage }. - #health(live: false) ⇒ Object
- #image(model:, prompt:, size:, with: nil, mask: nil, **opts) ⇒ Object
-
#initialize(provider_name, provider_class, instance_config: {}) ⇒ LexLLMAdapter
constructor
A new instance of LexLLMAdapter.
- #offerings(live: false, **filters) ⇒ Object
- #responses(model:, body:, messages:, stream: false, **opts) ⇒ Object
- #stream(model:, messages:, **opts, &block) ⇒ Object
- #supports?(capability) ⇒ Boolean
Constructor Details
#initialize(provider_name, provider_class, instance_config: {}) ⇒ LexLLMAdapter
Returns a new instance of LexLLMAdapter.
22 23 24 25 26 27 28 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 22 def initialize(provider_name, provider_class, instance_config: {}) @provider_name = provider_name.to_sym @provider_class = provider_class @instance_config = instance_config @capabilities = Array(instance_config[:capabilities] || instance_config['capabilities']).map(&:to_sym) @lex_llm_namespace = resolve_lex_llm_namespace end |
Instance Method Details
#chat(model:, messages:, **opts) ⇒ Object
0.8.0 provider funnel (08): positional canonical messages, and the return value is a Canonical::Response — the adapter returns it directly (with offering metadata merged), never a re-projected hash.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 33 def chat(model:, messages:, **opts) response = provider.chat( (, system: opts[:system]), model: model, tools: normalize_tools(opts[:tools]), params: completion_params(opts), headers: opts[:headers] || {}, schema: opts[:schema], thinking: opts[:thinking], tool_prefs: opts[:tool_prefs] ) (response, offering_metadata: opts[:offering_metadata]) end |
#completion_params(opts) ⇒ Object
Canonical Params keys only (03 O03a): temperature folds into params at the adapter edge; the 0.8.0 funnel has no temperature kwarg.
73 74 75 76 77 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 73 def completion_params(opts) params = (opts[:params] || {}).dup params[:temperature] = opts[:temperature] if opts.key?(:temperature) && !opts[:temperature].nil? params.empty? ? nil : params end |
#count_tokens(model:, messages:) ⇒ Object
144 145 146 147 148 149 150 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 144 def count_tokens(model:, messages:, **) { result: provider.count_tokens(messages: (), model: model), model: model, usage: {} } end |
#discover_offerings(live: false, **filters) ⇒ Object
SSOT writer contract: every lex-llm-* provider actor discovers its
catalog through adapter.discover_offerings (forwarded to the
per-instance Provider's catalog method — same body as #offerings),
then publishes the result into the shared inventory registry
(lex-llm Inventory::Registry) on its own discovery cadence,
reconciling write-time weights from current settings (lex-llm
Inventory::WeightReconciler) before each publish. Without this
funnel the actors have no catalog and the registry stays empty.
170 171 172 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 170 def discover_offerings(live: false, **filters) offerings(live: live, **filters) end |
#embed(model:, text:, dimensions: nil, **opts) ⇒ Object
0.8.0 embed artifact (05 §3 / O07): a documented Hash
{ text:, model:, embedding: Array<Float>, usage: Canonical::Usage }.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 107 def (model:, text:, dimensions: nil, **opts) response = provider.( text: text, model: model, dimensions: dimensions, params: opts[:params] || {}, headers: opts[:headers] || {} ) usage = response[:usage] input_tokens = usage.respond_to?(:input_tokens) ? usage.input_tokens.to_i : 0 { result: response[:embedding], model: response[:model], usage: { input_tokens: input_tokens, output_tokens: 0 }, metadata: (offering_metadata: opts[:offering_metadata]) } end |
#health(live: false) ⇒ Object
140 141 142 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 140 def health(live: false) provider.health(live: live) end |
#image(model:, prompt:, size:, with: nil, mask: nil, **opts) ⇒ Object
126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 126 def image(model:, prompt:, size:, with: nil, mask: nil, **opts) response = call_image_provider( prompt: prompt, model: model, size: size, with: with, mask: mask, params: opts[:params] || {}, headers: opts[:headers] || {} ) image_response(response, model: model, offering_metadata: opts[:offering_metadata]) end |
#offerings(live: false, **filters) ⇒ Object
152 153 154 155 156 157 158 159 160 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 152 def offerings(live: false, **filters) return [] unless provider.respond_to?(:discover_offerings) provider.discover_offerings(live: live, raise_on_unreachable: live, **filters) rescue ArgumentError => e raise unless e..include?('raise_on_unreachable') provider.discover_offerings(live: live, **filters) end |
#responses(model:, body:, messages:, stream: false, **opts) ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 79 def responses(model:, body:, messages:, stream: false, **opts, &) raise Legion::LLM::ProviderError, "Responses API dispatch is not supported for #{provider_name}" unless supports?(:responses) payload = build_responses_payload( body: body, model: model, messages: , stream: stream, system: opts[:system], tools: opts[:tools] ) if stream stream_responses_payload(payload, offering_metadata: opts[:offering_metadata], &) else response = provider.connection.post(responses_url, payload) responses_hash_response(response.body, offering_metadata: opts[:offering_metadata]) end end |
#stream(model:, messages:, **opts, &block) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 48 def stream(model:, messages:, **opts, &block) accumulator = build_stream_accumulator response = provider.stream_chat( (, system: opts[:system]), model: model, tools: normalize_tools(opts[:tools]), params: completion_params(opts), headers: opts[:headers] || {}, schema: opts[:schema], thinking: opts[:thinking], tool_prefs: opts[:tool_prefs] ) do |chunk| accumulate_stream_chunk(accumulator, chunk) block&.call(chunk) end if response (response, offering_metadata: opts[:offering_metadata]) else stream_fallback_response(accumulator, model: model, offering_metadata: opts[:offering_metadata]) end end |
#supports?(capability) ⇒ Boolean
99 100 101 102 103 |
# File 'lib/legion/llm/call/lex_llm_adapter.rb', line 99 def supports?(capability) return true unless capability.to_sym == :responses @capabilities.include?(:responses) || RESPONSES_PROVIDER_FAMILIES.include?(provider_name) end |