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/README.md +147 -285
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +109 -33
- package/dist/cli.js.map +1 -1
- package/dist/index.js +61 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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:
|
|
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 =
|
|
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 &&
|
|
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.
|
|
1669
|
-
"Sonnet: balanced cost and capability.
|
|
1670
|
-
"Opus: most capable and most expensive.
|
|
1671
|
-
"When routing priority is cheapest_adequate:
|
|
1672
|
-
"When routing priority is best_quality:
|
|
1673
|
-
"When routing priority is fastest_adequate:
|
|
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
|
-
|
|
1765
|
+
if (needsOpus) return opus ?? sonnet;
|
|
1766
|
+
if (isCodeTask) return sonnet ?? opus;
|
|
1767
|
+
return haiku ?? sonnet;
|
|
1721
1768
|
}
|
|
1722
|
-
if (
|
|
1769
|
+
if (priority === "best_quality") {
|
|
1723
1770
|
return opus ?? sonnet ?? haiku;
|
|
1724
1771
|
}
|
|
1725
|
-
if (
|
|
1726
|
-
|
|
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) {
|