mirror of
https://github.com/wolfpld/tracy.git
synced 2026-09-09 11:58:36 +00:00
Keep user manual chunks in a separate object.
This makes the user manual available outside of the LLM context. The code is also more readable, as splitting the manual into sections and splitting section content into chunks fit for embeddings is now separated. A bug has been fixed, where the above splits were mixed up for the last manual section, producing invalid data. The unembedded manual contents are no longer held in the memory. The only use case for this was to calculate the manual contents hash. The hash is now precalculated and cached.
This commit is contained in:
@@ -12,7 +12,7 @@ The user manual
|
||||
|
||||
**Bartosz Taudul** [\<wolf@nereid.pl\>](mailto:wolf@nereid.pl)
|
||||
|
||||
2025-11-11 <https://github.com/wolfpld/tracy>
|
||||
2025-11-29 <https://github.com/wolfpld/tracy>
|
||||
:::
|
||||
|
||||
# Quick overview {#quick-overview .unnumbered}
|
||||
|
||||
@@ -66,6 +66,7 @@ set(SERVER_FILES
|
||||
TracyFileselector.cpp
|
||||
TracyFilesystem.cpp
|
||||
TracyImGui.cpp
|
||||
TracyManualData.cpp
|
||||
TracyMarkdown.cpp
|
||||
TracyMicroArchitecture.cpp
|
||||
TracyMouse.cpp
|
||||
|
||||
@@ -24,7 +24,7 @@ extern double s_time;
|
||||
|
||||
constexpr size_t InputBufferSize = 1024;
|
||||
|
||||
TracyLlm::TracyLlm( Worker& worker )
|
||||
TracyLlm::TracyLlm( Worker& worker, const TracyManualData& manual )
|
||||
: m_exit( false )
|
||||
, m_input( nullptr )
|
||||
{
|
||||
@@ -47,7 +47,7 @@ TracyLlm::TracyLlm( Worker& worker )
|
||||
|
||||
m_api = std::make_unique<TracyLlmApi>();
|
||||
m_chatUi = std::make_unique<TracyLlmChat>();
|
||||
m_tools = std::make_unique<TracyLlmTools>( worker );
|
||||
m_tools = std::make_unique<TracyLlmTools>( worker, manual );
|
||||
|
||||
m_busy = true;
|
||||
QueueConnect();
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace tracy
|
||||
class TracyLlmApi;
|
||||
class TracyLlmChat;
|
||||
class TracyLlmTools;
|
||||
class TracyManualData;
|
||||
class Worker;
|
||||
|
||||
class TracyLlm
|
||||
@@ -40,7 +41,7 @@ class TracyLlm
|
||||
};
|
||||
|
||||
public:
|
||||
TracyLlm( Worker& worker );
|
||||
TracyLlm( Worker& worker, const TracyManualData& manual );
|
||||
~TracyLlm();
|
||||
|
||||
[[nodiscard]] bool IsBusy() const { std::lock_guard lock( m_lock ); return m_busy; }
|
||||
|
||||
@@ -9,15 +9,12 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "TracyConfig.hpp"
|
||||
#include "TracyEmbed.hpp"
|
||||
#include "TracyLlmApi.hpp"
|
||||
#include "TracyLlmTools.hpp"
|
||||
#include "TracyManualData.hpp"
|
||||
#include "TracyStorage.hpp"
|
||||
#include "TracyUtility.hpp"
|
||||
#include "TracyWorker.hpp"
|
||||
#include "tracy_xxhash.h"
|
||||
|
||||
#include "data/Manual.hpp"
|
||||
|
||||
constexpr const char* NoNetworkAccess = "Internet access is disabled by the user. You may inform the user that he can enable it in the settings, so that you can use the tools to gather information.";
|
||||
|
||||
@@ -81,122 +78,29 @@ static std::unique_ptr<pugi::xml_document> ParseHtml( const std::string& html )
|
||||
return doc;
|
||||
}
|
||||
|
||||
TracyLlmTools::TracyLlmTools( Worker& worker )
|
||||
: m_manual( Unembed( Manual ) )
|
||||
, m_worker( worker )
|
||||
TracyLlmTools::TracyLlmTools( Worker& worker, const TracyManualData& manual )
|
||||
: m_worker( worker )
|
||||
, m_manual( manual )
|
||||
{
|
||||
std::string_view manual( m_manual->data(), m_manual->size() );
|
||||
const auto sz = (int)m_manual->size();
|
||||
|
||||
std::vector<int> levels = { 0 };
|
||||
std::vector<std::string> chapterNames = { "Title Page" };
|
||||
|
||||
int manualChunkPos = 0;
|
||||
int pos = 0;
|
||||
while( pos < sz )
|
||||
int idx = 0;
|
||||
for( auto& chunk : m_manual.GetChunks() )
|
||||
{
|
||||
std::string::size_type next = pos;
|
||||
for(;;)
|
||||
std::string hdr;
|
||||
if( !chunk.section.empty() ) hdr += "Section " + chunk.section;
|
||||
if( !chunk.title.empty() )
|
||||
{
|
||||
next = manual.find( '\n', next );
|
||||
if( next == std::string_view::npos )
|
||||
{
|
||||
next = sz;
|
||||
break;
|
||||
}
|
||||
if( next+1 >= sz || manual[next+1] == '\n' ) break;
|
||||
next++;
|
||||
if( !chunk.section.empty() ) hdr += ": ";
|
||||
hdr += chunk.title;
|
||||
}
|
||||
if( next != pos )
|
||||
hdr += '\n';
|
||||
|
||||
for( auto& line : SplitLines( chunk.text.c_str(), chunk.text.size() ) )
|
||||
{
|
||||
std::string_view line( manual.data() + pos, next - pos );
|
||||
if( line != "---" && line != ":::" && line != "::: bclogo" )
|
||||
{
|
||||
if( line[0] == '#' )
|
||||
{
|
||||
if( manualChunkPos != pos )
|
||||
{
|
||||
auto start = manualChunkPos;
|
||||
auto end = pos;
|
||||
manualChunkPos = pos;
|
||||
|
||||
while( manual[start] != '\n' ) start++;
|
||||
while( manual[start] == '\n' ) start++;
|
||||
while( manual[end-1] == '\n' ) end--;
|
||||
|
||||
if( end > start )
|
||||
{
|
||||
std::string text, section, title, parents;
|
||||
text = std::string( manual.data() + start, end - start );
|
||||
if( levels[0] != 0 )
|
||||
{
|
||||
section = std::to_string( levels[0] );
|
||||
for( size_t i=1; i<levels.size(); i++ ) section += "." + std::to_string( levels[i] );
|
||||
}
|
||||
if( levels.size() == 1 )
|
||||
{
|
||||
title = chapterNames[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
title = chapterNames[levels.size()-1];
|
||||
parents = chapterNames[0];
|
||||
for( size_t i=1; i<levels.size() - 1; i++ ) parents += " > " + chapterNames[i];
|
||||
}
|
||||
m_manualChunks.emplace_back( ManualChunk {
|
||||
.text = std::move( text ),
|
||||
.section = std::move( section ),
|
||||
.title = std::move( title ),
|
||||
.parents = std::move( parents )
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
int level = 1;
|
||||
if( line.find( ".unnumbered}" ) == std::string_view::npos )
|
||||
{
|
||||
while( level < line.size() && line[level] == '#' ) level++;
|
||||
if( level != levels.size() )
|
||||
{
|
||||
levels.resize( level, 0 );
|
||||
chapterNames.resize( level );
|
||||
}
|
||||
levels[level - 1]++;
|
||||
chapterNames[level - 1] = line.substr( level + 1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string chunk;
|
||||
if( levels[0] != 0 )
|
||||
{
|
||||
chunk += "Section " + std::to_string( levels[0] );
|
||||
for( size_t i=1; i<levels.size(); i++ ) chunk += "." + std::to_string( levels[i] );
|
||||
chunk += "\n";
|
||||
}
|
||||
chunk += chapterNames[levels.size()-1] + "\n\n";
|
||||
chunk += std::string( line );
|
||||
m_chunkData.emplace_back( std::move( chunk ), m_manualChunks.size() );
|
||||
}
|
||||
}
|
||||
if( line.empty() ) continue;
|
||||
if( line == "---" || line == ":::" || line == "::: bclogo" ) continue;
|
||||
m_chunkData.emplace_back( hdr + line, idx );
|
||||
}
|
||||
pos = next + 1;
|
||||
while( pos < sz && manual[pos] == '\n' ) pos++;
|
||||
}
|
||||
if( manualChunkPos != pos )
|
||||
{
|
||||
std::string manualChunk;
|
||||
if( levels[0] != 0 )
|
||||
{
|
||||
manualChunk += "Section " + std::to_string( levels[0] );
|
||||
for( size_t i=1; i<levels.size(); i++ ) manualChunk += "." + std::to_string( levels[i] );
|
||||
manualChunk += "\n";
|
||||
}
|
||||
manualChunk += "Navigation: " + chapterNames[0];
|
||||
for( size_t i=1; i<levels.size(); i++ ) manualChunk += " > " + chapterNames[i];
|
||||
manualChunk += "\n\n";
|
||||
manualChunk += std::string( manual.data() + manualChunkPos, pos - manualChunkPos );
|
||||
m_manualChunks.emplace_back( ManualChunk{std::move( manualChunk ), "", "", ""} );
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,12 +192,11 @@ void TracyLlmTools::SelectManualEmbeddings( const std::string& model )
|
||||
assert( !m_manualEmbeddingState.inProgress );
|
||||
if( m_manualEmbeddingState.done && m_manualEmbeddingState.model == model ) return;
|
||||
|
||||
const uint64_t hash = XXH3_64bits( m_manual->data(), m_manual->size() );
|
||||
auto cache = GetCachePath( model.c_str() );
|
||||
|
||||
try
|
||||
{
|
||||
m_manualEmbeddings = std::make_unique<TracyLlmEmbeddings>( cache, hash );
|
||||
m_manualEmbeddings = std::make_unique<TracyLlmEmbeddings>( cache, m_manual.GetHash() );
|
||||
m_manualEmbeddingState = { .model = model, .done = true };
|
||||
}
|
||||
catch( std::exception& ) {}
|
||||
@@ -315,7 +218,6 @@ void TracyLlmTools::BuildManualEmbeddings( const std::string& model, TracyLlmApi
|
||||
|
||||
void TracyLlmTools::ManualEmbeddingsWorker( TracyLlmApi& api )
|
||||
{
|
||||
const uint64_t hash = XXH3_64bits( m_manual->data(), m_manual->size() );
|
||||
auto cache = GetCachePath( m_manualEmbeddingState.model.c_str() );
|
||||
|
||||
std::unique_lock lock( m_lock );
|
||||
@@ -395,7 +297,7 @@ void TracyLlmTools::ManualEmbeddingsWorker( TracyLlmApi& api )
|
||||
i += bsz;
|
||||
}
|
||||
|
||||
m_manualEmbeddings->Save( cache, hash );
|
||||
m_manualEmbeddings->Save( cache, m_manual.GetHash() );
|
||||
|
||||
lock.lock();
|
||||
m_manualEmbeddingState.inProgress = false;
|
||||
@@ -825,12 +727,13 @@ std::string TracyLlmTools::SearchManual( const std::string& query, TracyLlmApi&
|
||||
}
|
||||
if( chunks.size() > MaxOutputChunks ) chunks.resize( MaxOutputChunks );
|
||||
|
||||
auto& manualChunks = m_manual.GetChunks();
|
||||
const auto maxSize = CalcMaxSize();
|
||||
int totalSize = 0;
|
||||
int idx;
|
||||
for( idx = 0; idx < chunks.size(); idx++ )
|
||||
{
|
||||
totalSize += m_manualChunks[chunks[idx].first].text.size();
|
||||
totalSize += manualChunks[chunks[idx].first].text.size();
|
||||
if( totalSize >= maxSize ) break;
|
||||
}
|
||||
if( idx < chunks.size() ) chunks.resize( idx );
|
||||
@@ -838,7 +741,7 @@ std::string TracyLlmTools::SearchManual( const std::string& query, TracyLlmApi&
|
||||
nlohmann::json json;
|
||||
for( auto& chunk : chunks )
|
||||
{
|
||||
auto& m = m_manualChunks[chunk.first];
|
||||
auto& m = manualChunks[chunk.first];
|
||||
nlohmann::json r;
|
||||
r["distance"] = chunk.second;
|
||||
r["content"] = m.text;
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace tracy
|
||||
{
|
||||
|
||||
class TracyLlmApi;
|
||||
class TracyManualData;
|
||||
class Worker;
|
||||
|
||||
class TracyLlmTools
|
||||
@@ -36,15 +37,7 @@ public:
|
||||
float progress = 0;
|
||||
};
|
||||
|
||||
struct ManualChunk
|
||||
{
|
||||
std::string text;
|
||||
std::string section;
|
||||
std::string title;
|
||||
std::string parents;
|
||||
};
|
||||
|
||||
TracyLlmTools( Worker& worker );
|
||||
TracyLlmTools( Worker& worker, const TracyManualData& manual );
|
||||
~TracyLlmTools();
|
||||
|
||||
ToolReply HandleToolCalls( const nlohmann::json& json, TracyLlmApi& api, int contextSize, bool hasEmbeddingsModel );
|
||||
@@ -82,11 +75,10 @@ private:
|
||||
EmbeddingState m_manualEmbeddingState;
|
||||
std::unique_ptr<TracyLlmEmbeddings> m_manualEmbeddings;
|
||||
|
||||
std::shared_ptr<EmbedData> m_manual;
|
||||
std::vector<ManualChunk> m_manualChunks;
|
||||
std::vector<std::pair<std::string, uint32_t>> m_chunkData;
|
||||
|
||||
Worker& m_worker;
|
||||
const TracyManualData& m_manual;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
107
profiler/src/profiler/TracyManualData.cpp
Normal file
107
profiler/src/profiler/TracyManualData.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "TracyEmbed.hpp"
|
||||
#include "TracyManualData.hpp"
|
||||
|
||||
#define XXH_INLINE_ALL
|
||||
#include "tracy_xxhash.h"
|
||||
|
||||
#include "data/Manual.hpp"
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
TracyManualData::TracyManualData()
|
||||
{
|
||||
auto data = Unembed( Manual );
|
||||
m_hash = XXH3_64bits( data->data(), data->size() );
|
||||
|
||||
std::string_view manual( data->data(), data->size() );
|
||||
const auto sz = (int)data->size();
|
||||
|
||||
std::vector<int> levels = { 0 };
|
||||
std::vector<std::string> chapterNames = { "Title Page" };
|
||||
|
||||
int manualChunkPos = 0;
|
||||
int pos = 0;
|
||||
while( pos < sz )
|
||||
{
|
||||
std::string::size_type next = pos;
|
||||
for(;;)
|
||||
{
|
||||
next = manual.find( '\n', next );
|
||||
if( next == std::string_view::npos )
|
||||
{
|
||||
next = sz;
|
||||
break;
|
||||
}
|
||||
if( next+1 >= sz || manual[next+1] == '\n' ) break;
|
||||
next++;
|
||||
}
|
||||
if( next != pos )
|
||||
{
|
||||
std::string_view line( manual.data() + pos, next - pos );
|
||||
if( line[0] == '#' )
|
||||
{
|
||||
if( manualChunkPos != pos )
|
||||
{
|
||||
AddManualChunk( manual, manualChunkPos, pos, levels, chapterNames );
|
||||
manualChunkPos = pos;
|
||||
}
|
||||
|
||||
int level = 1;
|
||||
if( line.find( ".unnumbered}" ) == std::string_view::npos )
|
||||
{
|
||||
while( level < line.size() && line[level] == '#' ) level++;
|
||||
if( level != levels.size() )
|
||||
{
|
||||
levels.resize( level, 0 );
|
||||
chapterNames.resize( level );
|
||||
}
|
||||
levels[level - 1]++;
|
||||
chapterNames[level - 1] = line.substr( level + 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
pos = next + 1;
|
||||
while( pos < sz && manual[pos] == '\n' ) pos++;
|
||||
}
|
||||
if( manualChunkPos != pos )
|
||||
{
|
||||
AddManualChunk( manual, manualChunkPos, pos, levels, chapterNames );
|
||||
}
|
||||
}
|
||||
|
||||
void TracyManualData::AddManualChunk( const std::string_view& manual, int start, int end, const std::vector<int>& levels, const std::vector<std::string>& chapterNames )
|
||||
{
|
||||
while( manual[start] != '\n' ) start++;
|
||||
while( manual[start] == '\n' ) start++;
|
||||
while( manual[end-1] == '\n' ) end--;
|
||||
|
||||
if( end > start )
|
||||
{
|
||||
std::string text, section, title, parents;
|
||||
text = std::string( manual.data() + start, end - start );
|
||||
if( levels[0] != 0 )
|
||||
{
|
||||
section = std::to_string( levels[0] );
|
||||
for( size_t i=1; i<levels.size(); i++ ) section += "." + std::to_string( levels[i] );
|
||||
}
|
||||
if( levels.size() == 1 )
|
||||
{
|
||||
title = chapterNames[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
title = chapterNames[levels.size()-1];
|
||||
parents = chapterNames[0];
|
||||
for( size_t i=1; i<levels.size() - 1; i++ ) parents += " > " + chapterNames[i];
|
||||
}
|
||||
m_manualChunks.emplace_back( ManualChunk {
|
||||
.text = std::move( text ),
|
||||
.section = std::move( section ),
|
||||
.title = std::move( title ),
|
||||
.parents = std::move( parents )
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
37
profiler/src/profiler/TracyManualData.hpp
Normal file
37
profiler/src/profiler/TracyManualData.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef __TRACYMANUALDATA_HPP__
|
||||
#define __TRACYMANUALDATA_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace tracy
|
||||
{
|
||||
|
||||
class TracyManualData
|
||||
{
|
||||
public:
|
||||
struct ManualChunk
|
||||
{
|
||||
std::string text;
|
||||
std::string section;
|
||||
std::string title;
|
||||
std::string parents;
|
||||
};
|
||||
|
||||
TracyManualData();
|
||||
|
||||
[[nodiscard]] const std::vector<ManualChunk>& GetChunks() const { return m_manualChunks; }
|
||||
[[nodiscard]] uint64_t GetHash() const { return m_hash; }
|
||||
|
||||
private:
|
||||
void AddManualChunk( const std::string_view& manual, int manualChunkPos, int pos, const std::vector<int>& levels, const std::vector<std::string>& chapterNames );
|
||||
|
||||
std::vector<ManualChunk> m_manualChunks;
|
||||
uint64_t m_hash;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "TracyFileRead.hpp"
|
||||
#include "TracyFilesystem.hpp"
|
||||
#include "TracyImGui.hpp"
|
||||
#include "TracyManualData.hpp"
|
||||
#include "TracyPrint.hpp"
|
||||
#include "TracySourceView.hpp"
|
||||
#include "TracyTexture.hpp"
|
||||
@@ -57,11 +58,12 @@ View::View( void(*cbMainThread)(const std::function<void()>&, bool), const char*
|
||||
, m_achievements( s_config.achievements )
|
||||
, m_horizontalScrollMultiplier( s_config.horizontalScrollMultiplier )
|
||||
, m_verticalScrollMultiplier( s_config.verticalScrollMultiplier )
|
||||
, m_manualData( std::make_shared<TracyManualData>() )
|
||||
#ifdef __EMSCRIPTEN__
|
||||
, m_td( 2, "ViewMt" )
|
||||
#else
|
||||
, m_td( std::thread::hardware_concurrency(), "ViewMt" )
|
||||
, m_llm( m_worker )
|
||||
, m_llm( m_worker, *m_manualData )
|
||||
#endif
|
||||
{
|
||||
InitTextEditor();
|
||||
@@ -86,11 +88,12 @@ View::View( void(*cbMainThread)(const std::function<void()>&, bool), FileRead& f
|
||||
, m_achievements( s_config.achievements )
|
||||
, m_horizontalScrollMultiplier( s_config.horizontalScrollMultiplier )
|
||||
, m_verticalScrollMultiplier( s_config.verticalScrollMultiplier )
|
||||
, m_manualData( std::make_shared<TracyManualData>() )
|
||||
#ifdef __EMSCRIPTEN__
|
||||
, m_td( 2, "ViewMt" )
|
||||
#else
|
||||
, m_td( std::thread::hardware_concurrency(), "ViewMt" )
|
||||
, m_llm( m_worker )
|
||||
, m_llm( m_worker, *m_manualData )
|
||||
#endif
|
||||
{
|
||||
m_notificationTime = 4;
|
||||
|
||||
@@ -63,6 +63,7 @@ struct CpuCtxDraw;
|
||||
struct LockDraw;
|
||||
struct PlotDraw;
|
||||
struct FlameGraphContext;
|
||||
class TracyManualData;
|
||||
|
||||
|
||||
class View
|
||||
@@ -929,6 +930,8 @@ private:
|
||||
double m_horizontalScrollMultiplier = 1.0;
|
||||
double m_verticalScrollMultiplier = 1.0;
|
||||
|
||||
std::shared_ptr<TracyManualData> m_manualData;
|
||||
|
||||
TaskDispatch m_td;
|
||||
std::vector<FlameGraphItem> m_flameGraphData;
|
||||
struct
|
||||
|
||||
Reference in New Issue
Block a user