TraversionGraph Cost Function: 8 Factors That Influence Pathfinding in p2r3/convert

The TraversionGraph cost function determines conversion path weights by combining base step costs, category-change penalties, handler/format priority offsets, and lossy conversion multipliers to steer Dijkstra's algorithm toward optimal format conversion routes.

The TraversionGraph class in the p2r3/convert repository builds a weighted, directed graph representing all possible format-to-format conversions. Each edge's weight is produced by the cost function (costFunction) and determines which conversion path the pathfinder prefers. Understanding these cost components is essential for optimizing conversion chains or debugging routing decisions.

Core Cost Function Components

The cost calculation in src/TraversionGraph.ts aggregates multiple independent factors into a single edge weight used by the graph search algorithm.

Base Step Cost (DEPTH_COST)

Every conversion step incurs a constant base cost defined by DEPTH_COST. This penalizes longer conversion chains, encouraging direct routes when available. The calculation initializes with this value at lines 90-92:

let cost = DEPTH_COST;

Category-Change Penalties

When converting between different MIME categories (e.g., image to video), the cost function adds a category-change penalty. The categoryChangeCosts array stores custom costs for specific from/to pairs, while DEFAULT_CATEGORY_CHANGE_COST serves as the fallback when no specific entry exists. This logic executes in the conditional block starting at line 95:

if (fromCategory && toCategory) {
  // Category change cost calculation (lines 95-123)
}

Strict Category Enforcement

The strictCategories boolean modifies how category changes are penalized. When true, the function sums penalties for every possible category transition in the list. When false, penalties apply only if the source and target formats share no common category. This conditional appears at lines 100-110 in src/TraversionGraph.ts.

Handler and Format Priority Costs

Two priority-based costs bias the algorithm toward preferred handlers and formats:

  • Handler Priority Cost: HANDLER_PRIORITY_COST * handlerIndex adds incremental cost based on registration order (earlier handlers are cheaper). Implemented at lines 130-132.
  • Format Priority Cost: FORMAT_PRIORITY_COST multiplied by the format's index in supportedFormats makes earlier-listed formats more attractive. Calculated at lines 134-136:
cost += FORMAT_PRIORITY_COST * (handlerObj?.supportedFormats?.findIndex(f => f.mime === to.format.mime) ?? 0);

Lossy Conversion Multiplier

If the target format lacks the lossless flag, the accumulated cost is multiplied by LOSSY_COST_MULTIPLIER, strongly discouraging quality-degrading routes. This check occurs at lines 138-140:

if (!to.format.lossless) cost *= LOSSY_COST_MULTIPLIER;

Adaptive Costs and Dead-End Avoidance

The calculateAdaptiveCost method (lines 71-81) adds dynamic penalties for specific category sequences (e.g., text → image → audio) via categoryAdaptiveCosts. It also implements dead-end avoidance by returning Infinity for paths that previously failed:

if (isDeadEnd) return Infinity;

Customizing the Cost Function

You can influence pathfinding by adjusting cost parameters at initialization or runtime. The addCategoryChangeCost method allows custom penalties for specific category transitions.

import { TraversionGraph } from "./TraversionGraph";
import { FormatDefinition } from "./FormatHandler";

// Initialize graph with handlers
const graph = new TraversionGraph();
const handlers = [ffmpegHandler, imgMagickHandler];
graph.init(supportedMap, handlers, /*strictCategories=*/ false);

// Discourage image-to-video conversions with higher cost (default is 0.6)
graph.addCategoryChangeCost("image", "video", 2.0);

// Define source and target nodes
const fromNode = new ConvertPathNode(ffmpegHandler, pngFormat);
const toNode = new ConvertPathNode(imgMagickHandler, mp4Format);

// Execute search (simpleMode = true returns first valid path)
for await (const path of graph.searchPath(fromNode, toNode, true)) {
  console.log("Conversion chain:");
  path.forEach(p => console.log(` → ${p.handler.name} (${p.format.mime})`));
}

Summary

  • The TraversionGraph cost function combines eight distinct factors to calculate edge weights for Dijkstra's algorithm in src/TraversionGraph.ts.
  • Base costs (DEPTH_COST) penalize path length while category-change costs discourage跨-category conversions unless necessary.
  • Priority costs bias the algorithm toward earlier-registered handlers and formats with lower index positions.
  • The lossy multiplier (LOSSY_COST_MULTIPLIER) automatically degrades routes that result in quality loss.
  • Adaptive costs prevent problematic category sequences and avoid known dead ends by returning Infinity.
  • All cost calculations occur between lines 71-140, with the primary composition logic in the costFunction implementation.

Frequently Asked Questions

What is the default category change cost in TraversionGraph?

If no specific cost is defined for a category transition in categoryChangeCosts, the cost function uses DEFAULT_CATEGORY_CHANGE_COST. You can override this per from/to pair using addCategoryChangeCost() or by modifying the map directly before executing the search.

How does strictCategories affect pathfinding?

When strictCategories is set to true during graph initialization, the cost function sums penalties for every possible category transition. When false, it only adds costs when the source and target formats share no common MIME category, allowing more flexible routing through intermediate formats that share categories.

Why does the cost function multiply by LOSSY_COST_MULTIPLIER?

The lossy multiplier penalizes conversion routes that result in quality loss. If to.format.lossless evaluates to false, the accumulated cost is multiplied by this constant (typically a value greater than 1.0), making lossless routes significantly cheaper and thus preferred by Dijkstra's algorithm.

Where is the cost function defined in the source code?

The primary cost calculation logic resides in src/TraversionGraph.ts within the costFunction method (lines 90-140). Adaptive cost modifications and dead-end detection occur in the calculateAdaptiveCost method (lines 71-81), which is called during the path exploration phase.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →