build static method

AssembledContext build({
  1. required List<ChunkSearchResult> searchResults,
  2. int tokenBudget = 2000,
  3. ContextStrategy strategy = ContextStrategy.relevanceFirst,
  4. String separator = '\n\n---\n\n',
  5. bool singleSourceMode = false,
})

Assemble context from search results within a token budget.

searchResults - Chunks ranked by relevance (highest first). tokenBudget - Maximum tokens to use. strategy - How to select and order chunks. separator - Text between chunks. singleSourceMode - If true, only include chunks from the most relevant source.

Implementation

static AssembledContext build({
  required List<ChunkSearchResult> searchResults,
  int tokenBudget = 2000,
  ContextStrategy strategy = ContextStrategy.relevanceFirst,
  String separator = '\n\n---\n\n',
  bool singleSourceMode = false,
}) {
  if (searchResults.isEmpty) {
    return const AssembledContext(
      text: '',
      includedChunks: [],
      estimatedTokens: 0,
      remainingBudget: 0,
    );
  }

  // Filter to single source if requested
  var filteredResults = searchResults;
  if (singleSourceMode) {
    filteredResults = _filterToMostRelevantSource(searchResults);
  }

  // Apply strategy
  final orderedResults = switch (strategy) {
    ContextStrategy.relevanceFirst => filteredResults,
    ContextStrategy.diverseSources => _diversifySources(filteredResults),
    ContextStrategy.chronological => _orderChronologically(filteredResults),
  };

  // Select chunks within budget
  final selected = <ChunkSearchResult>[];
  var usedTokens = 0;
  final separatorTokens = (separator.length / 4).ceil();

  for (final chunk in orderedResults) {
    final chunkTokens = (chunk.content.length / 4).ceil();
    final totalIfAdded =
        usedTokens + chunkTokens + (selected.isEmpty ? 0 : separatorTokens);

    if (totalIfAdded <= tokenBudget) {
      selected.add(chunk);
      usedTokens = totalIfAdded;
    } else {
      break; // Budget exhausted
    }
  }

  // Build final text - group by source with clear headers (skip headers in singleSourceMode)
  final text = _buildGroupedText(
    selected,
    separator,
    skipHeaders: singleSourceMode,
  );

  return AssembledContext(
    text: text,
    includedChunks: selected,
    estimatedTokens: usedTokens,
    remainingBudget: tokenBudget - usedTokens,
  );
}