> ## Documentation Index
> Fetch the complete documentation index at: https://docs.traycer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Model Profiles and Cost Calculator

> Understanding model profiles, selection modes, and cost estimation.

export const ModelPricingCalculator = () => {
  const catalog = typeof globalThis !== "undefined" ? globalThis.MODEL_PRICING_CATALOG : undefined;
  const [selectedSteps, setSelectedSteps] = React.useState(() => {
    if (typeof window !== "undefined") {
      try {
        const saved = localStorage.getItem("traycer-model-profile-custom");
        if (saved) {
          const parsed = JSON.parse(saved);
          if (parsed && typeof parsed === "object") {
            return parsed;
          }
        }
      } catch (e) {}
    }
    return {};
  });
  const [activeProfile, setActiveProfile] = React.useState(() => {
    if (typeof window !== "undefined") {
      try {
        const saved = localStorage.getItem("traycer-model-profile-active");
        if (saved) {
          return saved;
        }
      } catch (e) {}
    }
    return null;
  });
  if (!catalog) {
    return <p style={{
      opacity: 0.8
    }}>Model catalog is unavailable.</p>;
  }
  const {creators = [], modelDescriptors = [], systemProfiles = {}, profileOrder = [], defaultProfileId: catalogDefaultProfileId, featureRows: catalogFeatureRows = [], costReferenceSteps = {}, reasoningMultipliersByCreator = {}, cacheCostModifier = {}, platformAccessFee = 0.1} = catalog;
  const getReasoningMultiplier = descriptor => {
    const creatorMultipliers = (reasoningMultipliersByCreator[descriptor.creator] ?? reasoningMultipliersByCreator["default"]) ?? ({});
    return creatorMultipliers[descriptor.reasoning] ?? 1.0;
  };
  const getCacheModifier = descriptor => {
    return cacheCostModifier[descriptor.creator] ?? 1.0;
  };
  const featureRows = Array.isArray(catalogFeatureRows) ? catalogFeatureRows : [];
  if (featureRows.length === 0) {
    return <p style={{
      opacity: 0.8
    }}>No feature rows are available.</p>;
  }
  const descriptorById = Object.fromEntries(modelDescriptors.map(descriptor => [descriptor.id, descriptor]));
  const groupedDescriptors = {};
  for (const creator of creators) {
    groupedDescriptors[creator] = modelDescriptors.filter(descriptor => descriptor.creator === creator);
  }
  const orderedProfileIds = profileOrder.length > 0 ? profileOrder : Object.keys(systemProfiles);
  const availableProfiles = orderedProfileIds.map(profileId => {
    const profile = systemProfiles[profileId];
    if (!profile || !profile.steps) {
      return null;
    }
    return {
      ...profile,
      id: profile.id ?? profileId
    };
  }).filter(Boolean);
  if (availableProfiles.length === 0) {
    return <p style={{
      opacity: 0.8
    }}>No model profiles are available.</p>;
  }
  const profileById = Object.fromEntries(availableProfiles.map(profile => [profile.id, profile]));
  const defaultProfileId = catalogDefaultProfileId && profileById[catalogDefaultProfileId] ? catalogDefaultProfileId : availableProfiles[0].id;
  const defaultProfile = profileById[defaultProfileId];
  const updateSelectedSteps = (newSteps, profileType = 'custom') => {
    setSelectedSteps(newSteps);
    setActiveProfile(profileType);
    if (typeof window !== 'undefined') {
      try {
        localStorage.setItem('traycer-model-profile-active', profileType);
        if (profileType === 'custom') {
          localStorage.setItem('traycer-model-profile-custom', JSON.stringify(newSteps));
        }
      } catch (e) {}
    }
  };
  const loadCustomProfile = () => {
    if (typeof window !== 'undefined') {
      try {
        const saved = localStorage.getItem('traycer-model-profile-custom');
        if (saved) {
          const parsed = JSON.parse(saved);
          updateSelectedSteps(parsed, 'custom');
        }
      } catch (e) {}
    }
  };
  const hasCustomProfile = () => {
    if (typeof window !== 'undefined') {
      try {
        const saved = localStorage.getItem('traycer-model-profile-custom');
        return !!saved;
      } catch (e) {
        return false;
      }
    }
    return false;
  };
  const activeProfileId = activeProfile === "custom" ? "custom" : profileById[activeProfile] ? activeProfile : defaultProfileId;
  const formatReasoning = reasoning => {
    if (reasoning === "none") {
      return "No thinking";
    }
    return reasoning.toUpperCase();
  };
  const formatModelOption = descriptor => {
    return `${descriptor.name} (${formatReasoning(descriptor.reasoning)})`;
  };
  const formatProfileLabel = profileName => {
    return profileName.replace(/\s+profile$/i, "");
  };
  const averageTokenPrice = descriptor => {
    return (descriptor.input + descriptor.output) / 2;
  };
  const safeMultiplier = value => {
    if (!Number.isFinite(value) || value <= 0) {
      return 1;
    }
    return value;
  };
  const rows = featureRows.map(feature => {
    const referenceDescriptorId = (costReferenceSteps[feature.id] ?? defaultProfile.steps[feature.id]) ?? modelDescriptors[0]?.id;
    const fallbackDescriptorId = defaultProfile.steps[feature.id] ?? modelDescriptors[0]?.id;
    if (!fallbackDescriptorId) {
      return {
        ...feature,
        descriptor: {
          id: "",
          name: "Unavailable",
          creator: "",
          reasoning: "none",
          input: 1,
          output: 1
        },
        estimatedCredits: feature.baseCredits
      };
    }
    const selectedDescriptorId = selectedSteps[feature.id] ?? fallbackDescriptorId;
    const selectedDescriptor = descriptorById[selectedDescriptorId] ?? descriptorById[fallbackDescriptorId];
    const referenceDescriptor = (descriptorById[referenceDescriptorId] ?? descriptorById[fallbackDescriptorId]) ?? selectedDescriptor;
    if (!selectedDescriptor || !referenceDescriptor) {
      return {
        ...feature,
        descriptor: {
          id: "",
          name: "Unavailable",
          creator: "",
          reasoning: "none",
          input: 1,
          output: 1
        },
        estimatedCredits: feature.baseCredits
      };
    }
    const modelFactor = safeMultiplier(averageTokenPrice(selectedDescriptor) / averageTokenPrice(referenceDescriptor));
    const reasoningFactor = safeMultiplier(getReasoningMultiplier(selectedDescriptor) / getReasoningMultiplier(referenceDescriptor));
    const cacheFactor = safeMultiplier(getCacheModifier(selectedDescriptor) / getCacheModifier(referenceDescriptor));
    const multiplier = modelFactor * reasoningFactor * cacheFactor;
    const modelBaseCost = feature.baseCredits > 0 ? feature.baseCredits - platformAccessFee : 0;
    const estimatedCredits = feature.baseCredits > 0 ? modelBaseCost * multiplier + platformAccessFee : 0;
    return {
      ...feature,
      descriptor: selectedDescriptor,
      estimatedCredits
    };
  });
  const profileButtonStyle = isActive => ({
    padding: "6px 14px",
    fontSize: 14,
    fontWeight: isActive ? 600 : 500,
    cursor: "pointer",
    border: "1px solid rgba(128, 128, 128, 0.2)",
    borderRadius: 4,
    background: isActive ? "rgba(128, 128, 128, 0.1)" : "transparent",
    color: "inherit",
    transition: "all 0.15s ease"
  });
  return <div style={{
    marginTop: 20,
    marginBottom: 24
  }}>
      <div style={{
    marginBottom: 16,
    display: "flex",
    gap: 8,
    alignItems: "center",
    flexWrap: "wrap"
  }}>
        <span style={{
    opacity: 0.7,
    fontSize: 14
  }}>Profile:</span>
        {availableProfiles.map(profile => <button key={profile.id} type="button" style={profileButtonStyle(activeProfileId === profile.id)} onClick={() => updateSelectedSteps({
    ...profile.steps
  }, profile.id)} onMouseEnter={e => activeProfileId !== profile.id && (e.target.style.background = "rgba(128, 128, 128, 0.05)")} onMouseLeave={e => activeProfileId !== profile.id && (e.target.style.background = "transparent")}>
            {formatProfileLabel(profile.name)}
          </button>)}
        {activeProfileId === "custom" ? <span style={{
    padding: "6px 14px",
    fontSize: 14,
    fontWeight: 500,
    border: "1px solid rgba(128, 128, 128, 0.2)",
    borderRadius: 4,
    background: "rgba(128, 128, 128, 0.1)",
    opacity: 0.7
  }}>
            Custom
          </span> : hasCustomProfile() ? <button type="button" style={{
    padding: "6px 14px",
    fontSize: 14,
    fontWeight: 500,
    cursor: "pointer",
    border: "1px solid rgba(128, 128, 128, 0.2)",
    borderRadius: 4,
    background: "transparent",
    color: "inherit",
    opacity: 0.6,
    transition: "all 0.15s ease"
  }} onClick={loadCustomProfile} onMouseEnter={e => {
    e.target.style.background = "rgba(128, 128, 128, 0.05)";
    e.target.style.opacity = 1;
  }} onMouseLeave={e => {
    e.target.style.background = "transparent";
    e.target.style.opacity = 0.6;
  }}>
            Custom
          </button> : null}
      </div>

      <div>
        <table>
          <thead>
            <tr>
              <th>Feature</th>
              <th>Model</th>
              <th>Credits</th>
            </tr>
          </thead>
          <tbody>
            {rows.map(row => <tr key={row.id}>
                <td>
                  <strong>{row.label}</strong>
                </td>
                <td>
                  <div style={{
    position: "relative"
  }}>
                    <select style={{
    width: "100%",
    padding: "6px 28px 6px 10px",
    fontSize: 14,
    border: "1px solid rgba(128, 128, 128, 0.2)",
    borderRadius: 4,
    background: "transparent",
    color: "inherit",
    cursor: "pointer",
    appearance: "none",
    WebkitAppearance: "none",
    MozAppearance: "none"
  }} name={`model-${row.id}`} aria-label={`${row.label} model`} value={row.descriptor.id} onChange={event => {
    const nextModelId = event.target.value;
    updateSelectedSteps({
      ...selectedSteps,
      [row.id]: nextModelId
    });
  }}>
                      {creators.map(creator => <optgroup key={creator} label={creator.toUpperCase()}>
                          {groupedDescriptors[creator].map(descriptor => <option key={descriptor.id} value={descriptor.id}>
                              {formatModelOption(descriptor)}
                            </option>)}
                        </optgroup>)}
                    </select>
                    <span style={{
    position: "absolute",
    right: 10,
    top: "50%",
    transform: "translateY(-50%)",
    pointerEvents: "none",
    opacity: 0.5
  }}>▼</span>
                  </div>
                </td>
                <td style={{
    whiteSpace: "nowrap"
  }}>
                  <code>{row.estimatedCredits.toFixed(3)}</code>
                </td>
              </tr>)}
          </tbody>
        </table>
      </div>
    </div>;
};

