@@ -333,14 +333,278 @@ If Redis container fails to start:
3333332. Check port availability
3343343. 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