From a4324d595d01916f8849e074d1c3cebc173414ea Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Sun, 25 Jan 2026 02:41:00 +0100 Subject: [PATCH] Add optional file globbing to SourceSearch. --- profiler/src/llm/tools.json | 4 +++ profiler/src/profiler/TracyLlmTools.cpp | 33 +++++++++++++++++++++++-- profiler/src/profiler/TracyLlmTools.hpp | 2 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/profiler/src/llm/tools.json b/profiler/src/llm/tools.json index 42d59711..7cf0c16d 100644 --- a/profiler/src/llm/tools.json +++ b/profiler/src/llm/tools.json @@ -153,6 +153,10 @@ "case_insensitive": { "type": "boolean", "description": "Case insensitive search." + }, + "glob": { + "type": "string", + "description": "File path match." } }, "required": ["query"] diff --git a/profiler/src/profiler/TracyLlmTools.cpp b/profiler/src/profiler/TracyLlmTools.cpp index 9a360996..33035a40 100644 --- a/profiler/src/profiler/TracyLlmTools.cpp +++ b/profiler/src/profiler/TracyLlmTools.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "TracyConfig.hpp" #include "TracyLlmApi.hpp" @@ -179,7 +180,8 @@ TracyLlmTools::ToolReply TracyLlmTools::HandleToolCalls( const std::string& tool } else if( tool == "source_search" ) { - return { .reply = SourceSearch( Param( "query" ), ParamOptBool( "case_insensitive", false ) ) }; + std::string empty; + return { .reply = SourceSearch( Param( "query" ), ParamOptBool( "case_insensitive", false ), ParamOptString( "glob", empty ) ) }; } return { .reply = "Unknown tool call: " + tool }; } @@ -823,7 +825,33 @@ std::string TracyLlmTools::SourceFile( const std::string& file, uint32_t line, u return json.dump( 2, ' ', false, nlohmann::json::error_handler_t::replace ); } -std::string TracyLlmTools::SourceSearch( std::string query, bool caseInsensitive ) const +static bool GlobMatch( const std::string& pattern, const std::string& text ) +{ + std::string rx; + for( char c : pattern ) + { + if( c == '*' ) + { + rx += ".*"; + } + else if( c == '?' ) + { + rx += "."; + } + else if( c == '.' || c == '+' || c == '^' || c == '$' || c == '|' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' ) + { + rx += '\\'; + rx += c; + } + else + { + rx += c; + } + } + return std::regex_search( text, std::regex( rx ) ); +} + +std::string TracyLlmTools::SourceSearch( std::string query, bool caseInsensitive, const std::string& glob ) const { auto& cache = m_worker.GetSourceFileCache(); nlohmann::json json = {}; @@ -832,6 +860,7 @@ std::string TracyLlmTools::SourceSearch( std::string query, bool caseInsensitive for( auto& item : cache ) { if( IsFrameExternal( item.first, nullptr ) ) continue; + if( !glob.empty() && !GlobMatch( glob, item.first ) ) continue; char* tmp = nullptr; auto& mem = item.second; diff --git a/profiler/src/profiler/TracyLlmTools.hpp b/profiler/src/profiler/TracyLlmTools.hpp index d96e31d7..378c1ffd 100644 --- a/profiler/src/profiler/TracyLlmTools.hpp +++ b/profiler/src/profiler/TracyLlmTools.hpp @@ -62,7 +62,7 @@ private: std::string GetWebpage( const std::string& url ); std::string SearchManual( const std::string& query, TracyLlmApi& api, bool hasEmbeddingsModel ); std::string SourceFile( const std::string& file, uint32_t line, uint32_t context ) const; - std::string SourceSearch( std::string query, bool caseInsensitive ) const; + std::string SourceSearch( std::string query, bool caseInsensitive, const std::string& glob ) const; void ManualEmbeddingsWorker( TracyLlmApi& api );