Module: Protege::Inference
- Extended by:
- Inference
- Included in:
- Inference
- Defined in:
- lib/protege/inference.rb,
lib/protege/inference/provider/request.rb,
lib/protege/inference/provider/response.rb,
lib/protege/inference/provider/tool_call.rb
Overview
Namespace for all inference concerns — providers, requests, responses, and the value objects they exchange.
Module methods here are the cross-namespace entry points. Other namespaces (Orchestrator, Gateway)
and the Rails layer call these rather than reaching into Provider::* internals directly.
Defined Under Namespace
Modules: Provider
Instance Method Summary collapse
-
#build_request(user_content: nil, tools: [], context_messages: []) ⇒ Provider::Request
Build a
Provider::Requestfor the first turn of a generation. -
#extend_request(previous:, response:, tool_results:, review_parts: []) ⇒ Provider::Request
Build the next-turn
Provider::Requestby appending the assistant turn and tool results. -
#generate(request) ⇒ Provider::Response
Generate against the configured provider — the seam that keeps the provider contract hash-based.
-
#generate_stream(request) {|chunk| ... } ⇒ Provider::Response
Stream a generation against the configured provider, wrapping the final response hash as #generate does.
-
#provider ⇒ ProviderMixin
Instantiate the provider class matching the configured
provider_id.
Instance Method Details
#build_request(user_content: nil, tools: [], context_messages: []) ⇒ Provider::Request
Build a Provider::Request for the first turn of a generation.
The message list is the resolver-supplied context_messages. When user_content is given, it
is appended as a trailing role: :user message — used by the reply path to inject the live
inbound email body (a stripped string, or multimodal parts when it has attachments). When
user_content is nil the request is exactly the resolver messages: a scheduled run assembles
its whole turn — instructions included — through its resolver chain, so the harness injects
nothing. The request carries only the conversation and the tool classes; the active provider reads
its own model / sampling / credentials from config.providers.
26 27 28 29 30 31 |
# File 'lib/protege/inference.rb', line 26 def build_request(user_content: nil, tools: [], context_messages: []) = Array() += [ModelMessage.new(role: :user, content: user_content)] unless user_content.nil? Provider::Request.new(messages:, tools:) end |
#extend_request(previous:, response:, tool_results:, review_parts: []) ⇒ Provider::Request
Build the next-turn Provider::Request by appending the assistant turn and tool results.
Used by the Orchestrator tool loop after the model requested tools: it appends the assistant
ModelMessage (carrying its tool_calls) followed by one role: :tool message per
result, then re-issues the request with the same tools as previous (model and sampling come
from the provider's config on each turn). Each tool result's payload is correlated back via
tool_call.id and serialized as JSON.
When review_parts are given (a tool asked to show the model an image/PDF it can't return in a
text-only tool result), a final role: :user message carrying those vision parts is appended
after the tool messages — the only place the OpenAI schema lets visual content reach the model.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/protege/inference.rb', line 51 def extend_request(previous:, response:, tool_results:, review_parts: []) assistant_msg = ModelMessage.new( role: :assistant, content: response.text || '', tool_calls: response.tool_calls ) = tool_results.map do |tr| ModelMessage.new( role: :tool, tool_call_id: tr.tool_call.id, content: tr.result.to_h.to_json ) end = previous. + [assistant_msg] + += [(review_parts)] if review_parts.any? Provider::Request.new(messages:, tools: previous.tools) end |
#generate(request) ⇒ Provider::Response
Generate against the configured provider — the seam that keeps the provider contract hash-based.
Hands the provider the wire request hash (+Request#to_wire+) and wraps its returned response hash
back into a Provider::Response, so providers never touch Protege's internal value types.
94 95 96 |
# File 'lib/protege/inference.rb', line 94 def generate(request) Provider::Response.from_wire(provider.generate(request.to_wire)) end |
#generate_stream(request) {|chunk| ... } ⇒ Provider::Response
Stream a generation against the configured provider, wrapping the final response hash as #generate does. Yielded chunks pass through untouched.
104 105 106 |
# File 'lib/protege/inference.rb', line 104 def generate_stream(request, &) Provider::Response.from_wire(provider.generate_stream(request.to_wire, &)) end |
#provider ⇒ ProviderMixin
Instantiate the provider class matching the configured provider_id.
Resolves against ProviderMixin.registered (every Provider descendant) by symbolic
id, so the lookup stays correct across Zeitwerk reloads.
79 80 81 82 83 84 85 86 |
# File 'lib/protege/inference.rb', line 79 def provider id = Protege.configuration.provider_id klass = ProviderMixin.registered.find { |k| k.id == id } raise Protege::ContractViolationError, "no provider registered for #{id.inspect}" unless klass klass.new end |