Skip to content

Conversation

@bsbodden
Copy link
Collaborator

Summary

Adds an experimental VCR (Video Cassette Recorder) test system for recording and replaying LLM and embedding API calls during testing. This enables:

  • Deterministic tests - Replay recorded responses for consistent results
  • Cost reduction - Avoid repeated API calls during test runs
  • Speed improvement - Local Redis playback is faster than API calls
  • Offline testing - Run tests without network access or API keys after initial recording

Features

Core VCR Framework

  • VCRMode enum with 6 modes: PLAYBACK, RECORD, RECORD_NEW, RECORD_FAILED, PLAYBACK_OR_RECORD, OFF
  • VCRTest annotation for declarative test configuration
  • VCRModel annotation for automatic model wrapping
  • VCRContext manages Redis container lifecycle with AOF/RDB persistence
  • VCRRegistry tracks recording status per test
  • VCRCassetteStore stores cassettes as Redis JSON documents
  • VCR_MODE environment variable override for CI/CD flexibility

Framework Wrappers

  • LangChain4J: VCREmbeddingModel, VCRChatModel
  • Spring AI: VCRSpringAIEmbeddingModel, VCRSpringAIChatModel

Demo Projects

  • demos/langchain4j-vcr/ - LangChain4J VCR demo with pre-recorded cassettes
  • demos/spring-ai-vcr/ - Spring AI VCR demo with pre-recorded cassettes

Both demos can be run without an API key using pre-recorded cassettes:

./gradlew :demos:langchain4j-vcr:test
./gradlew :demos:spring-ai-vcr:test

Documentation

  • Comprehensive vcr-testing.adoc guide
  • README section with quick start examples
  • Demo project READMEs with best practices

Test Plan

  • 129 VCR unit tests passing (./gradlew :core:test --tests "com.redis.vl.test.vcr.*")
  • 14 VCR demo tests passing (7 LangChain4J + 7 Spring AI)
  • Full build passing (./gradlew clean build check)
  • PLAYBACK mode verified - vcr-data files remain unchanged after test runs
  • Pre-recorded cassettes work without API key

Implements a VCR (Video Cassette Recorder) test system that enables recording
and replaying LLM and embedding API calls for deterministic, fast, and
cost-effective testing.

Core components:
- VCRMode: 6 modes (PLAYBACK, RECORD, RECORD_NEW, RECORD_FAILED,
  PLAYBACK_OR_RECORD, OFF) with smart mode selection
- VCRExtension: JUnit 5 extension with full lifecycle callbacks
- VCRContext: Redis container management with AOF/RDB persistence
- VCRRegistry: Test recording status tracking with smart mode logic
- @VCRTest, @VCRRecord, @VCRDisabled: Annotations for test configuration

Key features:
- Redis-based cassette storage with persistence to src/test/resources/vcr-data
- Testcontainers integration for isolated Redis instances
- Call counter management for deterministic key generation
- Statistics tracking (cache hits, misses, API calls)
- Configurable data directory and Redis image

Also includes:
- Comprehensive documentation in docs/experimental section
- README section with quick start example
- Design documents for VCR system and EmbeddingsCache enhancement

All 39 VCR tests passing. LLM/embedding interceptor implementation pending.
Implements VCRCassetteStore for storing and retrieving recorded API
responses in Redis:

- Store cassettes as Redis JSON documents with structured keys
- Support for single and batch embedding cassettes
- Handle both array and object JSON responses from Jedis
- Add VCRCassetteMissingException for strict playback mode
- Unit tests for cassette store operations

Cassette key format: vcr:{type}:{testId}:{callIndex}
Implements VCR wrappers for LangChain4J models:

- VCREmbeddingModel: wraps EmbeddingModel for recording/replaying embeddings
- VCRChatModel: wraps ChatLanguageModel for recording/replaying chat responses
- VCREmbeddingInterceptor: low-level interceptor for embedding operations

Features:
- Support for all VCR modes (PLAYBACK, RECORD, PLAYBACK_OR_RECORD, OFF)
- Redis-backed cassette storage integration
- In-memory cassette cache for testing
- Call counter tracking for unique cassette keys
- Statistics tracking (cache hits, misses, recorded count)

Unit tests verify recording and playback behavior.
Implements VCR wrappers for Spring AI models:

