benchmark

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

@index Match analysis helpers for benchmark run results.

@index Comparison helpers for benchmark runs with and without CCG.

@index Benchmark corpus loading, saving, and validation utilities.

@index JSONL session parsing and extraction helpers for benchmark analysis.

@index Markdown report generation for benchmark comparisons.

@index Benchmark runner orchestration for Claude CLI subprocess execution.

Package benchmark provides types and utilities for benchmarking ccg MCP tool effectiveness.

@index Token baseline and graph-assisted retrieval benchmarking helpers.

Index

Constants

View Source
const DefaultNaiveTokensMaxFileBytes int64 = 1 << 20

DefaultNaiveTokensMaxFileBytes caps per-file size considered by the naive baseline. @intent prevent oversized files from skewing or dominating the naive token baseline.

Variables

This section is empty.

Functions

func BuildClaudeArgs

func BuildClaudeArgs(cfg RunnerConfig) []string

BuildClaudeArgs constructs the CLI argument slice for `claude -p`. Working directory is set on the subprocess directly (not via --cwd flag). In "without-ccg" mode, --strict-mcp-config disables all MCP servers. NOTE: For actual runs, use Runner.Run which creates a real temp file for --mcp-config. @intent expose deterministic argument construction for tests and tooling that inspect benchmark invocation behavior.

func BuildPrompt

func BuildPrompt(q Query) string

BuildPrompt creates the prompt string that wraps a query with benchmark markers. The markers allow the JSONL analyzer to locate query boundaries. @intent surround benchmark queries with markers so later session analysis can recover exact query segments.

func CountCcgToolCalls

func CountCcgToolCalls(result RunResult) int

CountCcgToolCalls returns the number of tool calls using mcp__ccg__ prefix. @intent quantify how much a benchmark run relied on CCG-specific MCP tools.

func EstimateTokens

func EstimateTokens(text string) int

EstimateTokens는 텍스트의 대략적인 토큰 수를 반환한다 (4자 = 1토큰). @intent provide a cheap, consistent token estimate for baseline-versus-graph comparisons.

func ExtractAnswer

func ExtractAnswer(seg QuerySegment) string

ExtractAnswer returns the text of the last assistant text block in the segment. @intent capture the final assistant answer that should be evaluated for symbol hits.

func ExtractFilesRead

func ExtractFilesRead(seg QuerySegment) []string

ExtractFilesRead returns deduplicated file paths from Read tool calls in the segment. @intent estimate file-inspection behavior from tool-use records without double-counting reads.

func ExtractTokens

func ExtractTokens(seg QuerySegment) (inputTokens, outputTokens int)

ExtractTokens sums input and output token counts across all messages in a segment. @intent aggregate token usage for the full lifecycle of one benchmark query.

func GraphTokens

func GraphTokens(ctx context.Context, db *gorm.DB, backend SearchBackend, expander NodeExpander, query, repoRoot string, limit int) (tokens int, elapsedMs int64, count int, err error)

GraphTokens는 단일 쿼리로 검색해 토큰 수, 경과 시간(ms), 결과 수를 반환한다. @intent measure the token footprint of graph-assisted retrieval for one query.

func MatchFiles

func MatchFiles(result RunResult, query Query) float64

MatchFiles computes the ratio of expected files found in FilesRead or mentioned in the Answer text (as a fallback when tool-call data is unavailable). Returns 1.0 if no expected files are specified. @intent score whether a benchmark run inspected the files the query was expected to touch.

func MatchSymbols

func MatchSymbols(result RunResult, query Query) float64

MatchSymbols computes the ratio of expected symbols found in the answer text. Returns 1.0 if no expected symbols are specified. @intent score whether the final answer mentioned the symbols the query was expected to surface.

func NaiveTokens

func NaiveTokens(repoRoot string, exts []string) (int, error)

NaiveTokens는 repoRoot 아래 exts 확장자를 가진 모든 파일의 토큰 수 합계를 반환한다. @intent measure the naive full-file reading baseline for a repository slice.

func NaiveTokensWithOptions

func NaiveTokensWithOptions(repoRoot string, exts []string, opts NaiveTokensOptions) (int, error)

NaiveTokensWithOptions는 repoRoot 아래 exts 확장자를 가진 파일 중 옵션에 맞는 파일만 집계한다. @intent compute a configurable naive token baseline while skipping excluded or oversized files.

func SaveCorpus

func SaveCorpus(path string, c *Corpus) error

SaveCorpus writes a Corpus to a YAML file. @intent persist benchmark corpus definitions in the YAML format used by the CLI.

