From 85e5d75f5c3ca4ab8f5f4b85e7b95c395dfe6290 Mon Sep 17 00:00:00 2001 From: kadekillary Date: Sun, 8 Feb 2026 13:44:58 -0600 Subject: [PATCH 1/2] docs: add YARD documentation to all public methods and attributes Annotate every public attr_reader, constant, and method across 15 files with @return, @param, @raise, and @example YARD tags so `yard doc` produces complete API reference output. --- lib/langfuse.rb | 12 +++++++ lib/langfuse/api_client.rb | 50 +++++++++++++++++++++++++- lib/langfuse/cache_warmer.rb | 13 +++---- lib/langfuse/chat_prompt_client.rb | 18 +++++++++- lib/langfuse/client.rb | 32 ++++++++++++++++- lib/langfuse/config.rb | 27 ++++++++++++-- lib/langfuse/dataset_client.rb | 35 +++++++++++++++++- lib/langfuse/dataset_item_client.rb | 53 +++++++++++++++++++++++++-- lib/langfuse/observations.rb | 56 +++++++++++++++++++++++++++-- lib/langfuse/otel_setup.rb | 1 + lib/langfuse/prompt_cache.rb | 12 ++++++- lib/langfuse/propagation.rb | 2 ++ lib/langfuse/rails_cache_adapter.rb | 18 +++++++++- lib/langfuse/score_client.rb | 9 ++++- lib/langfuse/text_prompt_client.rb | 18 +++++++++- 15 files changed, 335 insertions(+), 21 deletions(-) diff --git a/lib/langfuse.rb b/lib/langfuse.rb index 0c07702..95204ae 100644 --- a/lib/langfuse.rb +++ b/lib/langfuse.rb @@ -20,10 +20,19 @@ # prompt = client.get_prompt("greeting") # module Langfuse + # Base error class for all Langfuse SDK errors class Error < StandardError; end + + # Raised when Langfuse configuration is invalid or incomplete class ConfigurationError < Error; end + + # Raised when a Langfuse API request fails class ApiError < Error; end + + # Raised when a requested resource is not found (HTTP 404) class NotFoundError < ApiError; end + + # Raised when API authentication fails (HTTP 401) class UnauthorizedError < ApiError; end end @@ -49,6 +58,7 @@ class UnauthorizedError < ApiError; end module Langfuse # rubocop:disable Metrics/ClassLength class << self + # @param configuration [Config] the global configuration object attr_writer :configuration # Returns the global configuration object @@ -293,6 +303,7 @@ def reset! # @param start_time [Time, Integer, nil] Optional start time (Time object or Unix timestamp in nanoseconds) # @param skip_validation [Boolean] Skip validation (for internal use). Defaults to false. # @return [BaseObservation] The observation wrapper (Span, Generation, or Event) + # @raise [ArgumentError] if an invalid observation type is provided # # @example Create root span # span = Langfuse.start_observation("root-operation", { input: {...} }) @@ -339,6 +350,7 @@ def start_observation(name, attrs = {}, as_type: :span, parent_span_context: nil # @param name [String] Descriptive name for the observation # @param attrs [Hash] Observation attributes (optional positional or keyword) # @param as_type [Symbol, String] Observation type (:span, :generation, :event, etc.) + # @param kwargs [Hash] Additional keyword arguments merged into observation attributes (e.g., input:, output:, metadata:) # @yield [observation] Optional block that receives the observation object # @yieldparam observation [BaseObservation] The observation object # @return [BaseObservation, Object] The observation (or block return value if block given) diff --git a/lib/langfuse/api_client.rb b/lib/langfuse/api_client.rb index 565ca49..d875314 100644 --- a/lib/langfuse/api_client.rb +++ b/lib/langfuse/api_client.rb @@ -22,7 +22,23 @@ module Langfuse # ) # class ApiClient # rubocop:disable Metrics/ClassLength - attr_reader :public_key, :secret_key, :base_url, :timeout, :logger, :cache + # @return [String] Langfuse public API key + attr_reader :public_key + + # @return [String] Langfuse secret API key + attr_reader :secret_key + + # @return [String] Base URL for Langfuse API + attr_reader :base_url + + # @return [Integer] HTTP request timeout in seconds + attr_reader :timeout + + # @return [Logger] Logger instance for debugging + attr_reader :logger + + # @return [PromptCache, nil] Optional cache for prompt responses + attr_reader :cache # Initialize a new API client # @@ -32,6 +48,7 @@ class ApiClient # rubocop:disable Metrics/ClassLength # @param timeout [Integer] HTTP request timeout in seconds # @param logger [Logger] Logger instance for debugging # @param cache [PromptCache, nil] Optional cache for prompt responses + # @return [ApiClient] def initialize(public_key:, secret_key:, base_url:, timeout: 5, logger: nil, cache: nil) @public_key = public_key @secret_key = secret_key @@ -195,6 +212,7 @@ def update_prompt(name:, version:, labels:) # # @param events [Array] Array of event hashes to send # @return [void] + # @raise [ArgumentError] if events is not an Array or is empty # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors after retries exhausted # @@ -226,6 +244,11 @@ def send_batch(events) raise ApiError, "Batch send failed: #{e.message}" end + # Shut down the API client and release resources + # + # Shuts down the cache if it supports shutdown (e.g., SWR thread pool). + # + # @return [void] def shutdown cache.shutdown if cache.respond_to?(:shutdown) end @@ -237,6 +260,9 @@ def shutdown # @return [Array] Array of dataset metadata hashes # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # datasets = api_client.list_datasets(page: 1, limit: 10) def list_datasets(page: nil, limit: nil) params = { page: page, limit: limit }.compact @@ -258,6 +284,9 @@ def list_datasets(page: nil, limit: nil) # @raise [NotFoundError] if the dataset is not found # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # data = api_client.get_dataset("my-dataset") def get_dataset(name) encoded_name = URI.encode_uri_component(name) response = connection.get("/api/public/v2/datasets/#{encoded_name}") @@ -278,6 +307,9 @@ def get_dataset(name) # @return [Hash] The created dataset data # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # data = api_client.create_dataset(name: "my-dataset", description: "QA evaluation set") def create_dataset(name:, description: nil, metadata: nil) payload = { name: name, description: description, metadata: metadata }.compact @@ -304,6 +336,13 @@ def create_dataset(name:, description: nil, metadata: nil) # @return [Hash] The created dataset item data # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # data = api_client.create_dataset_item( + # dataset_name: "my-dataset", + # input: { query: "What is Ruby?" }, + # expected_output: { answer: "A programming language" } + # ) # rubocop:disable Metrics/ParameterLists def create_dataset_item(dataset_name:, input: nil, expected_output: nil, metadata: nil, id: nil, source_trace_id: nil, @@ -332,6 +371,9 @@ def create_dataset_item(dataset_name:, input: nil, expected_output: nil, # @raise [NotFoundError] if the item is not found # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # data = api_client.get_dataset_item("item-uuid-123") def get_dataset_item(id) encoded_id = URI.encode_uri_component(id) response = connection.get("/api/public/dataset-items/#{encoded_id}") @@ -354,6 +396,9 @@ def get_dataset_item(id) # @return [Array] Array of dataset item hashes # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # items = api_client.list_dataset_items(dataset_name: "my-dataset", limit: 50) def list_dataset_items(dataset_name:, page: nil, limit: nil, source_trace_id: nil, source_observation_id: nil) params = build_dataset_items_params( @@ -379,6 +424,9 @@ def list_dataset_items(dataset_name:, page: nil, limit: nil, # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors # @note 404 responses are treated as success to keep DELETE idempotent across retries + # + # @example + # api_client.delete_dataset_item("item-uuid-123") def delete_dataset_item(id) encoded_id = URI.encode_uri_component(id) response = connection.delete("/api/public/dataset-items/#{encoded_id}") diff --git a/lib/langfuse/cache_warmer.rb b/lib/langfuse/cache_warmer.rb index 8d3ba5c..32cb556 100644 --- a/lib/langfuse/cache_warmer.rb +++ b/lib/langfuse/cache_warmer.rb @@ -20,6 +20,7 @@ module Langfuse # end # class CacheWarmer + # @return [Client] Langfuse client used for fetching prompts attr_reader :client # Initialize a new cache warmer @@ -35,8 +36,8 @@ def initialize(client: nil) # safe to call multiple times. # # @param prompt_names [Array] List of prompt names to cache - # @param versions [Hash, nil] Optional version numbers per prompt - # @param labels [Hash, nil] Optional labels per prompt + # @param versions [Hash] Optional version numbers per prompt + # @param labels [Hash] Optional labels per prompt # @return [Hash] Results with :success and :failed arrays # # @example Basic warming @@ -73,8 +74,8 @@ def warm(prompt_names, versions: {}, labels: {}) # are cached without manually specifying them. # # @param default_label [String, nil] Label to use for all prompts (default: "production") - # @param versions [Hash, nil] Optional version numbers per prompt - # @param labels [Hash, nil] Optional labels per specific prompts (overrides default_label) + # @param versions [Hash] Optional version numbers per prompt + # @param labels [Hash] Optional labels per specific prompts (overrides default_label) # @return [Hash] Results with :success and :failed arrays # # @example Auto-discover and warm all prompts with "production" label @@ -119,8 +120,8 @@ def warm_all(default_label: "production", versions: {}, labels: {}) # Useful when you want to abort deployment if cache warming fails. # # @param prompt_names [Array] List of prompt names to cache - # @param versions [Hash, nil] Optional version numbers per prompt - # @param labels [Hash, nil] Optional labels per prompt + # @param versions [Hash] Optional version numbers per prompt + # @param labels [Hash] Optional labels per prompt # @return [Hash] Results with :success array # @raise [CacheWarmingError] if any prompts fail to cache # diff --git a/lib/langfuse/chat_prompt_client.rb b/lib/langfuse/chat_prompt_client.rb index 1494359..6075414 100644 --- a/lib/langfuse/chat_prompt_client.rb +++ b/lib/langfuse/chat_prompt_client.rb @@ -20,7 +20,23 @@ module Langfuse # chat_prompt.labels # => ["production"] # class ChatPromptClient - attr_reader :name, :version, :labels, :tags, :config, :prompt + # @return [String] Prompt name + attr_reader :name + + # @return [Integer] Prompt version number + attr_reader :version + + # @return [Array] Labels assigned to this prompt + attr_reader :labels + + # @return [Array] Tags assigned to this prompt + attr_reader :tags + + # @return [Hash] Prompt configuration + attr_reader :config + + # @return [Array] Array of message hashes with role and content + attr_reader :prompt # Initialize a new chat prompt client # diff --git a/lib/langfuse/client.rb b/lib/langfuse/client.rb index cc75338..d594c37 100644 --- a/lib/langfuse/client.rb +++ b/lib/langfuse/client.rb @@ -19,11 +19,16 @@ module Langfuse # # rubocop:disable Metrics/ClassLength class Client - attr_reader :config, :api_client + # @return [Config] The client configuration + attr_reader :config + + # @return [ApiClient] The underlying API client + attr_reader :api_client # Initialize a new Langfuse client # # @param config [Config] Configuration object + # @return [Client] def initialize(config) @config = config @config.validate! @@ -356,6 +361,9 @@ def shutdown # @return [DatasetClient] The created dataset client # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # dataset = client.create_dataset(name: "my-dataset", description: "QA evaluation set") def create_dataset(name:, description: nil, metadata: nil) data = api_client.create_dataset(name: name, description: description, metadata: metadata) DatasetClient.new(data) @@ -368,6 +376,9 @@ def create_dataset(name:, description: nil, metadata: nil) # @raise [NotFoundError] if the dataset is not found # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # dataset = client.get_dataset("my-dataset") def get_dataset(name) data = api_client.get_dataset(name) DatasetClient.new(data) @@ -380,6 +391,9 @@ def get_dataset(name) # @return [Array] Array of dataset metadata hashes # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # datasets = client.list_datasets(page: 1, limit: 10) def list_datasets(page: nil, limit: nil) api_client.list_datasets(page: page, limit: limit) end @@ -397,6 +411,13 @@ def list_datasets(page: nil, limit: nil) # @return [DatasetItemClient] The created dataset item client # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # item = client.create_dataset_item( + # dataset_name: "my-dataset", + # input: { query: "What is Ruby?" }, + # expected_output: { answer: "A programming language" } + # ) # rubocop:disable Metrics/ParameterLists def create_dataset_item(dataset_name:, input: nil, expected_output: nil, metadata: nil, id: nil, source_trace_id: nil, @@ -417,6 +438,9 @@ def create_dataset_item(dataset_name:, input: nil, expected_output: nil, # @raise [NotFoundError] if the item is not found # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # item = client.get_dataset_item("item-uuid-123") def get_dataset_item(id) data = api_client.get_dataset_item(id) DatasetItemClient.new(data) @@ -432,6 +456,9 @@ def get_dataset_item(id) # @return [Array] Array of dataset item clients # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors + # + # @example + # items = client.list_dataset_items(dataset_name: "my-dataset", limit: 50) def list_dataset_items(dataset_name:, page: nil, limit: nil, source_trace_id: nil, source_observation_id: nil) items = api_client.list_dataset_items( @@ -448,6 +475,9 @@ def list_dataset_items(dataset_name:, page: nil, limit: nil, # @raise [UnauthorizedError] if authentication fails # @raise [ApiError] for other API errors # @note 404 responses are treated as success to keep DELETE idempotent across retries + # + # @example + # client.delete_dataset_item("item-uuid-123") def delete_dataset_item(id) api_client.delete_dataset_item(id) nil diff --git a/lib/langfuse/config.rb b/lib/langfuse/config.rb index bdc9993..734e66b 100644 --- a/lib/langfuse/config.rb +++ b/lib/langfuse/config.rb @@ -68,27 +68,50 @@ class Config # @return [Symbol] ActiveJob queue name for async processing attr_accessor :job_queue - # Default values + # @return [String] Default Langfuse API base URL DEFAULT_BASE_URL = "https://cloud.langfuse.com" + + # @return [Integer] Default HTTP request timeout in seconds DEFAULT_TIMEOUT = 5 + + # @return [Integer] Default cache TTL in seconds DEFAULT_CACHE_TTL = 60 + + # @return [Integer] Default maximum number of cached items DEFAULT_CACHE_MAX_SIZE = 1000 + + # @return [Symbol] Default cache backend DEFAULT_CACHE_BACKEND = :memory + + # @return [Integer] Default lock timeout in seconds for cache stampede protection DEFAULT_CACHE_LOCK_TIMEOUT = 10 + + # @return [Boolean] Default stale-while-revalidate setting DEFAULT_CACHE_STALE_WHILE_REVALIDATE = false + + # @return [Integer] Default number of background threads for cache refresh DEFAULT_CACHE_REFRESH_THREADS = 5 + + # @return [Boolean] Default async processing setting DEFAULT_TRACING_ASYNC = true + + # @return [Integer] Default number of events to batch before sending DEFAULT_BATCH_SIZE = 50 + + # @return [Integer] Default flush interval in seconds DEFAULT_FLUSH_INTERVAL = 10 + + # @return [Symbol] Default ActiveJob queue name DEFAULT_JOB_QUEUE = :default - # Number of seconds representing indefinite cache duration (~1000 years) + # @return [Integer] Number of seconds representing indefinite cache duration (~1000 years) INDEFINITE_SECONDS = 1000 * 365 * 24 * 60 * 60 # Initialize a new Config object # # @yield [config] Optional block for configuration # @yieldparam config [Config] The config instance + # @return [Config] a new Config instance def initialize @public_key = ENV.fetch("LANGFUSE_PUBLIC_KEY", nil) @secret_key = ENV.fetch("LANGFUSE_SECRET_KEY", nil) diff --git a/lib/langfuse/dataset_client.rb b/lib/langfuse/dataset_client.rb index 1800b80..912cacb 100644 --- a/lib/langfuse/dataset_client.rb +++ b/lib/langfuse/dataset_client.rb @@ -1,11 +1,41 @@ # frozen_string_literal: true module Langfuse + # Client wrapper for a Langfuse dataset + # + # Wraps raw API response data for a dataset, providing typed accessors + # and lazy-loaded dataset items. + # + # @example + # dataset = DatasetClient.new(api_response_hash) + # dataset.name # => "my-eval-set" + # dataset.items # => [DatasetItemClient, ...] + # class DatasetClient include TimestampParser - attr_reader :id, :name, :description, :metadata, :created_at, :updated_at + # @return [String] Unique identifier for the dataset + attr_reader :id + # @return [String] Human-readable name of the dataset + attr_reader :name + + # @return [String, nil] Optional description of the dataset + attr_reader :description + + # @return [Hash] Additional metadata as key-value pairs + attr_reader :metadata + + # @return [Time, nil] Timestamp when the dataset was created + attr_reader :created_at + + # @return [Time, nil] Timestamp when the dataset was last updated + attr_reader :updated_at + + # Initialize a new dataset client from API response data + # + # @param dataset_data [Hash] Raw dataset data from the API + # @raise [ArgumentError] if dataset_data is not a Hash or missing required fields def initialize(dataset_data) validate_dataset_data!(dataset_data) @id = dataset_data["id"] @@ -17,6 +47,9 @@ def initialize(dataset_data) @raw_items = dataset_data["items"] || [] end + # Lazily-parsed dataset items + # + # @return [Array] Items belonging to this dataset def items @items ||= @raw_items.map { |item_data| DatasetItemClient.new(item_data) } end diff --git a/lib/langfuse/dataset_item_client.rb b/lib/langfuse/dataset_item_client.rb index fb13a29..4632475 100644 --- a/lib/langfuse/dataset_item_client.rb +++ b/lib/langfuse/dataset_item_client.rb @@ -1,13 +1,53 @@ # frozen_string_literal: true module Langfuse + # Client wrapper for a Langfuse dataset item + # + # Wraps raw API response data for a single dataset item, providing typed + # accessors and status query helpers. + # + # @example + # item = DatasetItemClient.new(api_response_hash) + # item.input # => { "query" => "What is Ruby?" } + # item.active? # => true + # class DatasetItemClient include TimestampParser - attr_reader :id, :dataset_id, :input, :expected_output, :metadata, - :source_trace_id, :source_observation_id, :status, - :created_at, :updated_at + # @return [String] Unique identifier for the dataset item + attr_reader :id + # @return [String] Identifier of the parent dataset + attr_reader :dataset_id + + # @return [Object, nil] Input data for the dataset item + attr_reader :input + + # @return [Object, nil] Expected output for evaluation + attr_reader :expected_output + + # @return [Hash] Additional metadata as key-value pairs + attr_reader :metadata + + # @return [String, nil] Trace ID that produced this item + attr_reader :source_trace_id + + # @return [String, nil] Observation ID that produced this item + attr_reader :source_observation_id + + # @return [String] Item status (ACTIVE or ARCHIVED) + attr_reader :status + + # @return [Time, nil] Timestamp when the item was created + attr_reader :created_at + + # @return [Time, nil] Timestamp when the item was last updated + attr_reader :updated_at + + # Initialize a new dataset item client from API response data + # + # @param item_data [Hash] Raw item data from the API + # @raise [ArgumentError] if item_data is not a Hash or missing required fields def initialize(item_data) validate_item_data!(item_data) @id = item_data["id"] @@ -22,7 +62,14 @@ def initialize(item_data) @updated_at = parse_timestamp(item_data["updatedAt"]) end + # Whether this item is active + # + # @return [Boolean] def active? = status == "ACTIVE" + + # Whether this item is archived + # + # @return [Boolean] def archived? = status == "ARCHIVED" private diff --git a/lib/langfuse/observations.rb b/lib/langfuse/observations.rb index 69d5488..65add9d 100644 --- a/lib/langfuse/observations.rb +++ b/lib/langfuse/observations.rb @@ -58,7 +58,14 @@ module Langfuse # # @abstract Subclass and pass type: to super to create concrete observation types class BaseObservation - attr_reader :otel_span, :otel_tracer, :type + # @return [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + attr_reader :otel_span + + # @return [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + attr_reader :otel_tracer + + # @return [String] Observation type (e.g., "span", "generation", "event") + attr_reader :type # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer @@ -95,7 +102,10 @@ def trace_url Langfuse.client.trace_url(trace_id) end + # Ends the observation span. + # # @param end_time [Time, Integer, nil] Optional end time (Time object or Unix timestamp in nanoseconds) + # @return [void] def end(end_time: nil) @otel_span.finish(end_timestamp: end_time) end @@ -154,6 +164,7 @@ def start_observation(name, attrs = {}, as_type: :span, &block) # Sets observation-level input attributes. # # @param value [Object] Input value (will be JSON-encoded) + # @return [void] def input=(value) update_observation_attributes(input: value) end @@ -161,24 +172,29 @@ def input=(value) # Sets observation-level output attributes. # # @param value [Object] Output value (will be JSON-encoded) + # @return [void] def output=(value) update_observation_attributes(output: value) end # @param value [Hash] Metadata hash (expanded into individual langfuse.observation.metadata.* attributes) + # @return [void] def metadata=(value) update_observation_attributes(metadata: value) end # @param value [String] Level (DEBUG, DEFAULT, WARNING, ERROR) + # @return [void] def level=(value) update_observation_attributes(level: value) end + # Adds an event to this observation's span. + # # @param name [String] Event name # @param input [Object, nil] Optional event data # @param level [String] Log level (debug, default, warning, error) - # + # @return [void] def event(name:, input: nil, level: "default") attributes = { "langfuse.observation.input" => input&.to_json, @@ -245,6 +261,9 @@ def normalize_prompt(prompt) # span.end # class Span < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::SpanAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:span]) end @@ -281,6 +300,9 @@ def update(attrs) # gen.end # class Generation < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::GenerationAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:generation]) end @@ -293,6 +315,7 @@ def update(attrs) end # @param value [Hash] Usage hash with token counts (:prompt_tokens, :completion_tokens, :total_tokens) + # @return [void] def usage=(value) return unless @otel_span.recording? @@ -308,6 +331,7 @@ def usage=(value) end # @param value [String] Model name (e.g., "gpt-4", "claude-3-opus") + # @return [void] def model=(value) return unless @otel_span.recording? @@ -315,6 +339,7 @@ def model=(value) end # @param value [Hash] Model parameters (temperature, max_tokens, etc.) + # @return [void] def model_parameters=(value) return unless @otel_span.recording? @@ -346,6 +371,9 @@ def model_parameters=(value) # # Event is automatically ended # class Event < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::SpanAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:event]) end @@ -380,6 +408,9 @@ def update(attrs) # agent.end # class Agent < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::SpanAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:agent]) end @@ -410,6 +441,9 @@ def update(attrs) # tool.end # class Tool < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::SpanAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:tool]) end @@ -449,6 +483,9 @@ def update(attrs) # chain.end # class Chain < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::SpanAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:chain]) end @@ -482,6 +519,9 @@ def update(attrs) # retriever.end # class Retriever < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::SpanAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:retriever]) end @@ -515,6 +555,9 @@ def update(attrs) # evaluator.end # class Evaluator < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::SpanAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:evaluator]) end @@ -548,6 +591,9 @@ def update(attrs) # guardrail.end # class Guardrail < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::SpanAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:guardrail]) end @@ -586,6 +632,9 @@ def update(attrs) # embedding.end # class Embedding < BaseObservation + # @param otel_span [OpenTelemetry::SDK::Trace::Span] The underlying OTel span + # @param otel_tracer [OpenTelemetry::SDK::Trace::Tracer] The OTel tracer + # @param attributes [Hash, Types::EmbeddingAttributes, nil] Optional initial attributes def initialize(otel_span, otel_tracer, attributes: nil) super(otel_span, otel_tracer, attributes: attributes, type: OBSERVATION_TYPES[:embedding]) end @@ -598,16 +647,19 @@ def update(attrs) end # @param value [Hash] Usage hash with token counts (:prompt_tokens, :total_tokens) + # @return [void] def usage=(value) update_observation_attributes(usage_details: value) end # @param value [String] Model name (e.g., "text-embedding-ada-002") + # @return [void] def model=(value) update_observation_attributes(model: value) end # @param value [Hash] Model parameters (temperature, max_tokens, etc.) + # @return [void] def model_parameters=(value) update_observation_attributes(model_parameters: value) end diff --git a/lib/langfuse/otel_setup.rb b/lib/langfuse/otel_setup.rb index 7f7a074..846c66d 100644 --- a/lib/langfuse/otel_setup.rb +++ b/lib/langfuse/otel_setup.rb @@ -13,6 +13,7 @@ module Langfuse # module OtelSetup class << self + # @return [OpenTelemetry::SDK::Trace::TracerProvider, nil] The configured tracer provider attr_reader :tracer_provider # Initialize OpenTelemetry with Langfuse OTLP exporter diff --git a/lib/langfuse/prompt_cache.rb b/lib/langfuse/prompt_cache.rb index 2f0925a..e3b629c 100644 --- a/lib/langfuse/prompt_cache.rb +++ b/lib/langfuse/prompt_cache.rb @@ -53,7 +53,17 @@ def expired? end end - attr_reader :ttl, :max_size, :stale_ttl, :logger + # @return [Integer] Time-to-live in seconds + attr_reader :ttl + + # @return [Integer] Maximum number of cache entries + attr_reader :max_size + + # @return [Integer] Stale TTL for SWR in seconds + attr_reader :stale_ttl + + # @return [Logger] Logger instance for error reporting + attr_reader :logger # Initialize a new cache # diff --git a/lib/langfuse/propagation.rb b/lib/langfuse/propagation.rb index 07f92b0..735621a 100644 --- a/lib/langfuse/propagation.rb +++ b/lib/langfuse/propagation.rb @@ -60,6 +60,7 @@ module Propagation # @param as_baggage [Boolean] If true, propagates via OpenTelemetry baggage for cross-service propagation # @yield Block within which attributes are propagated # @return [Object] The result of the block + # @raise [ArgumentError] if no block is given # # @example Basic usage # Langfuse.propagate_attributes(user_id: "user_123", session_id: "session_abc") do @@ -329,6 +330,7 @@ def self._validate_string_value(value, key) # # @param key [String] Attribute key (user_id, session_id, etc.) # @return [OpenTelemetry::Context::Key] Context key object + # @raise [ArgumentError] if key is not a known propagated attribute # # @api private def self._get_propagated_context_key(key) diff --git a/lib/langfuse/rails_cache_adapter.rb b/lib/langfuse/rails_cache_adapter.rb index 99edb16..319b24c 100644 --- a/lib/langfuse/rails_cache_adapter.rb +++ b/lib/langfuse/rails_cache_adapter.rb @@ -17,7 +17,23 @@ module Langfuse class RailsCacheAdapter include StaleWhileRevalidate - attr_reader :ttl, :namespace, :lock_timeout, :stale_ttl, :thread_pool, :logger + # @return [Integer] Time-to-live in seconds + attr_reader :ttl + + # @return [String] Cache key namespace + attr_reader :namespace + + # @return [Integer] Lock timeout in seconds for stampede protection + attr_reader :lock_timeout + + # @return [Integer] Stale TTL for SWR in seconds + attr_reader :stale_ttl + + # @return [Concurrent::CachedThreadPool, nil] Thread pool for background refreshes + attr_reader :thread_pool + + # @return [Logger] Logger instance for error reporting + attr_reader :logger # Initialize a new Rails.cache adapter # diff --git a/lib/langfuse/score_client.rb b/lib/langfuse/score_client.rb index 8497af0..7d01068 100644 --- a/lib/langfuse/score_client.rb +++ b/lib/langfuse/score_client.rb @@ -22,7 +22,14 @@ module Langfuse # @api private # rubocop:disable Metrics/ClassLength class ScoreClient - attr_reader :api_client, :config, :logger + # @return [ApiClient] The API client for sending batches + attr_reader :api_client + + # @return [Config] Configuration object + attr_reader :config + + # @return [Logger] Logger instance + attr_reader :logger # Initialize a new ScoreClient # diff --git a/lib/langfuse/text_prompt_client.rb b/lib/langfuse/text_prompt_client.rb index 20f7eaa..d559ba5 100644 --- a/lib/langfuse/text_prompt_client.rb +++ b/lib/langfuse/text_prompt_client.rb @@ -20,7 +20,23 @@ module Langfuse # text_prompt.labels # => ["production"] # class TextPromptClient - attr_reader :name, :version, :labels, :tags, :config, :prompt + # @return [String] Prompt name + attr_reader :name + + # @return [Integer] Prompt version number + attr_reader :version + + # @return [Array] Labels assigned to this prompt + attr_reader :labels + + # @return [Array] Tags assigned to this prompt + attr_reader :tags + + # @return [Hash] Prompt configuration + attr_reader :config + + # @return [String] Raw prompt template string + attr_reader :prompt # Initialize a new text prompt client # From 5b5598098c8e9d9efa7f8a1155fa1bc243515503 Mon Sep 17 00:00:00 2001 From: kadekillary Date: Sun, 8 Feb 2026 14:00:36 -0600 Subject: [PATCH 2/2] docs: include RailsCacheAdapter in ApiClient cache type annotations --- lib/langfuse/api_client.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/langfuse/api_client.rb b/lib/langfuse/api_client.rb index 7151875..dbac268 100644 --- a/lib/langfuse/api_client.rb +++ b/lib/langfuse/api_client.rb @@ -37,7 +37,7 @@ class ApiClient # rubocop:disable Metrics/ClassLength # @return [Logger] Logger instance for debugging attr_reader :logger - # @return [PromptCache, nil] Optional cache for prompt responses + # @return [PromptCache, RailsCacheAdapter, nil] Optional cache for prompt responses attr_reader :cache # Initialize a new API client @@ -47,7 +47,7 @@ class ApiClient # rubocop:disable Metrics/ClassLength # @param base_url [String] Base URL for Langfuse API # @param timeout [Integer] HTTP request timeout in seconds # @param logger [Logger] Logger instance for debugging - # @param cache [PromptCache, nil] Optional cache for prompt responses + # @param cache [PromptCache, RailsCacheAdapter, nil] Optional cache for prompt responses # @return [ApiClient] def initialize(public_key:, secret_key:, base_url:, timeout: 5, logger: nil, cache: nil) @public_key = public_key