Files
claude-engineering-plugin/plugins/compound-engineering/skills/dspy-ruby/assets/signature-template.rb
Vicente Reig Rincón de Arellano e8f3bbcb35 refactor(skills): update dspy-ruby skill to DSPy.rb v0.34.3 API (#162)
Rewrite all reference files, asset templates, and SKILL.md to use
current API patterns (.call(), result.field, T::Enum classes,
Tools::Base). Add two new reference files (toolsets, observability)
covering tools DSL, event system, and Langfuse integration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 12:01:43 -06:00

222 lines
6.5 KiB
Ruby

# frozen_string_literal: true
# =============================================================================
# DSPy.rb Signature Template — v0.34.3 API
#
# Signatures define the interface between your application and LLMs.
# They specify inputs, outputs, and task descriptions using Sorbet types.
#
# Key patterns:
# - Use T::Enum classes for controlled outputs (not inline T.enum([...]))
# - Use description: kwarg on fields to guide the LLM
# - Use default values for optional fields
# - Use Date/DateTime/Time for temporal data (auto-converted)
# - Access results with result.field (not result[:field])
# - Invoke with predictor.call() (not predictor.forward())
# =============================================================================
# --- Basic Signature ---
class SentimentAnalysis < DSPy::Signature
description "Analyze sentiment of text"
class Sentiment < T::Enum
enums do
Positive = new('positive')
Negative = new('negative')
Neutral = new('neutral')
end
end
input do
const :text, String
end
output do
const :sentiment, Sentiment
const :score, Float, description: "Confidence score from 0.0 to 1.0"
end
end
# Usage:
# predictor = DSPy::Predict.new(SentimentAnalysis)
# result = predictor.call(text: "This product is amazing!")
# result.sentiment # => Sentiment::Positive
# result.score # => 0.92
# --- Signature with Date/Time Types ---
class EventScheduler < DSPy::Signature
description "Schedule events based on requirements"
input do
const :event_name, String
const :start_date, Date # ISO 8601: YYYY-MM-DD
const :end_date, T.nilable(Date) # Optional date
const :preferred_time, DateTime # ISO 8601 with timezone
const :deadline, Time # Stored as UTC
end
output do
const :scheduled_date, Date # LLM returns ISO string, auto-converted
const :event_datetime, DateTime # Preserves timezone
const :created_at, Time # Converted to UTC
end
end
# Date/Time format handling:
# Date → ISO 8601 (YYYY-MM-DD)
# DateTime → ISO 8601 with timezone (YYYY-MM-DDTHH:MM:SS+00:00)
# Time → ISO 8601, automatically converted to UTC
# --- Signature with Default Values ---
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"
const :include_metadata, T::Boolean, default: false
end
output do
const :results, T::Array[String]
const :total_found, Integer
const :search_time_ms, Float, default: 0.0 # Fallback if LLM omits
const :cached, T::Boolean, default: false
end
end
# Input defaults reduce boilerplate:
# search = DSPy::Predict.new(SmartSearch)
# result = search.call(query: "Ruby programming")
# # max_results=10, language="English", include_metadata=false are applied
# --- Signature with Nested Structs and Field Descriptions ---
class EntityExtraction < DSPy::Signature
description "Extract named entities from text"
class EntityType < T::Enum
enums do
Person = new('person')
Organization = new('organization')
Location = new('location')
DateEntity = new('date')
end
end
class Entity < T::Struct
const :name, String, description: "The entity text as it appears in the source"
const :type, EntityType
const :confidence, Float, description: "Extraction confidence from 0.0 to 1.0"
const :start_offset, Integer, default: 0
end
input do
const :text, String
const :entity_types, T::Array[EntityType], default: [],
description: "Filter to these entity types; empty means all types"
end
output do
const :entities, T::Array[Entity]
const :total_found, Integer
end
end
# --- Signature with Union Types ---
class FlexibleClassification < DSPy::Signature
description "Classify input with flexible result type"
class Category < T::Enum
enums do
Technical = new('technical')
Business = new('business')
Personal = new('personal')
end
end
input do
const :text, String
end
output do
const :category, Category
const :result, T.any(Float, String),
description: "Numeric score or text explanation depending on classification"
const :confidence, Float
end
end
# --- Signature with Recursive Types ---
class DocumentParser < DSPy::Signature
description "Parse document into tree structure"
class NodeType < T::Enum
enums do
Heading = new('heading')
Paragraph = new('paragraph')
List = new('list')
CodeBlock = new('code_block')
end
end
class TreeNode < T::Struct
const :node_type, NodeType, description: "The type of document element"
const :text, String, default: "", description: "Text content of the node"
const :level, Integer, default: 0
const :children, T::Array[TreeNode], default: [] # Self-reference → $defs in JSON Schema
end
input do
const :html, String, description: "Raw HTML to parse"
end
output do
const :root, TreeNode
const :word_count, Integer
end
end
# The schema generator creates #/$defs/TreeNode references for recursive types,
# compatible with OpenAI and Gemini structured outputs.
# Use `default: []` instead of `T.nilable(T::Array[...])` for OpenAI compatibility.
# --- Vision Signature ---
class ImageAnalysis < DSPy::Signature
description "Analyze an image and answer questions about its content"
input do
const :image, DSPy::Image, description: "The image to analyze"
const :question, String, description: "Question about the image content"
end
output do
const :answer, String
const :confidence, Float, description: "Confidence in the answer (0.0-1.0)"
end
end
# Vision usage:
# predictor = DSPy::Predict.new(ImageAnalysis)
# result = predictor.call(
# image: DSPy::Image.from_file("path/to/image.jpg"),
# question: "What objects are visible?"
# )
# result.answer # => "The image shows..."
# --- Accessing Schemas Programmatically ---
#
# SentimentAnalysis.input_json_schema # => { type: "object", properties: { ... } }
# SentimentAnalysis.output_json_schema # => { type: "object", properties: { ... } }
#
# # Field descriptions propagate to JSON Schema
# Entity.field_descriptions[:name] # => "The entity text as it appears in the source"
# Entity.field_descriptions[:confidence] # => "Extraction confidence from 0.0 to 1.0"