fix(cookbook): address reviewer feedback on rag_gateway example

- Add @component decorator and @component.output_types to LiteLLMGenerator
  for Haystack 2.0 compatibility (was causing runtime crash)
- Remove unused InMemoryBM25Retriever import from rag_haystack.py
- Pin ragas>=0.1.0,<0.2.0 to avoid API incompatibility with 0.2.x+
- Wire eval_model parameter through to RAGAS via LangchainLLMWrapper
- Remove silent dummy API key fallback; raise explicit ValueError instead
- Rename litellm_config.yaml to litellm_config.example.yaml (was gitignored)
- Update README references accordingly
This commit is contained in:
Prajwal Raymond Moras 2026-02-23 17:38:10 +05:30
parent d0ae097c6f
commit 2c830f5d3c
5 changed files with 114 additions and 7 deletions

View file

@ -19,7 +19,7 @@ This cookbook showcases:
rag_gateway/
├── README.md # This file
├── requirements.txt # Python dependencies
├── litellm_config.yaml # Multi-provider configuration (optional)
├── litellm_config.example.yaml # Multi-provider configuration (optional)
├── rag_llamaindex.py # LlamaIndex RAG pipeline
├── rag_haystack.py # Haystack RAG pipeline
├── evaluate_with_ragas.py # RAGAS evaluation script
@ -166,7 +166,7 @@ llm = setup_litellm(model_name="azure/gpt-4-deployment-name")
### Using Configuration File (Optional)
The `litellm_config.yaml` file demonstrates advanced configuration:
The `litellm_config.example.yaml` file demonstrates advanced configuration:
```yaml
model_list:

View file

@ -172,11 +172,22 @@ def evaluate_rag_system(
"""
print(f"\n📊 Evaluating with RAGAS (using {eval_model} as judge)...")
# Require API key explicitly — do not silently fall back to a dummy value
if not os.getenv("OPENAI_API_KEY"):
raise ValueError(
"OPENAI_API_KEY is not set. "
"RAGAS requires an LLM to act as a judge. "
"Set it with: export OPENAI_API_KEY='your-key-here'"
)
# Convert to RAGAS dataset format
dataset = Dataset.from_dict(rag_data)
# Configure RAGAS to use LiteLLM
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY", "dummy-key")
# Wire eval_model into RAGAS via LangchainLLMWrapper so the user-specified
# judge model is actually used instead of the RAGAS default.
from langchain_openai import ChatOpenAI
from ragas.llms import LangchainLLMWrapper
eval_llm = LangchainLLMWrapper(ChatOpenAI(model=eval_model))
# Run evaluation
try:
@ -188,6 +199,7 @@ def evaluate_rag_system(
context_precision,
context_recall,
],
llm=eval_llm,
)
scores = {

View file

@ -0,0 +1,94 @@
# LiteLLM Multi-Provider Configuration
# This config demonstrates production-ready patterns for RAG systems
model_list:
# Primary: OpenAI GPT-4 (high quality, higher cost)
- model_name: gpt-4-turbo
litellm_params:
model: gpt-4-turbo-preview
api_key: os.environ/OPENAI_API_KEY
timeout: 30 # 30 second timeout
max_retries: 3 # Retry up to 3 times
# Fallback 1: OpenAI GPT-3.5 (faster, cheaper)
- model_name: gpt-3.5-turbo
litellm_params:
model: gpt-3.5-turbo
api_key: os.environ/OPENAI_API_KEY
timeout: 20
max_retries: 2
# Fallback 2: Anthropic Claude (alternative provider)
- model_name: claude-3-haiku
litellm_params:
model: claude-3-haiku-20240307
api_key: os.environ/ANTHROPIC_API_KEY
timeout: 30
max_retries: 2
# Local option: Ollama (free, private, no API key needed)
- model_name: ollama-llama3
litellm_params:
model: ollama/llama3
api_base: http://localhost:11434
timeout: 60 # Local models may be slower
# Cost-effective option: Groq (very fast inference)
- model_name: groq-llama3
litellm_params:
model: groq/llama3-70b-8192
api_key: os.environ/GROQ_API_KEY
timeout: 15 # Groq is very fast
max_retries: 2
# Router configuration for load balancing and fallbacks
router_settings:
# Routing strategy: 'simple-shuffle', 'least-busy', 'usage-based-routing'
routing_strategy: simple-shuffle
# Retry configuration
num_retries: 3
retry_after: 2 # Wait 2 seconds before retry
# Timeout for entire request (including retries)
timeout: 45
# Fallback models (in order of preference)
fallbacks:
- gpt-4-turbo
- gpt-3.5-turbo
- claude-3-haiku
- groq-llama3
- ollama-llama3
# Logging configuration
litellm_settings:
# Log level: DEBUG, INFO, WARNING, ERROR
set_verbose: true
# Drop parameters not supported by provider
drop_params: true
# Success callback for logging
success_callback: ["langfuse"] # Optional: integrate with Langfuse for observability
# Cost tracking
track_cost_per_request: true
# Cache responses (optional, requires Redis)
# cache: true
# cache_params:
# type: redis
# host: localhost
# port: 6379
# General settings
general_settings:
# Master key for proxy authentication (if using proxy server)
# master_key: os.environ/LITELLM_MASTER_KEY
# Database for storing request logs (optional)
# database_url: os.environ/DATABASE_URL
# Enable request/response logging
store_model_in_db: true

View file

@ -13,8 +13,7 @@ import json
from pathlib import Path
from typing import List, Dict, Any
from haystack import Pipeline, Document
from haystack.components.retrievers.in_memory import InMemoryBM25Retriever
from haystack import Pipeline, Document, component
from haystack.components.builders import PromptBuilder
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.embedders import SentenceTransformersDocumentEmbedder, SentenceTransformersTextEmbedder
@ -45,6 +44,7 @@ def load_documents(data_path: str = "sample_data/documents.json") -> List[Docume
return documents
@component
class LiteLLMGenerator:
"""
Custom Haystack component that uses LiteLLM for generation.
@ -73,6 +73,7 @@ class LiteLLMGenerator:
print(f"✅ Configured LiteLLM Generator with model: {model}")
@component.output_types(replies=List[str], meta=Dict[str, Any])
def run(self, prompt: str) -> Dict[str, Any]:
"""
Generate response using LiteLLM.

View file

@ -9,7 +9,7 @@ llama-index-vector-stores-chroma>=0.1.0
haystack-ai>=2.0.0
# Evaluation
ragas>=0.1.0
ragas>=0.1.0,<0.2.0
datasets>=2.14.0
# Vector Store & Embeddings