logger.LogWarning("Skipping local retrieval for data source '{DataSourceName}' ({DataSourceId}) because its index has to be built anew.",dataSource.Name,dataSource.Id);
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.NOT_SEARCHED,"index-rebuilding",string.Format(TB("The data source '{0}' was left out of the answer: it is being indexed again and cannot be searched until that is finished."),dataSource.Name));
returntrue;
}
/// <summary>
/// Searches the vectors of a data source for the chunks closest in meaning to the query.
/// </summary>
/// <param name="dataSource">The data source to search.</param>
/// <param name="query">What to search for.</param>
/// <param name="maxMatches">How many chunks to return at most.</param>
/// <param name="filter">The chunks the search may return, or null to search all of them.</param>
/// <param name="run">The retrieval, which records what kept the search from covering the data source.</param>
"Skipping vector retrieval for data source '{DataSourceName}' ({DataSourceId}) because vector store '{VectorStoreName}' is unavailable.",
dataSource.Name,
dataSource.Id,
vectorStore.Name);
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.PARTLY_SEARCHED,"no-vector-store",string.Format(TB("The data source '{0}' was left out of the answer: its local index is not available."),dataSource.Name));
logger.LogWarning("Skipping vector retrieval for data source '{DataSourceName}' ({DataSourceId}) because the selected embedding provider is not available.",dataSource.Name,dataSource.Id);
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.PARTLY_SEARCHED,"no-embedding-provider",string.Format(TB("The data source '{0}' was left out of the answer: its embedding provider is not available. Please check it in the settings."),dataSource.Name));
logger.LogWarning("Skipping vector retrieval for data source '{DataSourceName}' ({DataSourceId}) because query embedding returned no vector.",dataSource.Name,dataSource.Id);
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.PARTLY_SEARCHED,"no-query-vector",string.Format(TB("The data source '{0}' was left out of the answer: its embedding provider '{1}' did not return a vector to search with."),dataSource.Name,embeddingProvider.Name));
// The embedding provider named the cause and what to do about it. That sentence is
// worth far more to the user than the fact that a search came back empty:
//
logger.LogWarning(
exception,
"Vector retrieval failed for data source '{DataSourceName}' ({DataSourceId}) because the embedding provider failed. FailureReason={FailureReason}, StatusCode={StatusCode}.",
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.PARTLY_SEARCHED,$"provider-{exception.FailureReason}",string.Format(TB("The data source '{0}' was left out of the answer. {1}"),dataSource.Name,exception.UserMessage));
return[];
}
catch(VectorStoreUnreadableExceptionexception)
{
//
// Its own gap key, because this is not a search which went wrong but an index which has
// to be built anew. Saying that once per session is what turns a silently shortened
// answer into one the user can do something about.
//
logger.LogWarning(exception,"Vector retrieval failed for data source '{DataSourceName}' ({DataSourceId}) because its vector store cannot be read.",dataSource.Name,dataSource.Id);
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.PARTLY_SEARCHED,"vector-store-unreadable",string.Format(TB("The data source '{0}' was left out of the answer: its index cannot be read anymore. You can repair it in your data source settings."),dataSource.Name));
return[];
}
catch(Exceptionexception)
{
logger.LogWarning(exception,"Vector retrieval failed for data source '{DataSourceName}' ({DataSourceId}).",dataSource.Name,dataSource.Id);
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.PARTLY_SEARCHED,"vector-search-failed",string.Format(TB("The data source '{0}' was left out of the answer because searching it failed."),dataSource.Name));
return[];
}
}
/// <summary>
/// Searches the keyword index of a data source with BM25.
/// </summary>
/// <param name="dataSource">The data source to search.</param>
/// <param name="maxMatches">How many chunks to return at most.</param>
/// <param name="search">The search to run against the keyword index, asking for at most maxMatches chunks of this data source.</param>
/// <param name="run">The retrieval, which records what kept the search from covering the data source.</param>
"Skipping vector retrieval for data source '{DataSourceName}' ({DataSourceId}) because the query has {CharacterCount} characters and exceeds the safe tokenizer request length of {MaxCharacterCount}. ProviderTokenLimit={ProviderTokenLimit}.",
dataSource.Name,
dataSource.Id,
query.Length,
RustService.MAX_TOKEN_COUNT_REQUEST_TEXT_LENGTH,
providerTokenLimit);
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.QUERY_NOT_SEARCHABLE,"query-too-long",string.Format(TB("The data source '{0}' was left out of the answer because your message is too long to search with."),dataSource.Name));
"Skipping vector retrieval for data source '{DataSourceName}' ({DataSourceId}) because the token count for embedding provider '{EmbeddingProviderName}' could not be determined. Reason='{Reason}'.",
dataSource.Name,
dataSource.Id,
embeddingProvider.Name,
tokenCountResponse?.Message??"No response was returned by the tokenizer service.");
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.PARTLY_SEARCHED,"no-token-count",string.Format(TB("The data source '{0}' was left out of the answer: the tokenizer of its embedding provider '{1}' is not available."),dataSource.Name,embeddingProvider.Name));
"Skipping vector retrieval for data source '{DataSourceName}' ({DataSourceId}) because the query has {QueryTokenCount} tokens, exceeding embedding provider '{EmbeddingProviderName}' limit of {ProviderTokenLimit} tokens.",
dataSource.Name,
dataSource.Id,
queryTokenCount,
embeddingProvider.Name,
providerTokenLimit);
awaitthis.ReportRetrievalGapAsync(dataSource,run,RetrievalGap.QUERY_NOT_SEARCHABLE,"query-over-token-limit",string.Format(TB("The data source '{0}' was left out of the answer because your message is longer than its embedding provider '{1}' accepts."),dataSource.Name,embeddingProvider.Name));
"Searching the vectors of data source '{DataSourceName}' ({DataSourceId}) without hiding incomplete generations, because local RAG index '{DatabaseName}' is unavailable.",
dataSource.Name,
dataSource.Id,
indexStore.Name);
return[];
}
/// <summary>
/// Completes the hits of the vector search with where their chunks are, as the index store keeps it.
/// </summary>
/// <remarks>
/// The vector store keeps only the pages of a chunk; its sheet and rows, its lines and the
/// attachment of a mail it is from are kept in the index store alone. One query for all hits.
/// Without the index store, the hits keep their pages, which is what they said before: a hit
/// which cannot say where it is still belongs into the answer.
/// </remarks>
/// <param name="dataSource">The data source searched.</param>
/// <param name="indexStore">The index store.</param>
logger.LogWarning(exception,"Could not read where the chunks found by the vector search of data source '{DataSourceName}' ({DataSourceId}) are. They name their pages only.",dataSource.Name,dataSource.Id);
"Local RAG {SearchName} search returned {ReturnedHits} chunks for data source '{DataSourceName}' ({DataSourceId}), which exceeds the requested maximum {MaxMatches}. Truncating to it.",