@@ -22,6 +22,7 @@ import {
2222import {
2323 searchQueryFromLanguage ,
2424 SearchQueryTranslator ,
25+ searchQueryWithScopeFromLanguage ,
2526} from "./searchQueryTranslator.js" ;
2627import * as querySchema from "./searchQuerySchema.js" ;
2728import * as querySchema2 from "./searchQuerySchema_v2.js" ;
@@ -182,16 +183,6 @@ export async function searchConversationWithLanguage(
182183 //
183184 return undefined ;
184185 }
185-
186- function createTextQueryOptions (
187- options : LanguageSearchOptions ,
188- ) : SearchOptions {
189- const ragOptions : SearchOptions = {
190- ...options ,
191- ...options . fallbackRagOptions ,
192- } ;
193- return ragOptions ;
194- }
195186}
196187
197188export type LanguageQueryExpr = {
@@ -1013,6 +1004,170 @@ export function compileSearchQuery2(
10131004 return searchQueryExprs ;
10141005}
10151006
1007+ export async function searchQueryExprFromLanguage2 (
1008+ conversation : IConversation ,
1009+ translator : SearchQueryTranslator ,
1010+ queryText : string ,
1011+ options ?: LanguageSearchOptions ,
1012+ languageSearchFilter ?: LanguageSearchFilter ,
1013+ debugContext ?: LanguageSearchDebugContext ,
1014+ ) : Promise < Result < LanguageQueryExpr > > {
1015+ const queryResult = await searchQueryWithScopeFromLanguage (
1016+ conversation ,
1017+ translator ,
1018+ queryText ,
1019+ options ?. modelInstructions ,
1020+ ) ;
1021+ if ( queryResult . success ) {
1022+ const query = queryResult . data ;
1023+ if ( debugContext ) {
1024+ debugContext . searchQuery = query ;
1025+ }
1026+ options ??= createLanguageSearchOptions ( ) ;
1027+ const queryExpressions = compileSearchQuery2 (
1028+ conversation ,
1029+ query ,
1030+ options . compileOptions ,
1031+ languageSearchFilter ,
1032+ ) ;
1033+ return success ( {
1034+ queryText,
1035+ query,
1036+ queryExpressions,
1037+ } ) ;
1038+ }
1039+ return queryResult ;
1040+ }
1041+
1042+ /**
1043+ * Search a conversation using natural language. Returns {@link ConversationSearchResult} containing
1044+ * relevant knowledge and messages.
1045+ *
1046+ * @param conversation - The conversation object to search within.
1047+ * @param searchText - The natural language search phrase.
1048+ * @param queryTranslator - Translates natural language to a {@link querySchema.SearchQuery} structured query.
1049+ * @param options - Optional search options.
1050+ * @param langSearchFilter - Optional filter options for the search.
1051+ * @param debugContext - Optional context for debugging the search process.
1052+ * @returns {ConversationSearchResult } Conversation search results.
1053+ */
1054+ export async function searchConversationWithLanguage2 (
1055+ conversation : IConversation ,
1056+ searchText : string ,
1057+ queryTranslator : SearchQueryTranslator ,
1058+ options ?: LanguageSearchOptions ,
1059+ langSearchFilter ?: LanguageSearchFilter ,
1060+ debugContext ?: LanguageSearchDebugContext ,
1061+ ) : Promise < Result < ConversationSearchResult [ ] > > {
1062+ options ??= createLanguageSearchOptions ( ) ;
1063+ const langQueryResult = await searchQueryExprFromLanguage2 (
1064+ conversation ,
1065+ queryTranslator ,
1066+ searchText ,
1067+ options ,
1068+ langSearchFilter ,
1069+ debugContext ,
1070+ ) ;
1071+ if ( ! langQueryResult . success ) {
1072+ return langQueryResult ;
1073+ }
1074+ const searchQueryExprs = langQueryResult . data . queryExpressions ;
1075+ if ( debugContext ) {
1076+ debugContext . searchQueryExpr = searchQueryExprs ;
1077+ debugContext . usedSimilarityFallback = new Array < boolean > (
1078+ searchQueryExprs . length ,
1079+ ) ;
1080+ debugContext . usedSimilarityFallback . fill ( false ) ;
1081+ }
1082+ let fallbackQueryExpr = compileFallbackQuery (
1083+ langQueryResult . data . query ,
1084+ options . compileOptions ,
1085+ langSearchFilter ,
1086+ ) ;
1087+
1088+ const searchResults : ConversationSearchResult [ ] = [ ] ;
1089+ for ( let i = 0 ; i < searchQueryExprs . length ; ++ i ) {
1090+ const searchQuery = searchQueryExprs [ i ] ;
1091+ const fallbackQuery = fallbackQueryExpr
1092+ ? fallbackQueryExpr [ i ]
1093+ : undefined ;
1094+ let queryResult = await runSearchQuery (
1095+ conversation ,
1096+ searchQuery ,
1097+ options ,
1098+ ) ;
1099+ if ( ! hasConversationResults ( queryResult ) && fallbackQuery ) {
1100+ // Rerun the query but with verb matching turned off for scopes
1101+ queryResult = await runSearchQuery (
1102+ conversation ,
1103+ fallbackQuery ,
1104+ options ,
1105+ ) ;
1106+ }
1107+ //
1108+ // If no matches and fallback enabled... run the raw query
1109+ //
1110+ if (
1111+ ! hasConversationResults ( queryResult ) &&
1112+ searchQuery . rawQuery &&
1113+ options . fallbackRagOptions
1114+ ) {
1115+ const textSearchOptions = createTextQueryOptions ( options ) ;
1116+ const ragMatches = await runSearchQueryTextSimilarity (
1117+ conversation ,
1118+ fallbackQuery ?? searchQuery ,
1119+ textSearchOptions ,
1120+ ) ;
1121+ if ( ragMatches ) {
1122+ searchResults . push ( ...ragMatches ) ;
1123+ if ( debugContext ?. usedSimilarityFallback ) {
1124+ debugContext . usedSimilarityFallback ! [ i ] = true ;
1125+ }
1126+ }
1127+ } else {
1128+ searchResults . push ( ...queryResult ) ;
1129+ }
1130+ }
1131+ return success ( searchResults ) ;
1132+
1133+ //
1134+ // Scoping queries can be precise. However, there may be random variations in how LLMs
1135+ // translate some user utterances into queries.. .particularly verbs. They verbs
1136+ // may not match action verbs actually in the index.. related terms may not meet the similarity
1137+ // cutoff.
1138+ // If configured (compileOptions.exactScope == false), we can do a fallback query that does
1139+ // not enforce verb matching. This improves recall while still providing a reasonable level of scoping because it
1140+ //
1141+ function compileFallbackQuery (
1142+ query : querySchema . SearchQuery ,
1143+ compileOptions : LanguageQueryCompileOptions ,
1144+ langSearchFilter ?: LanguageSearchFilter ,
1145+ ) : SearchQueryExpr [ ] | undefined {
1146+ const verbScope = compileOptions . verbScope ;
1147+ //
1148+ // If no exact scope... and verbScope is not provided or true,
1149+ // then we can build a fallback query that is more forgiving
1150+ if (
1151+ ! compileOptions . exactScope &&
1152+ ( verbScope == undefined || verbScope )
1153+ ) {
1154+ return compileSearchQuery2 (
1155+ conversation ,
1156+ query ,
1157+ {
1158+ ...compileOptions ,
1159+ verbScope : false ,
1160+ } ,
1161+ langSearchFilter ,
1162+ ) ;
1163+ }
1164+ //
1165+ // No fallback query currently possible
1166+ //
1167+ return undefined ;
1168+ }
1169+ }
1170+
10161171const Wildcard = "*" ;
10171172
10181173function isEntityTermArray (
@@ -1042,3 +1197,11 @@ function optimizeOrMax(termGroup: SearchTermGroup) {
10421197 }
10431198 return termGroup ;
10441199}
1200+
1201+ function createTextQueryOptions ( options : LanguageSearchOptions ) : SearchOptions {
1202+ const ragOptions : SearchOptions = {
1203+ ...options ,
1204+ ...options . fallbackRagOptions ,
1205+ } ;
1206+ return ragOptions ;
1207+ }
0 commit comments