- VCRSpringAIEmbeddingModel: wraps EmbeddingModel for recording/replaying
- VCRSpringAIChatModel: wraps ChatModel for recording/replaying chat

Features:
- Full Spring AI interface implementation
- Support for embedForResponse() and call() methods
- Redis-backed cassette storage integration
- In-memory cassette cache for unit testing
- Statistics tracking for cache hits, misses, and recordings

Unit tests verify recording and playback behavior.
Implements declarative VCR support via JUnit 5 annotations:

- @VCRModel: marks model fields for automatic VCR wrapping
- VCRModelWrapper: wraps LangChain4J and Spring AI models automatically
- VCRContext: manages Redis container, cassette store, and test state
- VCRExtension: JUnit 5 extension for lifecycle management

Features:
- VCR_MODE environment variable support for runtime mode override
- Automatic model detection (LangChain4J/Spring AI embedding/chat)
- Cassette store integration for Redis persistence
- Test isolation with per-test call counters
- Statistics tracking across test session

Example usage:
  @VCRTest(mode = VCRMode.PLAYBACK_OR_RECORD)
  class MyTest {
    @VCRModel
    private EmbeddingModel model = createModel();
  }
Adds a complete demo project showing VCR usage with LangChain4J:

- LangChain4JVCRDemoTest: demonstrates embedding and chat model recording
- Pre-recorded cassettes in src/test/resources/vcr-data/
- README with usage instructions and best practices

Demo tests:
- Single and batch text embedding
- Chat completion with various prompts
- Combined RAG-style workflow (embed + generate)

Run without API key using pre-recorded cassettes:
  ./gradlew :demos:langchain4j-vcr:test
Adds a complete demo project showing VCR usage with Spring AI:

- SpringAIVCRDemoTest: demonstrates embedding and chat model recording
- Pre-recorded cassettes in src/test/resources/vcr-data/
- README with usage instructions and best practices

Demo tests:
- Single and batch text embedding
- Chat completion with various prompts
- Combined RAG-style workflow (embed + generate)

Run without API key using pre-recorded cassettes:
  ./gradlew :demos:spring-ai-vcr:test
Updates build configuration:

- settings.gradle.kts: include langchain4j-vcr and spring-ai-vcr demos
- build.gradle.kts: configure demo subprojects with dependencies
- core/build.gradle.kts: add testcontainers dependency for VCR

Demo projects inherit common configuration and add their specific
AI framework dependencies (LangChain4J or Spring AI with OpenAI).
Updates the AsciiDoc documentation for VCR testing:

- Add VCR_MODE environment variable documentation
- Document annotation-based approach with @VCRModel
- Add troubleshooting section
- Include demo project references
Enhances the README VCR section with:

- Quick start with JUnit 5 annotation example
- VCR modes table with API key requirements
- Environment variable override instructions (VCR_MODE)
- Improved code examples for LangChain4J and Spring AI
- How it works explanation
- Links to demo projects

Makes it easier for users to understand and adopt the VCR test system.
Fixes three bugs that caused vcr-data files to be modified even when
tests were configured with @VCRTest(mode = VCRMode.PLAYBACK):

1. Fixed @nested test class annotation lookup - The VCRExtension was not
   finding @VCRTest annotations on enclosing classes for nested tests,
   causing it to fall back to the default PLAYBACK_OR_RECORD mode.
   Added findVCRTestAnnotation() to walk up the class hierarchy.

2. Added temp directory copy for playback mode - In PLAYBACK mode, the
   vcr-data directory is now copied to a temp directory before mounting
   to Docker, preventing any modifications to the source files.

3. Fixed registry writes in playback mode - testSuccessful() and
   testFailed() now check the mode before writing to the registry,
   only updating it when recording.

Additional changes:
- Disabled Redis AOF persistence in playback mode (--appendonly no)
- Disabled Redis RDB saves in playback mode (--save "")
…ar override

- Updated Basic Usage section to show declarative @VCRModel annotation approach
- Added VCR_MODE environment variable override documentation
- Fixed outdated references (VCRCassetteStore now implemented, not 'coming soon')
- Changed -Dvcr.mode system property reference to VCR_MODE env var
- Added IMPORTANT note about model initialization timing requirement
@bsbodden bsbodden merged commit 984985c into main Dec 13, 2025
4 checks passed
@bsbodden bsbodden self-assigned this Dec 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants