promptpilot 0.1.8 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1420,6 +1420,16 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
1420
1420
  };
1421
1421
  }
1422
1422
  const claudeTiersOnly = isClaudeTiersOnlyTargetSet(availableTargets);
1423
+ const routingCandidates = claudeTiersOnly ? filterClaudeTierCandidates(availableTargets, options.input, options.routingPriority) : availableTargets;
1424
+ if (routingCandidates.length === 1) {
1425
+ return {
1426
+ selectedTarget: stripInternalTargetFields(routingCandidates[0]),
1427
+ rankedTargets: [{ ...stripInternalTargetFields(routingCandidates[0]), rank: 1, reason: "Selected by Claude tier pre-filter based on prompt signals." }],
1428
+ routingReason: "Selected by Claude tier pre-filter based on prompt signals.",
1429
+ routingWarnings: [],
1430
+ routingProvider: "heuristic"
1431
+ };
1432
+ }
1423
1433
  const response = await this.client.generateJson({
1424
1434
  model: routerModel,
1425
1435
  timeoutMs: options.input.timeoutMs ?? this.config.timeoutMs,
@@ -1437,7 +1447,7 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
1437
1447
  targetHints: options.input.targetHints ?? [],
1438
1448
  workloadBias: options.workloadBias,
1439
1449
  routingPriority: options.routingPriority,
1440
- candidateTargets: availableTargets.map((target) => ({
1450
+ candidateTargets: routingCandidates.map((target) => ({
1441
1451
  id: target.id,
1442
1452
  provider: target.provider,
1443
1453
  model: target.model,
@@ -1456,7 +1466,7 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
1456
1466
  new Set((response.rankedTargetIds ?? []).map((value) => value.trim()).filter(Boolean))
1457
1467
  ).slice(0, Math.max(1, options.routingTopK));
1458
1468
  const rankedTargets = rankedTargetIds.map((id, index) => {
1459
- const target = availableTargets.find((candidate) => candidate.id === id);
1469
+ const target = routingCandidates.find((candidate) => candidate.id === id);
1460
1470
  if (!target) {
1461
1471
  return null;
1462
1472
  }
@@ -1467,7 +1477,7 @@ Mode: Ultra compression. Minimize tokens aggressively.` : getOptimizationSystemP
1467
1477
  };
1468
1478
  }).filter((value) => value !== null);
1469
1479
  const selectedTargetId = response.selectedTargetId?.trim();
1470
- const selectedTargetCandidate = (selectedTargetId && availableTargets.find((candidate) => candidate.id === selectedTargetId)) ?? (rankedTargets[0] ? availableTargets.find(
1480
+ const selectedTargetCandidate = (selectedTargetId && routingCandidates.find((candidate) => candidate.id === selectedTargetId)) ?? (rankedTargets[0] ? routingCandidates.find(
1471
1481
  (candidate) => candidate.provider === rankedTargets[0].provider && candidate.model === rankedTargets[0].model && candidate.label === rankedTargets[0].label
1472
1482
  ) ?? null : null);
1473
1483
  if (!selectedTargetCandidate || rankedTargets.length === 0) {
@@ -1665,12 +1675,13 @@ function buildDownstreamRoutingSystemPrompt(priority, workloadBias, claudeTiersO
1665
1675
  if (claudeTiersOnly) {
1666
1676
  lines.push(
1667
1677
  "You are choosing between Claude model tiers (Haiku, Sonnet, Opus).",
1668
- "Haiku: fastest and cheapest. Best for email, chat, support, summarization, and simple rewrites. Avoid for deep coding or multi-step reasoning.",
1669
- "Sonnet: balanced cost and capability. Best for coding, debugging, refactoring, writing, and general-purpose tasks. The default for most prompts.",
1670
- "Opus: most capable and most expensive. Reserve for complex architecture decisions, multi-constraint agentic planning, long-horizon reasoning, or prompts that clearly require the strongest model.",
1671
- "When routing priority is cheapest_adequate: prefer Haiku for lightweight tasks, Sonnet for most code and writing tasks, and Opus only when clearly necessary.",
1672
- "When routing priority is best_quality: prefer Opus for code and reasoning, Sonnet for writing and simple code.",
1673
- "When routing priority is fastest_adequate: prefer Haiku unless the task clearly needs Sonnet-level capability."
1678
+ "Haiku: fastest and cheapest. ONLY suitable for email, chat, support, summarization, and trivial one-sentence rewrites. Do NOT use Haiku for any coding, debugging, refactoring, or technical tasks.",
1679
+ "Sonnet: balanced cost and capability. The DEFAULT for all coding, debugging, refactoring, writing, and general-purpose tasks. If the prompt mentions code, a file, a module, a bug, or any technical work, choose Sonnet at minimum.",
1680
+ "Opus: most capable and most expensive. Use for complex architecture decisions, multi-constraint agentic planning, system design, long-horizon reasoning, or when the prompt explicitly requires the strongest model.",
1681
+ "When routing priority is cheapest_adequate: Haiku for non-technical lightweight tasks only, Sonnet for anything involving code or technical content, Opus only when clearly necessary.",
1682
+ "When routing priority is best_quality: Opus for all code and reasoning tasks, Sonnet for writing and non-technical tasks.",
1683
+ "When routing priority is fastest_adequate: Haiku only for lightweight non-technical tasks, Sonnet otherwise.",
1684
+ "IMPORTANT: refactor, debug, fix, auth, module, CI, test, and TypeScript are all coding signals \u2014 always choose Sonnet or Opus for these, never Haiku."
1674
1685
  );
1675
1686
  }
1676
1687
  return lines.join("\n");
@@ -1706,6 +1717,39 @@ function isClaudeTiersOnlyTargetSet(targets) {
1706
1717
  (t) => t.provider === "anthropic" && /haiku|sonnet|opus/i.test(t.model)
1707
1718
  );
1708
1719
  }
1720
+ function isCodeSignal(input) {
1721
+ const task = (input.task ?? "").toLowerCase();
1722
+ const preset = (input.preset ?? "").toLowerCase();
1723
+ const hints = input.targetHints ?? [];
1724
+ return task === "code" || preset === "code" || hints.some((h) => ["coding", "agentic", "tool_use", "refactor", "debugging", "architecture"].includes(h)) || /\b(refactor|debug|fix|auth|module|ci|test|typescript|javascript|function|class|api|endpoint|build|deploy|lint|migration)\b/i.test(input.prompt);
1725
+ }
1726
+ function isArchitectureSignal(input) {
1727
+ const hints = input.targetHints ?? [];
1728
+ return hints.includes("architecture") || /\b(architect|architecture|design system|migration plan|multi.?step|long.?horizon|agentic.*plan|system design|microservice|monolith)\b/i.test(input.prompt);
1729
+ }
1730
+ function filterClaudeTierCandidates(targets, input, priority) {
1731
+ if (priority === "best_quality") {
1732
+ const filtered = targets.filter((t) => /opus|sonnet/i.test(t.model));
1733
+ return filtered.length > 0 ? filtered : targets;
1734
+ }
1735
+ if (priority === "cheapest_adequate") {
1736
+ if (isArchitectureSignal(input)) {
1737
+ const filtered2 = targets.filter((t) => /opus|sonnet/i.test(t.model));
1738
+ return filtered2.length > 0 ? filtered2 : targets;
1739
+ }
1740
+ if (isCodeSignal(input)) {
1741
+ const sonnet = targets.find((t) => /sonnet/i.test(t.model));
1742
+ return sonnet ? [sonnet] : targets.filter((t) => !/haiku/i.test(t.model));
1743
+ }
1744
+ const filtered = targets.filter((t) => /haiku|sonnet/i.test(t.model));
1745
+ return filtered.length > 0 ? filtered : targets;
1746
+ }
1747
+ if (priority === "fastest_adequate") {
1748
+ const filtered = targets.filter((t) => !/opus/i.test(t.model));
1749
+ return filtered.length > 0 ? filtered : targets;
1750
+ }
1751
+ return targets;
1752
+ }
1709
1753
  function selectClaudeTierHeuristic(input, priority, targets) {
1710
1754
  const haiku = targets.find((t) => /haiku/i.test(t.model)) ?? null;
1711
1755
  const sonnet = targets.find((t) => /sonnet/i.test(t.model)) ?? null;
@@ -1716,15 +1760,18 @@ function selectClaudeTierHeuristic(input, priority, targets) {
1716
1760
  const prompt = input.prompt;
1717
1761
  const isLightweight = ["email", "chat", "support", "summarization"].includes(task) || ["email", "chat", "support", "summarization"].includes(preset) || hints.some((h) => ["email", "support", "chat", "summarization"].includes(h));
1718
1762
  const needsOpus = /\b(architect|architecture|design system|migration plan|multi.?step|complex.*refactor|long.?horizon|agentic.*plan)\b/i.test(prompt) || hints.includes("architecture") || priority === "best_quality";
1763
+ const isCodeTask = ["code"].includes(task) || ["code"].includes(preset) || hints.some((h) => ["coding", "agentic", "tool_use", "refactor", "debugging", "architecture"].includes(h)) || /\b(refactor|debug|fix|auth|module|ci|test|typescript|javascript|function|class|api|endpoint)\b/i.test(prompt);
1719
1764
  if (priority === "fastest_adequate") {
1720
- return isLightweight || !needsOpus ? haiku ?? sonnet : sonnet ?? haiku;
1765
+ if (needsOpus) return opus ?? sonnet;
1766
+ if (isCodeTask) return sonnet ?? opus;
1767
+ return haiku ?? sonnet;
1721
1768
  }
1722
- if (needsOpus) {
1769
+ if (priority === "best_quality") {
1723
1770
  return opus ?? sonnet ?? haiku;
1724
1771
  }
1725
- if (isLightweight && priority === "cheapest_adequate") {
1726
- return haiku ?? sonnet;
1727
- }
1772
+ if (needsOpus) return opus ?? sonnet;
1773
+ if (isCodeTask) return sonnet ?? opus;
1774
+ if (isLightweight) return haiku ?? sonnet;
1728
1775
  return sonnet ?? haiku ?? opus;
1729
1776
  }
1730
1777
  function inferCapabilities(target) {