@@ -78,7 +78,9 @@ def search_for_topics(query, page = 1, hyde: true)
7878          return  Post . none 
7979        end 
8080
81-         search_embedding  =  hyde  ? hyde_embedding ( search_term )  : embedding ( search_term ) 
81+         search_embedding  =  nil 
82+         search_embedding  =  hyde_embedding ( search_term )  if  hyde 
83+         search_embedding  =  embedding ( search_term )  if  search_embedding . blank? 
8284
8385        over_selection_limit  =  limit  * OVER_SELECTION_FACTOR 
8486
@@ -176,26 +178,51 @@ def quick_search(query)
176178      end 
177179
178180      def  hypothetical_post_from ( search_term ) 
179-         prompt  =  DiscourseAi ::Completions ::Prompt . new ( <<~TEXT . strip ) 
180-           You are a content creator for a forum. The forum description is as follows: 
181-           #{ SiteSetting . title }  
182-           #{ SiteSetting . site_description }  
181+         context  = 
182+           DiscourseAi ::Personas ::BotContext . new ( 
183+             user : @guardian . user , 
184+             skip_tool_details : true , 
185+             feature_name : "semantic_search_hyde" , 
186+             messages : [ {  type : :user ,  content : <<~TEXT . strip  } ] , 
187+               Using this description, write a forum post about the subject inside the <input></input> XML tags: 
188+ 
189+               <input>#{ search_term }  </input> 
190+             TEXT 
191+           ) 
183192
184-           Put the forum post between <ai></ai> tags.  
185-         TEXT  
193+         bot   =   build_bot ( @guardian . user ) 
194+         return   nil   if   bot . nil? 
186195
187-         prompt . push ( type : :user ,  content : <<~TEXT . strip ) 
188-           Using this description, write a forum post about the subject inside the <input></input> XML tags: 
196+         structured_output  =  nil 
197+         raw_response  =  +"" 
198+         hyde_schema_key  =  bot . persona . response_format &.first . to_h 
189199
190-           <input>#{ search_term }  </input> 
191-         TEXT 
200+         buffer_blk  = 
201+           Proc . new  do  |partial ,  _ ,  type |
202+             if  type  == :structured_output 
203+               structured_output  =  partial 
204+             elsif  type . blank? 
205+               # Assume response is a regular completion. 
206+               raw_response  << partial 
207+             end 
208+           end 
192209
193-         llm_response  = 
194-           DiscourseAi ::Completions ::Llm . proxy ( 
195-             SiteSetting . ai_embeddings_semantic_search_hyde_model , 
196-           ) . generate ( prompt ,  user : @guardian . user ,  feature_name : "semantic_search_hyde" ) 
210+         bot . reply ( context ,  &buffer_blk ) 
211+ 
212+         structured_output &.read_buffered_property ( hyde_schema_key [ "key" ] &.to_sym )  || raw_response 
213+       end 
214+ 
215+       # Priorities are: 
216+       #   1. Persona's default LLM 
217+       #   2. `ai_embeddings_semantic_search_hyde_model` setting. 
218+       def  find_ai_hyde_model ( persona_klass ) 
219+         model_id  = 
220+           persona_klass . default_llm_id  ||
221+             SiteSetting . ai_embeddings_semantic_search_hyde_model &.split ( ":" ) &.last 
197222
198-         Nokogiri ::HTML5 . fragment ( llm_response ) . at ( "ai" ) &.text . presence  || llm_response 
223+         return  if  model_id . blank? 
224+ 
225+         LlmModel . find_by ( id : model_id ) 
199226      end 
200227
201228      private 
@@ -209,6 +236,18 @@ def build_hyde_key(digest, hyde_model)
209236      def  build_embedding_key ( digest ,  hyde_model ,  embedding_model ) 
210237        "#{ build_hyde_key ( digest ,  hyde_model ) }  -#{ embedding_model }  " 
211238      end 
239+ 
240+       def  build_bot ( user ) 
241+         persona_id  =  SiteSetting . ai_embeddings_semantic_search_hyde_persona 
242+ 
243+         persona_klass  =  AiPersona . find_by ( id : persona_id ) &.class_instance 
244+         return  if  persona_klass . nil? 
245+ 
246+         llm_model  =  find_ai_hyde_model ( persona_klass ) 
247+         return  if  llm_model . nil? 
248+ 
249+         DiscourseAi ::Personas ::Bot . as ( user ,  persona : persona_klass . new ,  model : llm_model ) 
250+       end 
212251    end 
213252  end 
214253end 
0 commit comments