### Basic Evaluation Setup Source: https://context7_llms Explains how to set up a basic evaluation using DSPy.rb's evaluation framework. It involves creating a predictor with a signature, defining a metric (e.g., `exact_match`), and initializing an `Evaluate` object. The evaluator can then be used to evaluate against test examples, providing progress display and scoring. ```ruby # Create evaluator with a predictor and metric predictor = DSPy::Predict.new(YourSignature) metric = DSPy::Metrics.exact_match(field: :answer) evaluator = DSPy::Evaluate.new(predictor, metric: metric) # Evaluate against test examples result = evaluator.evaluate(test_examples, display_progress: true) puts "Score: #{result.score}" puts "Passed: #{result.passed_examples}/#{result.total_examples}" ``` -------------------------------- ### Install DSPy.rb Gem Source: https://context7_llms Instructions to add the DSPy.rb gem to your project's Gemfile and install it using Bundler. ```ruby gem 'dspy', '~> 0.15' ``` -------------------------------- ### SimpleOptimizer for Prompt Optimization Source: https://context7_llms Illustrates the use of `SimpleOptimizer` for basic prompt optimization in DSPy.rb. It involves creating `FewShotExample` objects with input and output pairs, then using the optimizer to compile a module instance with these examples. This process aims to improve the LLM's performance on specific tasks. ```ruby # Create training examples examples = [ DSPy::FewShotExample.new( input: { text: "Great product!" }, output: { sentiment: "positive", score: 0.9 } ), DSPy::FewShotExample.new( input: { text: "Terrible service" }, output: { sentiment: "negative", score: 0.1 } ) ] # Optimize optimizer = DSPy::SimpleOptimizer.new optimized_module = optimizer.compile( module_instance: classifier, training_examples: examples ) ``` -------------------------------- ### Usage Example: Bounding Box Detection Source: https://context7_llms Demonstrates how to use the BoundingBoxDetection signature with a DSPy Predict module to analyze an image and extract detected objects. ```ruby # Object detection with type safety detector = DSPy::Predict.new(BoundingBoxDetection) image = DSPy::Image.new(url: 'https://example.com/landscape.jpg') detection = detector.call( image: image, query: 'vehicles' ) detection.objects.each do |obj| puts "#{obj.label} at (#{obj.bbox.x}, #{obj.bbox.y})" puts "Size: #{obj.bbox.width} x #{obj.bbox.height}" end ``` -------------------------------- ### Single Example Evaluation Source: https://context7_llms Demonstrates how to evaluate a single example using a configured evaluator in DSPy.rb. The `evaluate` method is called with a single example, and the result object contains information about whether the example passed, detailed metric results, and the actual prediction made. ```ruby example = { input: { question: "What is 2+2?" }, expected: { answer: "4" } } result = evaluator.call(example) puts result.passed # => true/false puts result.metrics # => Hash with metric details puts result.prediction # => Actual prediction object ``` -------------------------------- ### Batch Evaluation Results Source: https://context7_llms Shows how to perform a batch evaluation using a list of test examples with the DSPy.rb evaluation framework. The `evaluate` method is called with the entire list of examples, returning a comprehensive result object that summarizes the overall performance. ```ruby batch_result = evaluator.evaluate(test_examples) ``` -------------------------------- ### Module Composition Source: https://context7_llms Shows how to compose multiple dspy-rb modules within a single module. The `DocumentProcessor` example chains a classifier, summarizer, and keyword extractor to process a document. ```ruby class DocumentProcessor < DSPy::Module def initialize super @classifier = DocumentClassifier.new @summarizer = DocumentSummarizer.new @extractor = KeywordExtractor.new end def forward(document:) classification = @classifier.call(content: document) summary = @summarizer.call(content: document) keywords = @extractor.call(content: document) { document_type: classification.document_type, summary: summary.summary, keywords: keywords.keywords } end end ``` -------------------------------- ### Custom Metrics for Evaluation Source: https://context7_llms Provides examples of creating custom metrics for evaluation in DSPy.rb. This includes defining metrics using Procs for simple checks and creating multi-factor metrics that calculate a score based on multiple criteria like accuracy, completeness, and confidence. ```ruby # Custom metric as proc custom_metric = ->(example, prediction) do return false unless prediction && prediction.respond_to?(:answer) prediction.answer.downcase.strip == example.expected_answer.downcase.strip end # Multi-factor custom metric quality_metric = ->(example, prediction) do return 0.0 unless prediction score = 0.0 score += 0.5 if prediction.answer == example.expected_answer # Accuracy score += 0.3 if prediction.explanation&.length&.> 50 # Completeness score += 0.2 if prediction.confidence&.> 0.8 # Confidence score end evaluator = DSPy::Evaluate.new(predictor, metric: quality_metric) ``` -------------------------------- ### Overall Metrics Source: https://context7_llms Access overall performance metrics from a batch result. Metrics include score, total examples, passed examples, and pass rate. ```ruby puts batch_result.score # 0.0 to 1.0 puts batch_result.total_examples # Total count puts batch_result.passed_examples # Passed count puts batch_result.pass_rate # Passed/Total ratio ``` -------------------------------- ### DSPy.rb Image Input Error Handling Source: https://context7_llms Provides examples of error handling for multimodal features in DSPy.rb, covering cases where a non-vision model is used or when a provider has incompatible image feature support (e.g., Anthropic not supporting image URLs). ```ruby # Non-vision model error begin non_vision_lm = DSPy::LM.new('openai/gpt-3.5-turbo', api_key: ENV['OPENAI_API_KEY']) image = DSPy::Image.new(url: 'https://example.com/image.jpg') non_vision_lm.raw_chat do |messages| messages.user_with_image('What is this?', image) end rescue ArgumentError => e puts "Error: #{e.message}" # Model does not support vision end # Provider compatibility error begin anthropic_lm = DSPy::LM.new('anthropic/claude-3-sonnet', api_key: ENV['ANTHROPIC_API_KEY']) image = DSPy::Image.new(url: 'https://example.com/image.jpg') anthropic_lm.raw_chat do |messages| messages.user_with_image('What is this?', image) end rescue DSPy::LM::IncompatibleImageFeatureError => e puts "Error: #{e.message}" # Anthropic doesn't support image URLs end ``` -------------------------------- ### Email Classifier Service with DSPy Source: https://context7_llms An example of a Rails service that uses DSPy's ChainOfThought module to classify emails. It takes email content and sender information as input. ```ruby # app/services/email_classifier_service.rb class EmailClassifierService def initialize @classifier = DSPy::ChainOfThought.new(EmailClassifier) end def classify(email) @classifier.call( email_content: email.body, sender: email.from ) end end ``` -------------------------------- ### DSPy ReAct and CodeAct Usage Source: https://context7_llms Demonstrates the initialization of DSPy's ReAct and CodeAct modules with their respective signatures and configurations. ReAct is configured with a maximum of 10 iterations and tools, while CodeAct uses a maximum of 8 iterations. ```APIDOC #### DSPy::ReAct - `new(signature, tools:, max_iterations: 10)` - Returns result with tool call history #### DSPy::CodeAct - `new(signature, max_iterations: 8)` - Returns solution and execution history ``` -------------------------------- ### Create Weather Toolset Source: https://context7_llms Demonstrates creating a WeatherToolset by inheriting from DSPy::Tools::Toolset. It defines two tools, `get_current` and `get_forecast`, with descriptions. The `tool` macro is used to register these tools. The actual implementation for fetching weather data is stubbed. The toolset can be converted to tool instances for use with agents. ```ruby class WeatherToolset < DSPy::Tools::Toolset toolset_name "weather" tool :get_current, tool_name: "weather_current", description: "Get current weather" tool :get_forecast, description: "Get weather forecast" def get_current(location:) # Actual implementation would call weather API "#{location}: 22°C, sunny" end def get_forecast(location:, days: 7) "#{days}-day forecast for #{location}" end end # Convert to tool instances for agents tools = WeatherToolset.to_tools agent = DSPy::ReAct.new(WeatherSignature, tools: tools) ``` -------------------------------- ### Advanced DSPy.rb Configuration Source: https://context7_llms Shows advanced configuration options for DSPy.rb, including setting specific language model parameters like `temperature` and `max_tokens`, and configuring a JSON logger for observability. ```ruby DSPy.configure do |c| # Language Model c.lm = DSPy::LM.new('openai/gpt-4o-mini', api_key: ENV['OPENAI_API_KEY'], temperature: 0.7, max_tokens: 2000 ) # Observability c.logger = Dry.Logger(:dspy, formatter: :json) do |logger| logger.add_backend(level: :info, stream: $stdout) end end ``` -------------------------------- ### Basic DSPy.rb Configuration Source: https://context7_llms Demonstrates how to configure DSPy.rb to use different language models (OpenAI, Anthropic, Ollama) by setting the `lm` attribute in the configuration block. ```ruby require 'dspy' # Configure with OpenAI DSPy.configure do |c| c.lm = DSPy::LM.new('openai/gpt-4o-mini', api_key: ENV['OPENAI_API_KEY']) end # Or configure with Anthropic DSPy.configure do |c| c.lm = DSPy::LM.new('anthropic/claude-3-sonnet', api_key: ENV['ANTHROPIC_API_KEY']) end # Or use Ollama for local models DSPy.configure do |c| c.lm = DSPy::LM.new('ollama/llama3.2') # No API key needed for local end ``` -------------------------------- ### MIPROv2 Optimizer for Advanced Optimization Source: https://context7_llms Demonstrates the advanced optimization capabilities using `MIPROv2`. This optimizer supports bootstrap sampling and allows configuration of parameters like `k_demos`, `num_candidates`, and `mode`. It compiles a module instance using training and validation data to enhance LLM performance. ```ruby optimizer = DSPy::MIPROv2.new( k_demos: 3, num_candidates: 10, mode: 'balanced' ) optimized = optimizer.compile( module_instance: complex_module, training_examples: training_data, validation_examples: val_data ) ``` -------------------------------- ### DSPy.rb Environment Variables Source: https://context7_llms Lists essential environment variables required for DSPy.rb, including API keys for LLM providers and optional variables for observability tools like OpenTelemetry and Langfuse. ```bash # LLM API Keys export OPENAI_API_KEY=sk-your-key-here export ANTHROPIC_API_KEY=sk-ant-your-key-here # Optional: Observability export OTEL_SERVICE_NAME=my-dspy-app export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 export LANGFUSE_SECRET_KEY=sk_your_key export LANGFUSE_PUBLIC_KEY=pk_your_key export NEW_RELIC_LICENSE_KEY=your_license_key ``` -------------------------------- ### DSPy.rb Basic Signature Structure Source: https://context7_llms Illustrates the fundamental structure for defining a DSPy.rb signature, including a description, input fields, and output fields. ```ruby class TaskSignature < DSPy::Signature description "Clear description of what this signature accomplishes" input do const :field_name, String end output do const :result_field, String end end ``` -------------------------------- ### Creating Custom Modules Source: https://context7_llms Demonstrates the creation of a custom dspy-rb module by inheriting from `DSPy::Module` and initializing a predictor within the `initialize` method. The `forward` method defines the module's execution logic. ```ruby class SentimentAnalyzer < DSPy::Module def initialize super @predictor = DSPy::Predict.new(SentimentSignature) end def forward(text:) @predictor.call(text: text) end end ``` -------------------------------- ### Using Images with DSPy.rb Raw Chat Source: https://context7_llms Illustrates how to send single or multiple images to a vision-capable LLM using DSPy.rb's raw_chat interface, including setting system prompts. ```ruby # Configure with vision-capable model lm = DSPy::LM.new('openai/gpt-4o-mini', api_key: ENV['OPENAI_API_KEY']) # Single image analysis image = DSPy::Image.new(url: "https://example.com/photo.jpg") response = lm.raw_chat do |messages| messages.user_with_image('What is in this image?', image) end puts response # String response # Multiple images image1 = DSPy::Image.new(url: "https://example.com/before.jpg") image2 = DSPy::Image.new(url: "https://example.com/after.jpg") response = lm.raw_chat do |messages| messages.user_with_images('Compare these images', [image1, image2]) end # With system prompt response = lm.raw_chat do |messages| messages.system('You are an expert image analyst.') messages.user_with_image('Analyze this image in detail.', image) end ``` -------------------------------- ### DSPy.rb Image Input Types Source: https://context7_llms Shows various ways to create DSPy::Image objects for multimodal LLM interactions, including from URLs, base64 strings, and byte arrays, with provider-specific options like detail level. ```ruby # URL-based images (OpenAI only) image = DSPy::Image.new(url: "https://example.com/image.jpg") # Base64 encoded images (both providers) image = DSPy::Image.new( base64: base64_string, content_type: "image/jpeg" ) # Byte array images (both providers) File.open("image.jpg", "rb") do |file| image = DSPy::Image.new( data: file.read, content_type: "image/jpeg" ) end # With detail level (OpenAI only) image = DSPy::Image.new( url: "https://example.com/image.jpg", detail: "high" ) ``` -------------------------------- ### Dynamic Module Configuration with Strategies Source: https://context7_llms Demonstrates a dynamic module in DSPy that can switch between different strategies (Predict, ChainOfThought) based on a complexity parameter during runtime. ```ruby class AdaptiveModule < DSPy::Module def initialize super @strategies = { simple: DSPy::Predict.new(SimpleSignature), complex: DSPy::ChainOfThought.new(ComplexSignature) } end def forward(input:, complexity: :simple) strategy = @strategies[complexity] strategy.call(input: input) end end ``` -------------------------------- ### Predict Predictor Usage Source: https://context7_llms Demonstrates the basic usage of the `DSPy::Predict` predictor for direct LLM calls based on a defined signature. It shows how to instantiate the predictor and call it with input arguments. ```ruby predictor = DSPy::Predict.new(EmailClassifier) result = predictor.call( email_content: "My order hasn't arrived", sender: "customer@example.com" ) ``` -------------------------------- ### DSPy Strategy Selection Source: https://context7_llms Demonstrates how to select the DSPy strategy for provider optimization. Options include 'Strict' for provider-optimized performance and 'Compatible' for broader compatibility. ```ruby # Automatic provider optimization DSPy.configure do |c| c.strategy = DSPy::Strategy::Strict # Provider-optimized # or c.strategy = DSPy::Strategy::Compatible # Works everywhere end ``` -------------------------------- ### DSPy.rb OpenAI Provider Image Usage Source: https://context7_llms Demonstrates using image inputs with the OpenAI provider in DSPy.rb, specifically highlighting that URL-based images are supported. ```ruby lm = DSPy::LM.new('openai/gpt-4o-mini', api_key: ENV['OPENAI_API_KEY']) # URL images work image = DSPy::Image.new(url: 'https://example.com/image.jpg') response = lm.raw_chat do |messages| messages.user_with_image('What color is this?', image) end ``` -------------------------------- ### Integration Testing with VCR Source: https://context7_llms Perform integration tests for DSPy modules by recording and replaying LLM interactions using VCR. This ensures consistent testing without repeated API calls. ```ruby RSpec.describe "LLM Integration", vcr: true do let(:predictor) { DSPy::Predict.new(AnalysisSignature) } it "analyzes text with real LLM" do result = predictor.call(text: "Sample text") expect(result).to respond_to(:analysis) end end ``` -------------------------------- ### OpenTelemetry Integration Source: https://context7_llms Integrate DSPy with OpenTelemetry by configuring the SDK and forwarding DSPy logs to a specified file for trace analysis. ```ruby require 'opentelemetry/sdk' # Configure OTEL OpenTelemetry::SDK.configure do |c| c.service_name = 'my-dspy-app' c.use 'OpenTelemetry::Instrumentation::Net::HTTP' end # DSPy logs can be forwarded to OTEL DSPy.configure do |c| c.logger = Dry.Logger(:dspy, formatter: :json) do |logger| logger.add_backend(stream: "/var/log/dspy/traces.json") end end ``` -------------------------------- ### Per-Instance LM Configuration Source: https://context7_llms Explains how to configure a specific Language Model (LM) instance for a dspy-rb module. This allows overriding the default LM or setting specific parameters like API keys. ```ruby module = DSPy::ChainOfThought.new(SignatureClass) module.configure do |config| config.lm = DSPy::LM.new('anthropic/claude-3-opus', api_key: ENV['ANTHROPIC_API_KEY'] ) end ``` -------------------------------- ### Custom Agent Implementation (Chain of Thought + CodeAct) Source: https://context7_llms Demonstrates building a custom agent by composing DSPy modules: ChainOfThought for planning, CodeAct for execution, and Predict for validation. It processes a task through these stages. ```ruby class CustomAgent < DSPy::Module def initialize super @planner = DSPy::ChainOfThought.new(PlanningSignature) # Assuming PlanningSignature is defined @executor = DSPy::CodeAct.new(ExecutionSignature) # Assuming ExecutionSignature is defined @validator = DSPy::Predict.new(ValidationSignature) # Assuming ValidationSignature is defined end def forward(task:) plan = @planner.call(task: task) execution = @executor.call(plan: plan.plan) validation = @validator.call(result: execution.solution) { result: execution.solution, confidence: validation.confidence } end end ``` -------------------------------- ### Memory Integration with Agents Source: https://context7_llms Illustrates how to integrate memory tools into a ReAct agent for a personal assistant. The agent can access and utilize stored memories to respond to user messages. ```ruby class PersonalAssistant < DSPy::Module def initialize super memory_tools = DSPy::Tools::MemoryToolset.to_tools @agent = DSPy::ReAct.new( AssistantSignature, # Assuming AssistantSignature is defined tools: memory_tools ) end def forward(user_message:, user_id:) @agent.call( user_message: user_message, user_id: user_id ) end end ``` -------------------------------- ### Basic Memory Operations Source: https://context7_llms Shows how to configure and use DSPy's in-memory storage adapter for storing, retrieving, and searching memories associated with user IDs and tags. ```ruby # Initialize memory DSPy::Memory.configure do |config| config.storage_adapter = :in_memory # or :redis end # Store memory memory_id = DSPy::Memory.manager.store_memory( "User prefers dark mode", user_id: "user123", tags: ["preferences", "ui"] ) # Retrieve memory memory = DSPy::Memory.manager.retrieve_memory(memory_id) # Search memories memories = DSPy::Memory.manager.search_memories( user_id: "user123", tags: ["preferences"] ) ``` -------------------------------- ### DSPy.rb Anthropic Provider Image Usage Source: https://context7_llms Shows how to use image inputs with the Anthropic provider in DSPy.rb, emphasizing that only base64 or raw data is supported, not URLs. ```ruby lm = DSPy::LM.new('anthropic/claude-4', api_key: ENV['ANTHROPIC_API_KEY']) # Must use base64 or raw data File.open('image.jpg', 'rb') do |file| image = DSPy::Image.new( data: file.read, content_type: 'image/jpeg' ) response = lm.raw_chat do |messages| messages.system('You are a color detection expert.') messages.user_with_image('What color?', image) end end ``` -------------------------------- ### CodeAct Predictor Usage Source: https://context7_llms Shows how to use the `DSPy::CodeAct` predictor, an agent focused on dynamic code generation. It takes a signature and can execute code to arrive at a solution, providing both the final answer and execution history. ```ruby agent = DSPy::CodeAct.new( DataAnalysisSignature, max_iterations: 8 ) result = agent.call(task: "Analyze this CSV data...") puts result.solution # Final answer puts result.history # Execution steps ``` -------------------------------- ### Rails Integration for DSPy Source: https://context7_llms Configures DSPy within a Rails application's initializers, setting up the language model using Rails credentials and configuring a structured logger based on the environment. ```ruby # config/initializers/dspy.rb Rails.application.config.after_initialize do DSPy.configure do |c| c.lm = DSPy::LM.new( Rails.application.credentials.llm_model, api_key: Rails.application.credentials.llm_api_key ) c.logger = Dry.Logger(:dspy, formatter: Rails.env.production? ? :json : :string) end end ``` -------------------------------- ### ReAct Predictor Usage Source: https://context7_llms Demonstrates the `DSPy::ReAct` predictor, a tool-using agent that combines reasoning with external tools. It requires defining tools (like CalculatorTool or MemoryToolset) and a signature for the agent's task. ```ruby # Define tools calculator = DSPy::Tools::CalculatorTool.new memory_tools = DSPy::Tools::MemoryToolset.to_tools # Create agent agent = DSPy::ReAct.new( ResearchSignature, tools: [calculator, *memory_tools], max_iterations: 10 ) result = agent.call(query: "Calculate compound interest...") ``` -------------------------------- ### DSPy Batch Processing Source: https://context7_llms Shows how to process multiple items efficiently using DSPy by mapping a predictor call over a list of inputs. ```python # Process multiple items efficiently items = ["text1", "text2", "text3"] results = items.map do |item| predictor.call(text: item) end ``` -------------------------------- ### ReAct Agent Implementation Source: https://context7_llms Implements a ReAct agent for research tasks, integrating a calculator tool and memory tools. The agent takes a query and returns a result based on its reasoning and actions. ```ruby class ResearchAssistant < DSPy::Module def initialize super # Create tools calculator = DSPy::Tools::CalculatorTool.new memory_tools = DSPy::Tools::MemoryToolset.to_tools @agent = DSPy::ReAct.new( ResearchSignature, # Assuming ResearchSignature is defined elsewhere tools: [calculator, *memory_tools] ) end def forward(query:) @agent.call(query: query) end end ``` -------------------------------- ### Default Values in Signatures (v0.7.0+) Source: https://context7_llms Demonstrates how to set default values for input parameters in a dspy-rb signature. This simplifies usage by providing sensible defaults when specific values are not provided. ```ruby class SmartSearch < DSPy::Signature description "Search with intelligent defaults" input do const :query, String const :max_results, Integer, default: 10 const :language, String, default: "English" end output do const :results, T::Array[String] const :cached, T::Boolean, default: false end end ``` -------------------------------- ### DSPy Configuration Source: https://context7_llms Configures the DSPy framework, allowing customization of the language model, logger, and extraction strategy. This is essential for tailoring DSPy's behavior to specific project needs. ```ruby DSPy.configure do |c| c.lm # Language model c.logger # Structured logger c.strategy # Extraction strategy end ``` -------------------------------- ### ProgramOfThought Predictor Usage Source: https://context7_llms Illustrates the `DSPy::ProgramOfThought` predictor, which enables the LLM to generate and execute code to solve problems. It requires a signature defining the problem and expected answer. ```ruby class MathProblem < DSPy::Signature description "Solve mathematical problems" input do const :problem, String end output do const :answer, T.any(Integer, Float, String) end end solver = DSPy::ProgramOfThought.new(MathProblem) result = solver.call(problem: "What is the sum of squares from 1 to 10?") ``` -------------------------------- ### Using Built-in Toolsets (Memory and Text Processing) Source: https://context7_llms Shows how to access and utilize DSPy.rb's built-in toolsets for memory management and text processing. The `MemoryToolset` provides functionalities for storing, retrieving, listing, updating, deleting, clearing, and counting memory entries, along with metadata operations. The `TextProcessingToolset` offers utilities for summarization, keyword extraction, token counting, translation, markdown formatting, entity extraction, sentiment analysis, text cleaning, and text comparison. ```ruby # Memory toolset with persistent storage memory_tools = DSPy::Tools::MemoryToolset.to_tools # Includes: memory_store, memory_retrieve, memory_search, # memory_list, memory_update, memory_delete, # memory_clear, memory_count, memory_get_metadata # Text processing toolset text_tools = DSPy::Tools::TextProcessingToolset.to_tools # Includes: summarize, extract_keywords, count_tokens, # translate, format_markdown, extract_entities, # sentiment_analysis, clean_text, compare_texts ``` -------------------------------- ### CodeAct Agent Implementation Source: https://context7_llms Implements a CodeAct agent for data analysis tasks. It takes a task description, generates and executes code, and returns the solution along with the execution history. ```ruby class DataAnalyst < DSPy::Module def initialize super @agent = DSPy::CodeAct.new( AnalysisSignature, # Assuming AnalysisSignature is defined elsewhere max_iterations: 8 ) end def forward(task:) result = @agent.call(task: task) { solution: result.solution, code_executed: result.history.map { |h| h[:ruby_code] } } end end ``` -------------------------------- ### Union Types for Flexible Outputs (v0.11.0+) Source: https://context7_llms Shows how to use `T.any` to define output fields that can accept multiple types. This is useful for handling different possible outcomes from an LLM call. ```ruby # Single-field unions - automatic type detection class TaskAction < DSPy::Signature output do const :action, T.any(CreateTask, UpdateTask, DeleteTask) end end ``` -------------------------------- ### Token Usage Tracking Source: https://context7_llms Track token usage by parsing DSPy log files. Extract input tokens, output tokens, and the model used from 'llm.generate' events. ```ruby # Automatic tracking with log processing File.foreach("log/dspy.log") do |line| event = JSON.parse(line) if event["event"] == "llm.generate" puts "Input tokens: #{event["gen_ai.usage.prompt_tokens"]}" puts "Output tokens: #{event["gen_ai.usage.completion_tokens"]}" puts "Model: #{event["gen_ai.request.model"]}" end end ``` -------------------------------- ### Unit Testing with RSpec Source: https://context7_llms Write unit tests for DSPy modules using RSpec. Mock inputs and assert expected outputs, including confidence scores. ```ruby RSpec.describe EmailClassifier do let(:classifier) { EmailClassifier.new } it "classifies spam correctly" do result = classifier.call( email_content: "Win a prize!", sender: "spam@example.com" ) expect(result.category).to eq("spam") expect(result.confidence).to be > 0.8 end end ``` -------------------------------- ### Configure Error Handling Source: https://context7_llms Configure the DSPy evaluator with parameters like max_errors and provide_traceback. Errors are then captured within the evaluation results. ```ruby # Configure error handling evaluator = DSPy::Evaluate.new( predictor, metric: metric, max_errors: 3, # Stop after 3 errors provide_traceback: true # Include stack traces ) # Errors are captured in results result = evaluator.evaluate(test_examples) error_count = result.results.count { |r| r.metrics[:error] } puts "#{error_count} examples failed with errors" ``` -------------------------------- ### DSPy Core Classes API Source: https://context7_llms Reference for core DSPy classes including Signature, Module, and Prediction. Details methods for defining signatures, processing logic, and handling predictions. ```APIDOC Core Classes #### DSPy::Signature - `description(text)` - Set signature description - `input { }` - Define input schema - `output { }` - Define output schema - `.input_json_schema` - Get input JSON schema - `.output_json_schema` - Get output JSON schema #### DSPy::Module - `initialize` - Constructor - `forward(**kwargs)` - Main processing method - `call(**kwargs)` - Alias for forward - `configure { |config| }` - Configure module #### DSPy::Prediction - Automatic type conversion from JSON - Access fields as methods - Handles enums, structs, arrays ``` -------------------------------- ### DSPy Support Agent with Memory Source: https://context7_llms Implements a SupportAgent using DSPy's ReAct module, incorporating a memory toolset. It first triages an email using EmailTriage and then generates a response using the ReAct agent with contextual information. ```ruby # Agent with memory class SupportAgent < DSPy::Module def initialize super memory_tools = DSPy::Tools::MemoryToolset.to_tools @triage = DSPy::ChainOfThought.new(EmailTriage) @agent = DSPy::ReAct.new( SupportResponse, tools: memory_tools ) end def forward(email:, customer_id:) # Triage email triage_result = @triage.call( subject: email.subject, body: email.body, customer_tier: email.customer.tier ) # Generate response with context response = @agent.call( email: email.body, customer_id: customer_id, priority: triage_result.priority.serialize ) { department: triage_result.department, priority: triage_result.priority, response: response.suggested_reply, should_escalate: triage_result.priority == EmailTriage::Priority::Urgent } end end ``` -------------------------------- ### Enable Observability Source: https://context7_llms Configure DSPy to use a specific logger, such as Dry.Logger, for observability. This enables span tracking for key operations. ```ruby # Enable observability DSPy.configure do |c| c.logger = Dry.Logger(:dspy, formatter: :json) end # Span tracking emitted for: # - llm.generate # - dspy.predict # - module.forward # - tool.execute ``` -------------------------------- ### DSPy Predictors API Source: https://context7_llms API reference for DSPy predictors, including Predict and ChainOfThought. Covers initialization and execution of predictions. ```APIDOC Predictors #### DSPy::Predict - `new(signature_class)` - Create predictor - `call(**inputs)` - Execute prediction #### DSPy::ChainOfThought - Adds `:reasoning` field automatically - Same API as Predict ``` -------------------------------- ### ChainOfThought Predictor Usage Source: https://context7_llms Shows how to use the `DSPy::ChainOfThought` predictor, which automatically adds a 'reasoning' field to the output. This is useful for generating step-by-step explanations for LLM responses. ```ruby # Automatically adds :reasoning field to output cot = DSPy::ChainOfThought.new(ComplexAnalysis) result = cot.call(data: complex_data) puts result.reasoning # Step-by-step explanation ``` -------------------------------- ### Create Calculator Tool with Sorbet Types Source: https://context7_llms Defines a CalculatorTool that inherits from DSPy::Tools::Base. It uses Sorbet signatures to enforce type constraints for its parameters (operation, num1, num2) and specifies the return type. The `call` method implements basic arithmetic operations and includes error handling for division by zero and unknown operations. ```ruby class CalculatorTool < DSPy::Tools::Base tool_name 'calculator' tool_description 'Performs basic arithmetic operations' sig { params(operation: String, num1: Float, num2: Float).returns(T.any(Float, String)) } def call(operation:, num1:, num2:) case operation.downcase when 'add' then num1 + num2 when 'subtract' then num1 - num2 when 'multiply' then num1 * num2 when 'divide' return "Error: Cannot divide by zero" if num2 == 0 num1 / num2 else "Error: Unknown operation '#{operation}'. Use add, subtract, multiply, or divide" end end end ``` -------------------------------- ### Registry & Storage Source: https://context7_llms Store and load compiled DSPy modules using the Registry. Modules can be stored with metadata like accuracy. ```ruby # Store compiled modules registry = DSPy::Registry.new registry.store( module_instance: optimized_classifier, name: "email_classifier_v2", metadata: { accuracy: 0.95 } ) # Load later classifier = registry.load("email_classifier_v2") ``` -------------------------------- ### Streaming Responses Implementation (Future Feature) Source: https://context7_llms A placeholder for a DSPy module designed to handle streaming responses from language models, yielding chunks of data as they are received. ```ruby # Future feature - not yet implemented class StreamingPredictor < DSPy::Module def forward(input:, &block) @lm.stream(prompt: build_prompt(input)) do |chunk| yield chunk end end end ``` -------------------------------- ### DSPy.rb Built-in Metrics Source: https://context7_llms Details the various built-in metrics available in DSPy.rb for evaluating LLM outputs. This includes exact match comparison (case-sensitive or insensitive), contains/substring matching, numeric difference with a specified tolerance, and composite metrics using logical AND operations. ```ruby # Exact match comparison exact_metric = DSPy::Metrics.exact_match(field: :answer, case_sensitive: false) # Contains/substring matching contains_metric = DSPy::Metrics.contains(field: :answer) # Numeric difference with tolerance numeric_metric = DSPy::Metrics.numeric_difference(field: :score, tolerance: 0.1) # Composite AND (all must pass) composite_metric = DSPy::Metrics.composite_and(exact_metric, contains_metric) ``` -------------------------------- ### Redis Memory Storage Configuration Source: https://context7_llms Configures DSPy's memory system to use Redis as the storage adapter, specifying the Redis client connection and a namespace for memory data. ```ruby require 'redis' DSPy::Memory.configure do |config| config.storage_adapter = :redis config.redis_client = Redis.new(url: ENV['REDIS_URL']) config.redis_namespace = 'dspy:memory' end ``` -------------------------------- ### DSPy Debug Mode Configuration Source: https://context7_llms Enables debug logging for DSPy by configuring the logger to output all events to standard output. It also demonstrates processing log events from a file. ```python DSPy.configure do |c| c.logger = Dry.Logger(:dspy) do |logger| logger.add_backend(level: :debug, stream: $stdout) end end # Process all log events File.foreach("log/dspy.log") do |line| event = JSON.parse(line) puts "Event: #{event["event"]}" puts "Attributes: #{event.select { |k, v| k.start_with?("gen_ai") || k.start_with?("dspy") }}" end ``` -------------------------------- ### Arrays of Structs Source: https://context7_llms Demonstrates how to define an output field that is an array of structs. This allows for returning collections of structured data, which can be easily iterated over. ```ruby output do const :products, T::Array[Product] end # Automatic conversion from JSON result.products.each do |product| puts "#{product.name}: $#{product.price}" end ``` -------------------------------- ### Testing Agents Source: https://context7_llms Test DSPy agents that utilize tools. Assert that the agent correctly calls tools and produces the expected output based on tool results. ```ruby RSpec.describe ResearchAssistant do let(:assistant) { ResearchAssistant.new } it "uses tools to answer questions" do result = assistant.call( query: "What is 2+2?" ) expect(result.answer).to eq("4") expect(result.tool_calls).to include( hash_including(tool: "calculator") ) end end ``` -------------------------------- ### DSPy.rb Structured Multimodal Signatures Source: https://context7_llms Defines a structured signature for image analysis using DSPy.rb, specifying inputs like image and focus, and outputs for detailed analysis including objects, colors, mood, and style. ```ruby # Image analysis with detailed extraction class ImageAnalysis < DSPy::Signature description "Analyze images comprehensively to extract objects, colors, mood, and style" input do const :image, DSPy::Image, description: 'Image to analyze' const :focus, String, default: 'general', description: 'Analysis focus' const :detail_level, String, default: 'standard', description: 'Detail level' end output do const :description, String, description: 'Overall image description' const :objects, T::Array[String], description: 'Objects detected in image' const :dominant_colors, T::Array[String], description: 'Main colors present' const :mood, String, description: 'Overall mood or atmosphere' const :style, String, description: 'Artistic style or characteristics' const :confidence, Float, description: 'Analysis confidence (0.0-1.0)' end end ``` -------------------------------- ### Union Types for Flexible Results Source: https://context7_llms Shows how to use `T.any` to define an output field that can hold values of different types. This is useful for handling scenarios where an operation might succeed or fail, returning different structures. ```ruby # Automatic type detection (v0.11.0+) output do const :result, T.any(SuccessResult, ErrorResult) end # Pattern matching case result.result when SuccessResult puts "Success: #{result.result.message}" when ErrorResult puts "Error: #{result.result.error}" end ``` -------------------------------- ### DSPy Connection Pooling Configuration Source: https://context7_llms Configures the HTTP client for DSPy, setting parameters such as HTTP timeout, maximum retries, and retry delay for managing connections. ```python # Configure HTTP client DSPy::LM.configure do |config| config.http_timeout = 30 config.max_retries = 3 config.retry_delay = 1 end ``` -------------------------------- ### Bounding Box Detection Signature Source: https://context7_llms Defines the structure for detecting objects in images with type-safe bounding box coordinates. It includes input parameters for the image and an optional query, and outputs detected objects with their labels, bounding boxes, and confidence scores. ```APIDOC class BoundingBox < T::Struct const :x, Float, description: 'Normalized x coordinate (0.0-1.0)' const :y, Float, description: 'Normalized y coordinate (0.0-1.0)' const :width, Float, description: 'Normalized width (0.0-1.0)' const :height, Float, description: 'Normalized height (0.0-1.0)' end class DetectedObject < T::Struct const :label, String, description: 'Object type/label' const :bbox, BoundingBox, description: 'Bounding box coordinates' const :confidence, Float, description: 'Detection confidence (0.0-1.0)' end class BoundingBoxDetection < DSPy::Signature description "Detect and locate objects in images with normalized bounding box coordinates" input do const :image, DSPy::Image, description: 'Image to analyze for object detection' const :query, T.any(String, NilClass), description: 'Object to detect' end output do const :objects, T::Array[DetectedObject], description: 'Detected objects with type-safe bounding boxes' const :count, Integer, description: 'Total objects detected' const :confidence, Float, description: 'Overall detection confidence' end end ``` -------------------------------- ### Defining Nested Structures in Ruby Source: https://context7_llms Demonstrates how to define nested structures using T::Struct for complex data modeling in Ruby, including arrays of nested structs. ```ruby class Company < T::Struct class Department < T::Struct const :name, String const :head, String end const :name, String const :departments, T::Array[Department] end ``` -------------------------------- ### Integration with Optimization Source: https://context7_llms Use the DSPy evaluator within optimization loops. The optimization process returns a score for each candidate predictor. ```ruby # Use evaluation in optimization loops optimizer = DSPy::MIPROv2.new(signature: YourSignature) result = optimizer.optimize(examples: train_examples) do |candidate_predictor, val_examples| evaluator = DSPy::Evaluate.new(candidate_predictor, metric: custom_metric) evaluation_result = evaluator.evaluate(val_examples, display_progress: false) evaluation_result.score # Return score for optimization end puts "Best optimized score: #{result.best_score_value}" ```