func ValidateCorpus

func ValidateCorpus(c *Corpus) error

ValidateCorpus checks that all queries have required fields and no duplicate IDs. @intent reject malformed benchmark corpora before any benchmark execution depends on them.

func WriteReport

func WriteReport(report *ComparisonReport, outPath string) error

WriteReport renders a ComparisonReport as markdown and writes it to outPath. @intent turn benchmark comparison data into a readable artifact for sharing and regression tracking.

Types

type BenchmarkRun

type BenchmarkRun struct {
	Mode    string      `json:"mode"`
	RunAt   time.Time   `json:"run_at"`
	Results []RunResult `json:"results"`
}

BenchmarkRun holds all results from a single benchmark execution. @intent represent one complete benchmark session for a given execution mode.

func (*BenchmarkRun) ResultByID

func (r *BenchmarkRun) ResultByID(id string) *RunResult

ResultByID returns the RunResult for the given query ID, or nil if not found. @intent support direct lookup of a query result inside a benchmark run.

type ComparisonReport

type ComparisonReport struct {
	WithCCG        *BenchmarkRun `json:"with_ccg"`
	WithoutCCG     *BenchmarkRun `json:"without_ccg,omitempty"`
	Matches        []MatchResult `json:"matches"`
	MatchesWithout []MatchResult `json:"matches_without,omitempty"`
}

ComparisonReport holds a comparison between two benchmark runs. @intent package benchmark runs and their scored matches into one comparison artifact.

func Compare

func Compare(withCCG *BenchmarkRun, withoutCCG *BenchmarkRun, corpus *Corpus) *ComparisonReport

Compare builds a ComparisonReport from a with-ccg run and an optional without-ccg run. Matches contains with-ccg metrics; MatchesWithout contains without-ccg metrics when provided. @intent produce one report that captures both benchmark modes and their scored query matches.

type ContentBlock

type ContentBlock struct {
	Type  string          `json:"type"`
	Text  string          `json:"text,omitempty"`
	Name  string          `json:"name,omitempty"` // tool_use
	ID    string          `json:"id,omitempty"`
	Input json.RawMessage `json:"input,omitempty"`
}

ContentBlock is a single content item (text, tool_use, etc.). @intent represent individual Claude message blocks so benchmark analyzers can inspect tool calls and text.

type Corpus

type Corpus struct {
	Version string  `yaml:"version" json:"version,omitempty"`
	Queries []Query `yaml:"queries" json:"queries"`
}

Corpus holds the collection of benchmark queries. @intent group benchmark queries into a reusable corpus that can be run and validated together.

func LoadCorpus

func LoadCorpus(path string) (*Corpus, error)

LoadCorpus reads a queries.yaml file and validates its contents. @intent load a benchmark corpus from disk while enforcing schema and ID constraints up front.

type Executor

type Executor interface {
	Execute(ctx context.Context, args []string, prompt, dir string) ([]byte, error)
}

Executor abstracts the subprocess execution so tests can inject a mock. dir sets the working directory for the subprocess; empty means inherit from parent. @intent decouple benchmark orchestration from the concrete Claude CLI process implementation.

type MatchResult

type MatchResult struct {
	QueryID                 string  `json:"query_id"`
	FileHitRatio            float64 `json:"file_hit_ratio"`
	SymbolHitRatio          float64 `json:"symbol_hit_ratio"`
	StrictSymbolHitRatio    float64 `json:"strict_symbol_hit_ratio"`
	TentativeSymbolHitRatio float64 `json:"tentative_symbol_hit_ratio"`
	LLMStrictBias           float64 `json:"llm_strict_bias"`
	StrictContaminationRate float64 `json:"strict_contamination_rate"`
	ToolAwareStrictRatio    float64 `json:"tool_aware_strict_ratio"`
	ToolAwareTentativeRatio float64 `json:"tool_aware_tentative_ratio"`
	TotalToolCalls          int     `json:"total_tool_calls"`
	CcgToolCalls            int     `json:"ccg_tool_calls"`
	TotalInputTokens        int     `json:"total_input_tokens"`
}

MatchResult holds the computed match metrics for a single query. @intent summarize scored file, symbol, tool, and token metrics for one benchmark query.

func AnalyzeRun

func AnalyzeRun(run *BenchmarkRun, corpus *Corpus) []MatchResult

AnalyzeRun computes MatchResult for every result in the run against the corpus. Results with no matching query are skipped. @intent turn a full benchmark run into per-query scored matches against the corpus definition.

