Files
sure/app/models/assistant/function/search_family_files.rb
Juan José Mata f96e58b9bc Enhance logging in search_family_files.rb for vector store debugging (#1033)
* Enhance logging in search_family_files.rb

Added logging for search parameters and results in SearchFamilyFiles.

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>

* Log level should be `debug` not `warn` here

* Unguarded `trace&.update` patterns

* API concernts from CodeRabbit

---------

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
2026-03-07 01:35:47 +01:00

170 lines
5.1 KiB
Ruby

class Assistant::Function::SearchFamilyFiles < Assistant::Function
class << self
def name
"search_family_files"
end
def description
<<~DESC
Search through documents that the family has uploaded to their financial document store.
Use this when the user asks questions about their uploaded financial documents such as
tax returns, bank statements, contracts, insurance policies, investment reports, or any
other files they've imported.
Returns relevant excerpts from matching documents along with the source filename and
a relevance score.
Supported file types include: PDF, DOCX, XLSX, PPTX, TXT, CSV, JSON, XML, HTML, MD,
and common source code formats.
Example:
```
search_family_files({
query: "What was the total income on my 2024 tax return?"
})
```
DESC
end
end
def strict_mode?
false
end
def params_schema
build_schema(
required: [ "query" ],
properties: {
query: {
type: "string",
description: "The search query to find relevant information in the family's uploaded documents"
},
max_results: {
type: "integer",
description: "Maximum number of results to return (default: 10, max: 20)"
}
}
)
end
def call(params = {})
query = params["query"]
max_results = (params["max_results"] || 10).to_i.clamp(1, 20)
Rails.logger.debug("[SearchFamilyFiles] query=#{query.inspect} max_results=#{max_results} family_id=#{family.id}")
unless family.vector_store_id.present?
Rails.logger.debug("[SearchFamilyFiles] family #{family.id} has no vector_store_id")
return {
success: false,
error: "no_documents",
message: "No documents have been uploaded to the family document store yet."
}
end
adapter = VectorStore.adapter
unless adapter
Rails.logger.debug("[SearchFamilyFiles] no VectorStore adapter configured")
return {
success: false,
error: "provider_not_configured",
message: "No vector store is configured. Set VECTOR_STORE_PROVIDER or configure OpenAI."
}
end
store_id = family.vector_store_id
Rails.logger.debug("[SearchFamilyFiles] searching store_id=#{store_id} via #{adapter.class.name}")
trace = create_langfuse_trace(
name: "search_family_files",
input: { query: query, max_results: max_results, store_id: store_id }
)
response = adapter.search(
store_id: store_id,
query: query,
max_results: max_results
)
unless response.success?
error_msg = response.error&.message
Rails.logger.debug("[SearchFamilyFiles] search failed: #{error_msg}")
begin
langfuse_client&.trace(id: trace.id, output: { error: error_msg }, level: "ERROR") if trace
rescue => e
Rails.logger.debug("[SearchFamilyFiles] Langfuse trace update failed: #{e.class}: #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}")
end
return {
success: false,
error: "search_failed",
message: "Failed to search documents: #{error_msg}"
}
end
results = response.data
Rails.logger.debug("[SearchFamilyFiles] #{results.size} chunk(s) returned")
results.each_with_index do |r, i|
Rails.logger.debug(
"[SearchFamilyFiles] chunk[#{i}] score=#{r[:score]} file=#{r[:filename].inspect} " \
"content_length=#{r[:content]&.length} preview=#{r[:content]&.truncate(10).inspect}"
)
end
mapped = results.map do |result|
{ content: result[:content], filename: result[:filename], score: result[:score] }
end
output = if mapped.empty?
{ success: true, results: [], message: "No matching documents found for the query." }
else
{ success: true, query: query, result_count: mapped.size, results: mapped }
end
begin
if trace
langfuse_client&.trace(id: trace.id, output: {
result_count: mapped.size,
chunks: mapped.map { |r| { filename: r[:filename], score: r[:score], content_length: r[:content]&.length } }
})
end
rescue => e
Rails.logger.debug("[SearchFamilyFiles] Langfuse trace update failed: #{e.class}: #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}")
end
output
rescue => e
Rails.logger.error("[SearchFamilyFiles] error: #{e.class.name} - #{e.message}")
{
success: false,
error: "search_failed",
message: "An error occurred while searching documents: #{e.message.truncate(200)}"
}
end
private
def langfuse_client
return unless ENV["LANGFUSE_PUBLIC_KEY"].present? && ENV["LANGFUSE_SECRET_KEY"].present?
@langfuse_client ||= Langfuse.new
end
def create_langfuse_trace(name:, input:)
return unless langfuse_client
langfuse_client.trace(
name: name,
input: input,
user_id: user.id&.to_s,
environment: Rails.env
)
rescue => e
Rails.logger.debug("[SearchFamilyFiles] Langfuse trace creation failed: #{e.class}: #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}")
nil
end
end