1+ package com .azure .recipe ;
2+
3+ import com .azure .recipe .model .Recipe ;
4+ import com .azure .recipe .service .CosmosDbService ;
5+ import com .azure .recipe .service .OpenAIService ;
6+ import com .fasterxml .jackson .core .JsonProcessingException ;
7+ import lombok .extern .slf4j .Slf4j ;
8+
9+ import java .io .IOException ;
10+ import java .util .*;
11+
12+ @ Slf4j
13+ public class Main {
14+ public static CosmosDbService cosmosDbService = null ;
15+ public static OpenAIService openAIEmbeddingService = null ;
16+
17+ public static void main (String [] args ) throws IOException {
18+
19+ Scanner scanner = new Scanner (System .in );
20+
21+ cosmosDbService = initCosmosDbService ();
22+
23+ while (true ) {
24+ System .out .println ("\n " );
25+ System .out .println ("1.\t Upload and vectorize the recipe(s) and store it in Cosmos DB" );
26+ System .out .println ("2.\t Ask AI Assistant (search for a recipe by name or description, or ask a question)" );
27+ System .out .println ("3.\t Exit this Application" );
28+ System .out .print ("Please select an option: " );
29+ int selectedOption = Integer .parseInt (scanner .nextLine ());
30+ switch (selectedOption ) {
31+ case 1 -> uploadRecipes ();
32+ //case 2 -> generateEmbeddings();
33+ case 2 -> performSearch (scanner );
34+ default -> {
35+ return ;
36+ }
37+ }
38+
39+ }
40+ }
41+
42+ private static CosmosDbService initCosmosDbService () {
43+ CosmosDbService cosmosDbService = new CosmosDbService (AppConfig .cosmosUri ,
44+ AppConfig .cosmosKey ,
45+ AppConfig .cosmosDatabase ,
46+ AppConfig .cosmosContainer
47+ );
48+ int recipeWithEmbedding = cosmosDbService .getRecipeCount (true );
49+ int recipeWithNoEmbedding = cosmosDbService .getRecipeCount (false );
50+
51+ System .out .println ("\n " );
52+ System .out .printf ("We have %d vectorized recipe(s) and %d non vectorized recipe(s)." ,
53+ recipeWithEmbedding , recipeWithNoEmbedding );
54+
55+ return cosmosDbService ;
56+ }
57+
58+ private static OpenAIService initOpenAIService () {
59+ return new OpenAIService (AppConfig .openAIEndpoint ,
60+ AppConfig .openAIKey ,
61+ AppConfig .openAIEmbeddingDeployment ,
62+ AppConfig .openAICompletionsDeployment ,
63+ AppConfig .openAIMaxToken );
64+ }
65+
66+ public static void uploadRecipes () throws JsonProcessingException {
67+ List <Recipe > recipes = Utility .parseDocuments (AppConfig .recipeLocalFolder );
68+ uploadAndVectorizeDocs (recipes );
69+ }
70+
71+ public static void performSearch (Scanner scanner ) throws JsonProcessingException {
72+
73+ if (openAIEmbeddingService == null ) {
74+ log .info ("Connecting to Open AI Service.." );
75+ openAIEmbeddingService = initOpenAIService ();
76+ }
77+
78+
79+ System .out .println ("Type the recipe name or your question, hit enter when ready." );
80+ String userQuery = scanner .nextLine ();
81+
82+ log .info ("Converting User Query to Vector.." );
83+ var embeddingVector = openAIEmbeddingService .getEmbeddings (userQuery );
84+
85+ log .info ("Performing Vector Search in Cosmos DB NoSQL API.." );
86+ Iterable <Recipe > filteredRecipes = cosmosDbService .vectorSearch (embeddingVector );
87+
88+ for (Recipe recipe : filteredRecipes ) {
89+ log .info (String .format ("Query result: Recipe with (/id, partition key) = (%s,%s)" ,recipe .getId (),recipe .getId ()));
90+ }
91+
92+ log .info ("Retrieving recipe(s) from Cosmos DB (RAG pattern).." );
93+ var retrivedDocs = filteredRecipes ;
94+
95+ StringBuilder retrivedReceipeNames = new StringBuilder ();
96+
97+ for (Recipe recipe : retrivedDocs ) {
98+ recipe .embedding = null ; //removing embedding to reduce tokens during chat completion
99+ retrivedReceipeNames .append (", " ).append (recipe .name ); //to dispay recipes submitted for Completion
100+ }
101+
102+ log .info ("Processing '{}' to generate Completion using OpenAI Service.." , retrivedReceipeNames );
103+
104+ String completion =
105+ openAIEmbeddingService
106+ .getChatCompletionAsync (userQuery , Utility .OBJECT_MAPPER .writeValueAsString (retrivedDocs ));
107+
108+ String chatCompletion = completion ;
109+
110+ log .info ("AI Assistant Response:" , chatCompletion );
111+ System .out .println (chatCompletion );
112+ }
113+
114+ private static void uploadAndVectorizeDocs (List <Recipe > recipes ) throws JsonProcessingException {
115+ Map <String , List <Double >> dictEmbeddings = new HashMap <>();
116+ int recipeWithEmbedding = 0 ;
117+ int recipeWithNoEmbedding = 0 ;
118+ int recipeCount = 0 ;
119+
120+ if (openAIEmbeddingService == null ) {
121+ openAIEmbeddingService = initOpenAIService ();
122+ }
123+
124+ log .info ("Getting recipe(s) to vectorize.." );
125+ for (Recipe recipe : recipes ) {
126+ recipe .setId (recipe .getName ().replace (" " , "" ));
127+ recipeCount ++;
128+ log .info ("Vectorizing Recipe# {}.." , recipeCount );
129+ var embeddingVector = openAIEmbeddingService .getEmbeddings (Utility .OBJECT_MAPPER .writeValueAsString (recipe ));
130+ recipe .embedding = embeddingVector ;
131+ dictEmbeddings .put (recipe .id , embeddingVector );
132+ }
133+
134+ log .info ("Updating {} recipe(s) in Cosmos DB for vectors.." , recipes .size ());
135+
136+ cosmosDbService .uploadRecipes (recipes );
137+
138+ log .info ("Getting Updated Recipe Stats" );
139+ recipeWithEmbedding = cosmosDbService .getRecipeCount (true );
140+ recipeWithNoEmbedding = cosmosDbService .getRecipeCount (false );
141+
142+ log .info ("Vectorized {} recipe(s)." , recipeCount );
143+ System .out .println ("\n " );
144+ System .out .printf ("We have %d vectorized recipe(s) and %d non vectorized recipe(s)." ,
145+ recipeWithEmbedding , recipeWithNoEmbedding );
146+ }
147+
148+
149+ }
0 commit comments