func ComputeMatch

func ComputeMatch(result RunResult, query Query) MatchResult

ComputeMatch derives a MatchResult from a single RunResult and its Query. @intent consolidate per-query benchmark scoring into one reusable result structure.

type MessagePayload

type MessagePayload struct {
	Role    string         `json:"role"`
	Content []ContentBlock `json:"content"`
	Usage   *UsageInfo     `json:"usage,omitempty"`
}

MessagePayload is the nested message object inside a SessionMessage. @intent expose assistant role, content blocks, and usage data from session lines.

type NaiveTokensOptions

type NaiveTokensOptions struct {
	Excludes     []string
	MaxFileBytes int64
}

NaiveTokensOptions tunes which files contribute to the naive token baseline. @intent control file selection (excludes, size cap) for the naive reading baseline.

type NodeExpander

type NodeExpander interface {
	GetEdgesFrom(ctx context.Context, nodeID uint) ([]model.Edge, error)
	GetNodesByIDs(ctx context.Context, ids []uint) ([]model.Node, error)
	GetAnnotation(ctx context.Context, nodeID uint) (*model.Annotation, error)
}

NodeExpander는 노드의 1-hop 이웃과 어노테이션을 가져오는 추상화다. nil을 전달하면 확장 없이 기본 검색 결과만 사용한다. @intent optionally enrich graph token context with neighbors and annotations beyond raw search hits.

type Query

type Query struct {
	ID                       string   `yaml:"id"                       json:"id"`
	Description              string   `yaml:"description"              json:"description"`
	ExpectedFiles            []string `yaml:"expected_files"           json:"expected_files,omitempty"`
	ExpectedSymbols          []string `yaml:"expected_symbols"         json:"expected_symbols,omitempty"`
	ExpectedStrictSymbols    []string `yaml:"expected_strict_symbols"  json:"expected_strict_symbols,omitempty"`
	ExpectedTentativeSymbols []string `yaml:"expected_tentative_symbols" json:"expected_tentative_symbols,omitempty"`
	Difficulty               string   `yaml:"difficulty"               json:"difficulty,omitempty"`
}

Query represents a single benchmark query with expected results. @intent define one benchmark prompt and the files or symbols it is expected to surface.

type QuerySegment

type QuerySegment struct {
	QueryID  string
	Messages []SessionMessage
}

QuerySegment groups the messages belonging to a single benchmark query. @intent isolate the portion of a session that belongs to one marked benchmark query.

func ExtractQuerySegments

func ExtractQuerySegments(msgs []SessionMessage) ([]QuerySegment, error)

ExtractQuerySegments splits a session's messages into per-query segments using markers. An unclosed segment (START without END) is included, spanning to the last message. @intent reconstruct per-query transcript slices from a whole benchmark session log.

type RunResult

type RunResult struct {
	QueryID      string     `json:"query_id"`
	ToolCalls    []ToolCall `json:"tool_calls,omitempty"`
	FilesRead    []string   `json:"files_read,omitempty"`
	Answer       string     `json:"answer,omitempty"`
	InputTokens  int        `json:"input_tokens"`
	OutputTokens int        `json:"output_tokens"`
	ElapsedMs    int64      `json:"elapsed_ms"`
	Error        string     `json:"error,omitempty"`
}

RunResult captures the outcome of executing a single query. @intent store answer text, tool usage, timing, and token counts for one benchmark query.

func ExtractRunResult

func ExtractRunResult(queryID string, seg QuerySegment) RunResult

ExtractRunResult builds a RunResult from a query segment. @intent convert an extracted query segment into the canonical benchmark run result shape.

type RunTokenBenchOptions

type RunTokenBenchOptions struct {
	Naive NaiveTokensOptions
}

RunTokenBenchOptions bundles optional knobs for the token benchmark execution. @intent extend the token benchmark with baseline tuning while keeping the core API stable.

type Runner

type Runner struct {
	// contains filtered or unexported fields
}

Runner executes benchmark queries sequentially using the provided Executor. @intent own the end-to-end lifecycle of benchmark query execution for one mode.

func NewRunner

func NewRunner(cfg RunnerConfig, exec Executor) *Runner

NewRunner creates a Runner with the given config and executor. @intent assemble benchmark orchestration from execution config and a subprocess adapter.

func (*Runner) Run

func (r *Runner) Run(ctx context.Context, corpus *Corpus) (*BenchmarkRun, error)

