Class: Riffer::Providers::Base
- Inherits:
-
Object
- Object
- Riffer::Providers::Base
- Defined in:
- lib/riffer/providers/base.rb
Overview
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.
Direct Known Subclasses
Constant Summary collapse
- WIRE_SEPARATOR =
"__"
Class Method Summary collapse
-
.semconv_provider_name ⇒ Object
Returns the provider name stamped as
gen_ai.provider.nameon trace spans, ideally a GenAI semconv well-known value. -
.skills_adapter(model = nil) ⇒ Object
Returns the preferred skill adapter for this provider; override in subclasses (optionally introspecting
model) for provider-specific formats.
Instance Method Summary collapse
-
#generate_text(prompt: nil, system: nil, messages: nil, model: nil, files: nil, **options) ⇒ Object
Generates text using the provider.
-
#stream_text(prompt: nil, system: nil, messages: nil, model: nil, files: nil, **options) ⇒ Object
Streams text from the provider.
Class Method Details
.semconv_provider_name ⇒ Object
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. – : () -> String
30 31 32 33 34 35 |
# 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 |
.skills_adapter(model = nil) ⇒ Object
Returns the preferred skill adapter for this provider; override in subclasses (optionally introspecting model) for provider-specific formats. – : (?String?) -> singleton(Riffer::Skills::Adapter)
20 21 22 |
# File 'lib/riffer/providers/base.rb', line 20 def self.skills_adapter(model = nil) Riffer::Skills::MarkdownAdapter end |
Instance Method Details
#generate_text(prompt: nil, system: nil, messages: nil, model: nil, files: nil, **options) ⇒ Object
Generates text using the provider.
– : (?prompt: String?, ?system: String?, ?messages: Array[Hash[Symbol, untyped] | Riffer::Messages::Base]?, ?model: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, **untyped) -> Riffer::Messages::Assistant
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/riffer/providers/base.rb', line 41 def generate_text(prompt: nil, system: nil, messages: nil, model: nil, files: nil, **) validate_input!(prompt: prompt, system: system, messages: ) @current_tools = [:tools] || [] #: Array[singleton(Riffer::Tool)] @current_model = model = (prompt: prompt, system: system, messages: , files: files) () = () params = build_request_params(, model, ) in_chat_span(model, , ) 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 [:structured_output] && tool_calls.empty? Riffer::Tracing.record_usage(span, token_usage) record_token_usage_metric(model, token_usage) record_cost_metric(model, 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 |
#stream_text(prompt: nil, system: nil, messages: nil, model: nil, files: nil, **options) ⇒ Object
Streams text from the provider.
– : (?prompt: String?, ?system: String?, ?messages: Array[Hash[Symbol, untyped] | Riffer::Messages::Base]?, ?model: String?, ?files: Array[Hash[Symbol, untyped] | Riffer::Messages::FilePart]?, **untyped) -> Enumerator[Riffer::StreamEvents::Base, void]
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/riffer/providers/base.rb', line 79 def stream_text(prompt: nil, system: nil, messages: nil, model: nil, files: nil, **) validate_input!(prompt: prompt, system: system, messages: ) @current_tools = [:tools] || [] #: Array[singleton(Riffer::Tool)] @current_model = model = (prompt: prompt, system: system, messages: , files: files) () = () params = build_request_params(, model, ) # 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, , ) do |span| # The recorder feeds both the span and the token-usage metric, so build # it whenever either is live — metrics fire even with tracing off. observe = span.recording? || Riffer::Metrics.recording? sink = observe ? Riffer::Tracing::StreamRecorder.new(yielder) : yielder execute_stream(params, sink) if sink.is_a?(Riffer::Tracing::StreamRecorder) record_stream_outcome(span, sink) record_token_usage_metric(model, sink.token_usage) record_cost_metric(model, sink.token_usage) end end end end end |