Skip to content

Commit 25b45ab

Browse files
committed
docs(vcr): update VCR testing documentation
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
1 parent 8aee01f commit 25b45ab

File tree

1 file changed

+270
-6
lines changed

1 file changed

+270
-6
lines changed

docs/content/modules/ROOT/pages/vcr-testing.adoc

Lines changed: 270 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -333,14 +333,278 @@ If Redis container fails to start:
333333
2. Check port availability
334334
3. Verify the Redis image exists
335335

336-
== Future Enhancements
336+
== Framework Integration
337337

338-
The following features are planned for future releases:
338+
The VCR system provides drop-in wrappers for popular AI frameworks. These wrappers work standalone without requiring any other RedisVL components.
339339

340-
* **LLM Interceptor** - Automatic interception of LangChain4J LLM calls
341-
* **Embedding Interceptor** - Integration with EmbeddingsCache
342-
* **Cassette Management CLI** - Tools for managing recorded cassettes
343-
* **Selective Recording** - Fine-grained control over what gets recorded
340+
=== LangChain4J
341+
342+
==== Embedding Model
343+
344+
Use `VCREmbeddingModel` to wrap any LangChain4J `EmbeddingModel`:
345+
346+
[source,java]
347+
----
348+
import com.redis.vl.test.vcr.VCREmbeddingModel;
349+
import com.redis.vl.test.vcr.VCRMode;
350+
import dev.langchain4j.model.embedding.EmbeddingModel;
351+
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
352+
import dev.langchain4j.data.embedding.Embedding;
353+
import dev.langchain4j.model.output.Response;
354+
355+
// Create your LangChain4J embedding model
356+
EmbeddingModel openAiModel = OpenAiEmbeddingModel.builder()
357+
.apiKey(System.getenv("OPENAI_API_KEY"))
358+
.modelName("text-embedding-3-small")
359+
.build();
360+
361+
// Wrap with VCR for recording/playback
362+
VCREmbeddingModel vcrModel = new VCREmbeddingModel(openAiModel);
363+
vcrModel.setMode(VCRMode.PLAYBACK_OR_RECORD);
364+
vcrModel.setTestId("MyTest.testEmbedding");
365+
366+
// Use exactly like the original model - VCR handles caching transparently
367+
Response<Embedding> response = vcrModel.embed("What is Redis?");
368+
float[] vector = response.content().vector();
369+
370+
// Batch embeddings also supported
371+
List<TextSegment> segments = List.of(
372+
TextSegment.from("Document chunk 1"),
373+
TextSegment.from("Document chunk 2")
374+
);
375+
Response<List<Embedding>> batchResponse = vcrModel.embedAll(segments);
376+
----
377+
378+
The `VCREmbeddingModel` implements the full `dev.langchain4j.model.embedding.EmbeddingModel` interface, so it can be used anywhere a LangChain4J embedding model is expected.
379+
380+
==== Supported Methods
381+
382+
* `embed(String text)` - Single text embedding
383+
* `embed(TextSegment segment)` - TextSegment embedding
384+
* `embedAll(List<TextSegment> segments)` - Batch embedding
385+
* `dimension()` - Returns embedding dimensions
386+
387+
==== Chat Model
388+
389+
Use `VCRChatModel` to wrap any LangChain4J `ChatLanguageModel`:
390+
391+
[source,java]
392+
----
393+
import com.redis.vl.test.vcr.VCRChatModel;
394+
import com.redis.vl.test.vcr.VCRMode;
395+
import dev.langchain4j.model.chat.ChatLanguageModel;
396+
import dev.langchain4j.model.openai.OpenAiChatModel;
397+
import dev.langchain4j.data.message.AiMessage;
398+
import dev.langchain4j.data.message.UserMessage;
399+
import dev.langchain4j.model.output.Response;
400+
401+
// Create your LangChain4J chat model
402+
ChatLanguageModel openAiModel = OpenAiChatModel.builder()
403+
.apiKey(System.getenv("OPENAI_API_KEY"))
404+
.modelName("gpt-4o-mini")
405+
.build();
406+
407+
// Wrap with VCR for recording/playback
408+
VCRChatModel vcrModel = new VCRChatModel(openAiModel);
409+
vcrModel.setMode(VCRMode.PLAYBACK_OR_RECORD);
410+
vcrModel.setTestId("MyTest.testChat");
411+
412+
// Use exactly like the original model - VCR handles caching transparently
413+
Response<AiMessage> response = vcrModel.generate(UserMessage.from("What is Redis?"));
414+
String answer = response.content().text();
415+
416+
// Simple string convenience method
417+
String simpleAnswer = vcrModel.generate("Tell me about Redis Vector Library");
418+
419+
// Multiple messages
420+
Response<AiMessage> chatResponse = vcrModel.generate(
421+
UserMessage.from("What is Redis?"),
422+
UserMessage.from("How does it handle vectors?")
423+
);
424+
----
425+
426+
The `VCRChatModel` implements the full `dev.langchain4j.model.chat.ChatLanguageModel` interface, so it can be used anywhere a LangChain4J chat model is expected.
427+
428+
==== Supported Chat Methods
429+
430+
* `generate(ChatMessage... messages)` - Generate from varargs messages
431+
* `generate(List<ChatMessage> messages)` - Generate from list of messages
432+
* `generate(String text)` - Simple string convenience method
433+
434+
=== Spring AI
435+
436+
==== Embedding Model
437+
438+
Use `VCRSpringAIEmbeddingModel` to wrap any Spring AI `EmbeddingModel`:
439+
440+
[source,java]
441+
----
442+
import com.redis.vl.test.vcr.VCRSpringAIEmbeddingModel;
443+
import com.redis.vl.test.vcr.VCRMode;
444+
import org.springframework.ai.embedding.EmbeddingModel;
445+
import org.springframework.ai.embedding.EmbeddingResponse;
446+
import org.springframework.ai.openai.OpenAiEmbeddingModel;
447+
import org.springframework.ai.document.Document;
448+
449+
// Create your Spring AI embedding model
450+
EmbeddingModel openAiModel = new OpenAiEmbeddingModel(openAiApi);
451+
452+
// Wrap with VCR for recording/playback
453+
VCRSpringAIEmbeddingModel vcrModel = new VCRSpringAIEmbeddingModel(openAiModel);
454+
vcrModel.setMode(VCRMode.PLAYBACK_OR_RECORD);
455+
vcrModel.setTestId("MyTest.testSpringAIEmbedding");
456+
457+
// Use exactly like the original model
458+
float[] vector = vcrModel.embed("What is Redis?");
459+
460+
// Batch embeddings
461+
List<float[]> vectors = vcrModel.embed(List.of("text 1", "text 2"));
462+
463+
// Document embedding
464+
Document doc = new Document("Document content here");
465+
float[] docVector = vcrModel.embed(doc);
466+
467+
// Full EmbeddingResponse API
468+
EmbeddingResponse response = vcrModel.embedForResponse(List.of("query text"));
469+
----
470+
471+
The `VCRSpringAIEmbeddingModel` implements the full `org.springframework.ai.embedding.EmbeddingModel` interface for seamless integration with Spring AI applications.
472+
473+
==== Supported Methods
474+
475+
* `embed(String text)` - Single text embedding returning `float[]`
476+
* `embed(Document document)` - Document embedding
477+
* `embed(List<String> texts)` - Batch embedding returning `List<float[]>`
478+
* `embedForResponse(List<String> texts)` - Full `EmbeddingResponse`
479+
* `call(EmbeddingRequest request)` - Standard Spring AI call pattern
480+
* `dimensions()` - Returns embedding dimensions
481+
482+
==== Chat Model
483+
484+
Use `VCRSpringAIChatModel` to wrap any Spring AI `ChatModel`:
485+
486+
[source,java]
487+
----
488+
import com.redis.vl.test.vcr.VCRSpringAIChatModel;
489+
import com.redis.vl.test.vcr.VCRMode;
490+
import org.springframework.ai.chat.model.ChatModel;
491+
import org.springframework.ai.chat.model.ChatResponse;
492+
import org.springframework.ai.chat.prompt.Prompt;
493+
import org.springframework.ai.chat.messages.UserMessage;
494+
import org.springframework.ai.openai.OpenAiChatModel;
495+
496+
// Create your Spring AI chat model
497+
ChatModel openAiModel = new OpenAiChatModel(openAiApi);
498+
499+
// Wrap with VCR for recording/playback
500+
VCRSpringAIChatModel vcrModel = new VCRSpringAIChatModel(openAiModel);
501+
vcrModel.setMode(VCRMode.PLAYBACK_OR_RECORD);
502+
vcrModel.setTestId("MyTest.testChat");
503+
504+
// Use exactly like the original model - VCR handles caching transparently
505+
String response = vcrModel.call("What is Redis?");
506+
507+
// With Prompt object
508+
Prompt prompt = new Prompt(List.of(new UserMessage("Explain vector search")));
509+
ChatResponse chatResponse = vcrModel.call(prompt);
510+
String answer = chatResponse.getResult().getOutput().getText();
511+
512+
// Multiple messages
513+
String multiResponse = vcrModel.call(
514+
new UserMessage("What is Redis?"),
515+
new UserMessage("How does it handle vectors?")
516+
);
517+
----
518+
519+
The `VCRSpringAIChatModel` implements the full `org.springframework.ai.chat.model.ChatModel` interface for seamless integration with Spring AI applications.
520+
521+
==== Supported Chat Methods
522+
523+
* `call(String message)` - Simple string convenience method
524+
* `call(Message... messages)` - Generate from varargs messages
525+
* `call(Prompt prompt)` - Full Prompt/ChatResponse API
526+
527+
=== Common VCR Operations
528+
529+
Both wrappers share common VCR functionality:
530+
531+
[source,java]
532+
----
533+
// Set VCR mode
534+
vcrModel.setMode(VCRMode.PLAYBACK_OR_RECORD);
535+
536+
// Set test identifier (for cassette key generation)
537+
vcrModel.setTestId("MyTestClass.testMethod");
538+
539+
// Reset call counter between tests
540+
vcrModel.resetCallCounter();
541+
542+
// Get statistics
543+
int hits = vcrModel.getCacheHits();
544+
int misses = vcrModel.getCacheMisses();
545+
int recorded = vcrModel.getRecordedCount();
546+
547+
// Reset statistics
548+
vcrModel.resetStatistics();
549+
550+
// Access underlying delegate
551+
EmbeddingModel delegate = vcrModel.getDelegate();
552+
----
553+
554+
=== Using with JUnit 5
555+
556+
Combine VCR wrappers with the `@VCRTest` annotation for a complete testing solution:
557+
558+
[source,java]
559+
----
560+
import com.redis.vl.test.vcr.VCRTest;
561+
import com.redis.vl.test.vcr.VCRMode;
562+
import com.redis.vl.test.vcr.VCREmbeddingModel;
563+
import org.junit.jupiter.api.BeforeEach;
564+
import org.junit.jupiter.api.Test;
565+
566+
@VCRTest(mode = VCRMode.PLAYBACK_OR_RECORD)
567+
public class EmbeddingServiceTest {
568+
569+
private VCREmbeddingModel vcrModel;
570+
571+
@BeforeEach
572+
void setUp() {
573+
EmbeddingModel realModel = OpenAiEmbeddingModel.builder()
574+
.apiKey(System.getenv("OPENAI_API_KEY"))
575+
.build();
576+
vcrModel = new VCREmbeddingModel(realModel);
577+
vcrModel.setMode(VCRMode.PLAYBACK_OR_RECORD);
578+
}
579+
580+
@Test
581+
void testSemanticSearch() {
582+
vcrModel.setTestId("EmbeddingServiceTest.testSemanticSearch");
583+
584+
// First run: calls OpenAI API and records response
585+
// Subsequent runs: replays from Redis cassette
586+
Response<Embedding> response = vcrModel.embed("search query");
587+
588+
assertNotNull(response);
589+
assertEquals(1536, response.content().vector().length);
590+
}
591+
592+
@Test
593+
void testBatchEmbedding() {
594+
vcrModel.setTestId("EmbeddingServiceTest.testBatchEmbedding");
595+
596+
List<TextSegment> docs = List.of(
597+
TextSegment.from("Document 1"),
598+
TextSegment.from("Document 2"),
599+
TextSegment.from("Document 3")
600+
);
601+
602+
Response<List<Embedding>> response = vcrModel.embedAll(docs);
603+
604+
assertEquals(3, response.content().size());
605+
}
606+
}
607+
----
344608

345609
== See Also
346610

0 commit comments

Comments
 (0)