Run executes each query in the corpus and returns a BenchmarkRun. Executor errors are captured in RunResult.Error rather than aborting the run. @intent execute the full benchmark corpus while preserving per-query failures in the final report.

type RunnerConfig

type RunnerConfig struct {
	Mode         string // "with-ccg" | "without-ccg"
	CWD          string // benchmark workspace directory
	MaxToolCalls int
	TimeoutSec   int
}

RunnerConfig holds configuration for a benchmark run. @intent collect execution mode, workspace, and timeout settings for one benchmark run.

func DefaultRunnerConfig

func DefaultRunnerConfig() RunnerConfig

DefaultRunnerConfig returns a RunnerConfig with sensible defaults. @intent provide conservative benchmark defaults that work for most local runs.

type SearchBackend

type SearchBackend interface {
	Query(ctx context.Context, db *gorm.DB, query string, limit int) ([]model.Node, error)
}

SearchBackend는 FTS 검색 백엔드 추상화다. @intent abstract graph search so token benchmarks can run against any configured backend.

type SessionMessage

type SessionMessage struct {
	Type      string          `json:"type"`
	Message   *MessagePayload `json:"message,omitempty"`
	Content   json.RawMessage `json:"content,omitempty"`
	ToolUseID string          `json:"tool_use_id,omitempty"`
	IsError   bool            `json:"is_error,omitempty"`
}

SessionMessage represents one line from a Claude Code session JSONL file. @intent model the subset of Claude session JSONL needed to reconstruct benchmark runs.

func ParseJSONL

func ParseJSONL(path string) ([]SessionMessage, error)

ParseJSONL reads a Claude Code session JSONL file and returns all parsed lines. Lines that are not valid JSON are silently skipped. @intent ingest recorded Claude sessions even when they contain partial or non-JSON noise lines.

type TokenBenchResult

type TokenBenchResult struct {
	QueryID         string  `json:"query_id"`
	NaiveTokens     int     `json:"naive_tokens"`
	GraphTokens     int     `json:"graph_tokens"`
	Ratio           float64 `json:"ratio"`
	SearchElapsedMs int64   `json:"search_elapsed_ms"`
	ResultCount     int     `json:"result_count"`
	// Recall: 정답 파일/심볼이 결과에 포함되었는지 측정
	FilesHit     int     `json:"files_hit"`
	FilesTotal   int     `json:"files_total"`
	SymbolsHit   int     `json:"symbols_hit"`
	SymbolsTotal int     `json:"symbols_total"`
	Recall       float64 `json:"recall"`
}

TokenBenchResult는 단일 쿼리에 대한 토큰 벤치마크 결과다. @intent report naive-versus-graph token cost and recall for one benchmark query.

func RunTokenBench

func RunTokenBench(ctx context.Context, db *gorm.DB, backend SearchBackend, expander NodeExpander, corpus *Corpus, repoRoot string, exts []string, limit int) ([]TokenBenchResult, error)

RunTokenBench는 corpus의 각 쿼리에 대해 naive/graph 토큰과 recall을 비교한다. 검색은 항상 Description을 사용하며, expected_symbols/files는 정답 매칭에만 사용한다. limit은 쿼리당 총 결과 예산이며 단어 수에 반비례해 단어당 limit이 자동 조정된다. @intent execute the full token benchmark corpus and compare naive reading cost against graph retrieval cost.

func RunTokenBenchWithOptions

func RunTokenBenchWithOptions(ctx context.Context, db *gorm.DB, backend SearchBackend, expander NodeExpander, corpus *Corpus, repoRoot string, exts []string, limit int, opts RunTokenBenchOptions) ([]TokenBenchResult, error)

@intent extend token benchmarking with baseline tuning options while preserving the core execution flow.

type ToolCall

type ToolCall struct {
	Tool      string `json:"tool"`
	ToolUseID string `json:"tool_use_id,omitempty"`
	Input     string `json:"input,omitempty"`
	Output    string `json:"output,omitempty"`
}

ToolCall records a single tool invocation during query execution. @intent capture the tool usage footprint of one benchmarked query execution.

func ExtractToolCalls

func ExtractToolCalls(seg QuerySegment) []ToolCall

ExtractToolCalls collects all tool_use blocks from a segment's messages. @intent recover structured tool invocation history for one benchmark query.

type UsageInfo

type UsageInfo struct {
	InputTokens  int `json:"input_tokens"`
	OutputTokens int `json:"output_tokens"`
}

UsageInfo holds token usage from Claude's response. @intent capture token accounting for one Claude message so benchmark runs can aggregate usage.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL