@@ -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,47 @@ 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 : search_term } ] ,
187+ )
183188
184- Put the forum post between <ai></ai> tags.
185- TEXT
189+ bot = build_bot ( @guardian . user )
190+ return nil if bot . nil?
186191
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:
192+ structured_output = nil
193+ raw_response = +""
194+ hyde_schema_key = bot . persona . response_format &.first . to_h
189195
190- <input>#{ search_term } </input>
191- TEXT
196+ buffer_blk =
197+ Proc . new do |partial , _ , type |
198+ if type == :structured_output
199+ structured_output = partial
200+ elsif type . blank?
201+ # Assume response is a regular completion.
202+ raw_response << partial
203+ end
204+ end
192205
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" )
206+ bot . reply ( context , &buffer_blk )
207+
208+ structured_output &.read_buffered_property ( hyde_schema_key [ "key" ] &.to_sym ) || raw_response
209+ end
210+
211+ # Priorities are:
212+ # 1. Persona's default LLM
213+ # 2. `ai_embeddings_semantic_search_hyde_model` setting.
214+ def find_ai_hyde_model ( persona_klass )
215+ model_id =
216+ persona_klass . default_llm_id ||
217+ SiteSetting . ai_embeddings_semantic_search_hyde_model &.split ( ":" ) &.last
197218
198- Nokogiri ::HTML5 . fragment ( llm_response ) . at ( "ai" ) &.text . presence || llm_response
219+ return if model_id . blank?
220+
221+ LlmModel . find_by ( id : model_id )
199222 end
200223
201224 private
@@ -209,6 +232,18 @@ def build_hyde_key(digest, hyde_model)
209232 def build_embedding_key ( digest , hyde_model , embedding_model )
210233 "#{ build_hyde_key ( digest , hyde_model ) } -#{ embedding_model } "
211234 end
235+
236+ def build_bot ( user )
237+ persona_id = SiteSetting . ai_embeddings_semantic_search_hyde_persona
238+
239+ persona_klass = AiPersona . find_by ( id : persona_id ) &.class_instance
240+ return if persona_klass . nil?
241+
242+ llm_model = find_ai_hyde_model ( persona_klass )
243+ return if llm_model . nil?
244+
245+ DiscourseAi ::Personas ::Bot . as ( user , persona : persona_klass . new , model : llm_model )
246+ end
212247 end
213248 end
214249end
0 commit comments