class Riffer::Providers::Base
Base class for all LLM providers. A template-method flow: subclasses implement the hooks (build_request_params, execute_generate, execute_stream, extract_token_usage, extract_content, extract_tool_calls) and the base class orchestrates them.
Constants
- REQUEST_PARAM_ATTRIBUTES
-
A deliberate whitelist — caller options outside it stay off spans.
- WIRE_SEPARATOR
-
@rbs @current_tools: Array @rbs @current_model: String?
Public Class Methods
Source
# File lib/riffer/providers/base.rb, line 30 def self.semconv_provider_name class_name = name return "unknown" unless class_name Riffer::Helpers::ClassNameConverter.convert(class_name.split("::").last.to_s) end
Returns the provider name stamped as gen_ai.provider.name on trace spans, ideally a GenAI semconv well-known value. Defaults to the snake_cased class name rather than raising like the abstract provider methods, so enabling tracing never breaks an otherwise-working custom provider.
Source
# File lib/riffer/providers/base.rb, line 20 def self.skills_adapter(model = nil) Riffer::Skills::MarkdownAdapter end
Returns the preferred skill adapter for this provider; override in subclasses (optionally introspecting model) for provider-specific formats.
Public Instance Methods
Source
# File lib/riffer/providers/base.rb, line 41 def generate_text(prompt: nil, system: nil, messages: nil, model: nil, files: nil, **options) validate_input!(prompt: prompt, system: system, messages: messages) @current_tools = options[:tools] || [] #: Array[singleton(Riffer::Tool)] @current_model = model messages = normalize_messages(prompt: prompt, system: system, messages: messages, files: files) validate_normalized_messages!(messages) messages = merge_consecutive_messages(messages) params = build_request_params(messages, model, options) in_chat_span(model, messages, options) do |span| response = execute_generate(params) content = extract_content(response) tool_calls = extract_tool_calls(response) token_usage = extract_token_usage(response) finish_reason = extract_finish_reason(response) structured_output = parse_structured_output(content) if options[:structured_output] && tool_calls.empty? Riffer::Tracing.record_usage(span, token_usage) record_finish_reason(span, finish_reason&.reason, finish_reason&.raw) capture_output(span, content: content, tool_calls: tool_calls, finish_reason: finish_reason&.reason) Riffer::Messages::Assistant.new( content, tool_calls: tool_calls, token_usage: token_usage, structured_output: structured_output, finish_reason: finish_reason&.reason ) end end
Generates text using the provider.
Source
# File lib/riffer/providers/base.rb, line 77 def stream_text(prompt: nil, system: nil, messages: nil, model: nil, files: nil, **options) validate_input!(prompt: prompt, system: system, messages: messages) @current_tools = options[:tools] || [] #: Array[singleton(Riffer::Tool)] @current_model = model messages = normalize_messages(prompt: prompt, system: system, messages: messages, files: files) validate_normalized_messages!(messages) messages = merge_consecutive_messages(messages) params = build_request_params(messages, model, options) # The enumerator body runs in its own fiber, where the fiber-local OTEL # context is empty — capture here so the chat span parents to the caller's # trace. trace_context = Riffer::Tracing.current_context Enumerator.new do |yielder| Riffer::Tracing.with_context(trace_context) do in_chat_span(model, messages, options) do |span| sink = span.recording? ? Riffer::Tracing::StreamRecorder.new(yielder) : yielder execute_stream(params, sink) record_stream_outcome(span, sink) if sink.is_a?(Riffer::Tracing::StreamRecorder) end end end end
Streams text from the provider.