## Introduction

Traycer uses different models for different steps (planning, review, verification, iteration, etc.). You can control this behavior through **profiles** and **model selection modes**.

## Understanding Profiles

A **profile** is a preset configuration that maps specific models to each step in your Traycer tasks.

* Different steps have different requirements—some need deep reasoning, others benefit from speed
* Profiles bundle these step-to-model mappings with specific capability/cost tradeoffs
* Steps include operations like planning, iteration, review, verification, and orchestration

## System Profiles

Traycer provides two built-in profiles that serve as the foundation for model selection:

### Balanced

The default profile for everyday usage. Offers a carefully tuned mix of quality, speed, and cost across all steps.

### Frontier

The premium profile using top-tier models with maximum reasoning capability. Best for complex tasks where quality is the priority.

## Model Selection Modes

Traycer supports three different ways to select models:

### 1. Profile Mode (Default)

Use **Balanced** or **Frontier** as-is. Each step automatically uses its pre-configured model from the selected profile.

### 2. Custom Profile

Start from **Balanced** or **Frontier**, then override specific steps with different models:

* Modify only the steps you want to change
* Unmodified steps continue using the base profile's models
* Save your configuration as a reusable custom profile
* Great for optimizing specific workflow patterns

### 3. Single Model

Select one model to use across **all** steps:

* Simplest option—one model everywhere
* Less optimized than profiles, but easier to reason about
* Useful when you want consistent behavior or have a preferred model

## Cost Calculator and Comparison

Use the interactive calculator below to compare profiles, switch between selection modes, and estimate credit usage for each step.

<ModelPricingCalculator />

## Where to Access Model Selection

Model selection controls are available throughout Traycer:

* **Task features**: Configure models when creating tasks, generating plans, iterating, or running verifications
* **YOLO or Executions configuration**: Define model behavior for autonomous features via the configuration modal
