llm-switcher 1.1.11 → 1.2.0

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +202 -257
  3. package/README.vi.md +200 -256
  4. package/blindfold/blindfold.mjs +200 -53
  5. package/blindfold/make-certs.sh +26 -7
  6. package/catalog.mjs +246 -0
  7. package/config.example.json +12 -34
  8. package/docs/TOKEN-OPTIMIZER-INTEROP.md +110 -110
  9. package/docs/codex-blindfold.md +28 -17
  10. package/docs/cross-platform.md +16 -7
  11. package/docs/diagrams/ir-healer-pipeline.mmd +16 -0
  12. package/docs/diagrams/ir-healer-pipeline.png +0 -0
  13. package/docs/diagrams/ir-healer-pipeline.svg +90 -0
  14. package/docs/diagrams/ir-translation-pipeline.html +14925 -0
  15. package/docs/diagrams/ir-translation-pipeline.sequence.json +31 -0
  16. package/docs/diagrams/ir-translation-pipeline.svg +5128 -0
  17. package/docs/diagrams/system-architecture.architecture.json +76 -0
  18. package/docs/diagrams/system-architecture.html +14978 -0
  19. package/docs/diagrams/system-architecture.svg +5147 -0
  20. package/docs/diagrams/system-topology.mmd +30 -0
  21. package/docs/diagrams/system-topology.png +0 -0
  22. package/docs/diagrams/system-topology.svg +125 -0
  23. package/docs/response-matrix.json +1130 -1130
  24. package/ensure-ca-bundle.mjs +28 -0
  25. package/formats.mjs +13 -155
  26. package/mcp.mjs +39 -11
  27. package/package.json +1 -1
  28. package/proxy.mjs +92 -27
  29. package/shim.mjs +200 -57
  30. package/skills/llm-switcher/SKILL.md +93 -88
  31. package/state.mjs +1100 -191
  32. package/switch +0 -0
  33. package/switch.cmd +2 -2
  34. package/switch.mjs +228 -53
  35. package/tests/blindfold-e2e.test.mjs +380 -0
  36. package/tests/blindfold-task5.test.mjs +429 -0
  37. package/tests/blindfold.task3.test.mjs +700 -0
  38. package/tests/blindfold.test.mjs +10 -5
  39. package/tests/catalog.test.mjs +147 -0
  40. package/tests/contract-lab.test.mjs +22 -7
  41. package/tests/formats.test.mjs +33 -46
  42. package/tests/gateway.e2e.test.mjs +136 -36
  43. package/tests/helpers.mjs +24 -24
  44. package/tests/lifecycle.test.mjs +16 -10
  45. package/tests/live-optimizer-interop.mjs +205 -205
  46. package/tests/mcp.test.mjs +78 -2
  47. package/tests/real-user-sim.test.mjs +464 -0
  48. package/tests/shim.test.mjs +159 -66
  49. package/tests/state.test.mjs +975 -193
  50. package/tests/switch.test.mjs +446 -2
  51. package/ui.html +61 -154
@@ -0,0 +1,30 @@
1
+ flowchart LR
2
+ subgraph Clients[" Dev Clients & Coding CLIs "]
3
+ CC["Claude Code CLI\n(/v1/messages)"]
4
+ CDX["OpenAI Codex CLI\n(/v1/responses)"]
5
+ end
6
+
7
+ subgraph Optimizers[" Optional Middle-Layer "]
8
+ OPT["Token Optimizers\n(Headroom / RTK)"]
9
+ end
10
+
11
+ subgraph Gateway[" LLM Switcher Edge Gateway (:3456) "]
12
+ ROUTER["Edge Router\n(Zero-Mutation)"]
13
+ HEALER["Healer Engine\n(Auto-Fix Schemas)"]
14
+ IR["Bi-Directional IR\n(Event Stream Synth)"]
15
+ ROUTER --> HEALER --> IR
16
+ end
17
+
18
+ subgraph Upstreams[" Upstream Providers "]
19
+ INTACT["intact Gateway\n(Recommended Pooler)"]
20
+ OTHER["9Router / Vertex / Other"]
21
+ end
22
+
23
+ CC -->|direct /v1/messages| ROUTER
24
+ CDX -->|direct /v1/responses| ROUTER
25
+ CC -.->|prune prompt| OPT
26
+ CDX -.->|prune prompt| OPT
27
+ OPT -->|forward :3456| ROUTER
28
+
29
+ IR -->|contract & pool| INTACT
30
+ IR -->|standard call| OTHER
@@ -0,0 +1,125 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1712.757 298.51666666666665" width="1712.757" height="298.51666666666665" style="--bg:#1a1b26;--fg:#a9b1d6;--line:#3d59a1;--accent:#7aa2f7;--muted:#565f89;background:var(--bg)">
2
+ <style>
3
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;display=swap');
4
+ text { font-family: 'Inter', system-ui, sans-serif; }
5
+ svg {
6
+ /* Derived from --bg and --fg (overridable via --line, --accent, etc.) */
7
+ --_text: var(--fg);
8
+ --_text-sec: var(--muted, color-mix(in srgb, var(--fg) 60%, var(--bg)));
9
+ --_text-muted: var(--muted, color-mix(in srgb, var(--fg) 40%, var(--bg)));
10
+ --_text-faint: color-mix(in srgb, var(--fg) 25%, var(--bg));
11
+ --_line: var(--line, color-mix(in srgb, var(--fg) 50%, var(--bg)));
12
+ --_arrow: var(--accent, color-mix(in srgb, var(--fg) 85%, var(--bg)));
13
+ --_node-fill: var(--surface, color-mix(in srgb, var(--fg) 3%, var(--bg)));
14
+ --_node-stroke: var(--border, color-mix(in srgb, var(--fg) 20%, var(--bg)));
15
+ --_group-fill: var(--bg);
16
+ --_group-hdr: color-mix(in srgb, var(--fg) 5%, var(--bg));
17
+ --_inner-stroke: color-mix(in srgb, var(--fg) 12%, var(--bg));
18
+ --_key-badge: color-mix(in srgb, var(--fg) 10%, var(--bg));
19
+ }
20
+ </style>
21
+ <defs>
22
+ <marker id="arrowhead" markerWidth="8" markerHeight="5" refX="7" refY="2.5" orient="auto">
23
+ <polygon points="0 0, 8 2.5, 0 5" fill="var(--_arrow)" stroke="var(--_arrow)" stroke-width="0.75" stroke-linejoin="round" />
24
+ </marker>
25
+ <marker id="arrowhead-start" markerWidth="8" markerHeight="5" refX="1" refY="2.5" orient="auto-start-reverse">
26
+ <polygon points="8 0, 0 2.5, 8 5" fill="var(--_arrow)" stroke="var(--_arrow)" stroke-width="0.75" stroke-linejoin="round" />
27
+ </marker>
28
+ </defs>
29
+ <g class="subgraph" data-id="Clients" data-label=" Dev Clients &amp; Coding CLIs ">
30
+ <rect x="40" y="40" width="180.654" height="191.60000000000002" rx="0" ry="0" fill="var(--_group-fill)" stroke="var(--_node-stroke)" stroke-width="1" />
31
+ <rect x="40" y="40" width="180.654" height="28" rx="0" ry="0" fill="var(--_group-hdr)" stroke="var(--_node-stroke)" stroke-width="1" />
32
+ <text x="52" y="54" font-size="12" font-weight="600" fill="var(--_text-sec)" dy="4.199999999999999"> Dev Clients &amp; Coding CLIs </text>
33
+ </g>
34
+ <g class="subgraph" data-id="Optimizers" data-label=" Optional Middle-Layer ">
35
+ <rect x="384.674" y="90.56666666666666" width="176.949" height="113.80000000000001" rx="0" ry="0" fill="var(--_group-fill)" stroke="var(--_node-stroke)" stroke-width="1" />
36
+ <rect x="384.674" y="90.56666666666666" width="176.949" height="28" rx="0" ry="0" fill="var(--_group-hdr)" stroke="var(--_node-stroke)" stroke-width="1" />
37
+ <text x="396.674" y="104.56666666666666" font-size="12" font-weight="600" fill="var(--_text-sec)" dy="4.199999999999999"> Optional Middle-Layer </text>
38
+ </g>
39
+ <g class="subgraph" data-id="Gateway" data-label=" LLM Switcher Edge Gateway (:3456) ">
40
+ <rect x="728.0190000000001" y="92.55000000000001" width="563.89" height="113.80000000000001" rx="0" ry="0" fill="var(--_group-fill)" stroke="var(--_node-stroke)" stroke-width="1" />
41
+ <rect x="728.0190000000001" y="92.55000000000001" width="563.89" height="28" rx="0" ry="0" fill="var(--_group-hdr)" stroke="var(--_node-stroke)" stroke-width="1" />
42
+ <text x="740.0190000000001" y="106.55000000000001" font-size="12" font-weight="600" fill="var(--_text-sec)" dy="4.199999999999999"> LLM Switcher Edge Gateway (:3456) </text>
43
+ </g>
44
+ <g class="subgraph" data-id="Upstreams" data-label=" Upstream Providers ">
45
+ <rect x="1462.4630000000002" y="48.45" width="210.29399999999998" height="174.70000000000002" rx="0" ry="0" fill="var(--_group-fill)" stroke="var(--_node-stroke)" stroke-width="1" />
46
+ <rect x="1462.4630000000002" y="48.45" width="210.29399999999998" height="28" rx="0" ry="0" fill="var(--_group-hdr)" stroke="var(--_node-stroke)" stroke-width="1" />
47
+ <text x="1474.4630000000002" y="62.45" font-size="12" font-weight="600" fill="var(--_text-sec)" dy="4.199999999999999"> Upstream Providers </text>
48
+ </g>
49
+ <polyline class="edge" data-from="CC" data-to="ROUTER" data-style="solid" data-arrow-start="false" data-arrow-end="true" data-label="direct /v1/messages" points="200.208,101.93333333333334 366.674,101.93333333333334 366.674,56.26666666666667 710.0190000000001,56.26666666666667 710.0190000000001,150 744.0190000000001,150" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
50
+ <polyline class="edge" data-from="CDX" data-to="ROUTER" data-style="solid" data-arrow-start="false" data-arrow-end="true" data-label="direct /v1/responses" points="209.1,197.66666666666669 366.674,197.66666666666669 366.674,239.36666666666667 710.0190000000001,239.36666666666667 710.0190000000001,176.9 744.0190000000001,176.9" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
51
+ <polyline class="edge" data-from="CC" data-to="OPT" data-style="dotted" data-arrow-start="false" data-arrow-end="true" data-label="prune prompt" points="200.208,119.86666666666667 254.654,119.86666666666667 254.654,144.81666666666666 366.674,144.81666666666666 366.674,152.5 400.674,152.5" fill="none" stroke="var(--_line)" stroke-width="1" stroke-dasharray="4 4" marker-end="url(#arrowhead)" />
52
+ <polyline class="edge" data-from="CDX" data-to="OPT" data-style="dotted" data-arrow-start="false" data-arrow-end="true" data-label="prune prompt" points="209.1,179.73333333333335 254.654,179.73333333333335 254.654,178.11666666666667 366.674,178.11666666666667 366.674,170.43333333333334 400.674,170.43333333333334" fill="none" stroke="var(--_line)" stroke-width="1" stroke-dasharray="4 4" marker-end="url(#arrowhead)" />
53
+ <polyline class="edge" data-from="OPT" data-to="ROUTER" data-style="solid" data-arrow-start="false" data-arrow-end="true" data-label="forward :3456" points="545.623,161.46666666666667 710.019,161.4666666666667 710.019,163.45000000000002 744.0190000000001,163.45000000000002" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
54
+ <polyline class="edge" data-from="IR" data-to="INTACT" data-style="solid" data-arrow-start="false" data-arrow-end="true" data-label="contract &amp; pool" points="1275.909,172.41666666666669 1325.909,172.41666666666669 1325.909,180.10000000000002 1444.4630000000002,180.10000000000002 1444.4630000000002,180.25 1478.4630000000002,180.25000000000003" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
55
+ <polyline class="edge" data-from="IR" data-to="OTHER" data-style="solid" data-arrow-start="false" data-arrow-end="true" data-label="standard call" points="1275.909,154.48333333333335 1333.631,154.48333333333335 1333.631,128.925 1444.4630000000002,128.925 1444.4630000000002,110.9 1478.4630000000002,110.90000000000002" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
56
+ <polyline class="edge" data-from="ROUTER" data-to="HEALER" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="876.3710000000001,163.45000000000002 916.3710000000001,163.45000000000002" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
57
+ <polyline class="edge" data-from="HEALER" data-to="IR" data-style="solid" data-arrow-start="false" data-arrow-end="true" points="1072.4350000000002,163.45000000000002 1112.4350000000002,163.45000000000002" fill="none" stroke="var(--_line)" stroke-width="1" marker-end="url(#arrowhead)" />
58
+ <g class="edge-label" data-from="CC" data-to="ROUTER" data-label="direct /v1/messages">
59
+ <rect x="592.2990000000001" y="40.26666666666667" width="109.72000000000001" height="30.3" rx="2" ry="2" fill="var(--bg)" stroke="var(--_inner-stroke)" stroke-width="1" />
60
+ <text x="647.1590000000001" y="55.41666666666667" text-anchor="middle" font-size="11" font-weight="400" fill="var(--_text-sec)" dy="3.8499999999999996">direct /v1/messages</text>
61
+ </g>
62
+ <g class="edge-label" data-from="CDX" data-to="ROUTER" data-label="direct /v1/responses">
63
+ <rect x="588.7350000000001" y="223.36666666666667" width="113.28400000000002" height="30.3" rx="2" ry="2" fill="var(--bg)" stroke="var(--_inner-stroke)" stroke-width="1" />
64
+ <text x="645.3770000000002" y="238.51666666666668" text-anchor="middle" font-size="11" font-weight="400" fill="var(--_text-sec)" dy="3.8499999999999996">direct /v1/responses</text>
65
+ </g>
66
+ <g class="edge-label" data-from="CC" data-to="OPT" data-label="prune prompt">
67
+ <rect x="262.654" y="128.81666666666666" width="80.02000000000001" height="30.3" rx="2" ry="2" fill="var(--bg)" stroke="var(--_inner-stroke)" stroke-width="1" />
68
+ <text x="302.664" y="143.96666666666667" text-anchor="middle" font-size="11" font-weight="400" fill="var(--_text-sec)" dy="3.8499999999999996">prune prompt</text>
69
+ </g>
70
+ <g class="edge-label" data-from="CDX" data-to="OPT" data-label="prune prompt">
71
+ <rect x="262.654" y="162.11666666666667" width="80.02000000000001" height="30.3" rx="2" ry="2" fill="var(--bg)" stroke="var(--_inner-stroke)" stroke-width="1" />
72
+ <text x="302.664" y="177.26666666666668" text-anchor="middle" font-size="11" font-weight="400" fill="var(--_text-sec)" dy="3.8499999999999996">prune prompt</text>
73
+ </g>
74
+ <g class="edge-label" data-from="OPT" data-to="ROUTER" data-label="forward :3456">
75
+ <rect x="603.623" y="145.46666666666667" width="82.39600000000002" height="30.3" rx="2" ry="2" fill="var(--bg)" stroke="var(--_inner-stroke)" stroke-width="1" />
76
+ <text x="644.821" y="160.61666666666667" text-anchor="middle" font-size="11" font-weight="400" fill="var(--_text-sec)" dy="3.8499999999999996">forward :3456</text>
77
+ </g>
78
+ <g class="edge-label" data-from="IR" data-to="INTACT" data-label="contract &amp; pool">
79
+ <rect x="1333.909" y="164.10000000000002" width="86.55400000000002" height="30.3" rx="2" ry="2" fill="var(--bg)" stroke="var(--_inner-stroke)" stroke-width="1" />
80
+ <text x="1377.1860000000001" y="179.25000000000003" text-anchor="middle" font-size="11" font-weight="400" fill="var(--_text-sec)" dy="3.8499999999999996">contract &amp; pool</text>
81
+ </g>
82
+ <g class="edge-label" data-from="IR" data-to="OTHER" data-label="standard call">
83
+ <rect x="1341.631" y="112.92500000000001" width="78.83200000000001" height="30.3" rx="2" ry="2" fill="var(--bg)" stroke="var(--_inner-stroke)" stroke-width="1" />
84
+ <text x="1381.047" y="128.07500000000002" text-anchor="middle" font-size="11" font-weight="400" fill="var(--_text-sec)" dy="3.8499999999999996">standard call</text>
85
+ </g>
86
+ <g class="node" data-id="CC" data-label="Claude Code CLI
87
+ (/v1/messages)" data-shape="rectangle">
88
+ <rect x="60.446" y="84" width="139.762" height="53.800000000000004" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
89
+ <text x="130.327" y="110.9" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)"><tspan x="130.327" dy="-3.9000000000000012">Claude Code CLI</tspan><tspan x="130.327" dy="16.900000000000002">(/v1/messages)</tspan></text>
90
+ </g>
91
+ <g class="node" data-id="CDX" data-label="OpenAI Codex CLI
92
+ (/v1/responses)" data-shape="rectangle">
93
+ <rect x="60.446" y="161.8" width="148.654" height="53.800000000000004" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
94
+ <text x="134.773" y="188.70000000000002" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)"><tspan x="134.773" dy="-3.9000000000000012">OpenAI Codex CLI</tspan><tspan x="134.773" dy="16.900000000000002">(/v1/responses)</tspan></text>
95
+ </g>
96
+ <g class="node" data-id="OPT" data-label="Token Optimizers
97
+ (Headroom / RTK)" data-shape="rectangle">
98
+ <rect x="400.674" y="134.56666666666666" width="144.949" height="53.800000000000004" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
99
+ <text x="473.1485" y="161.46666666666667" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)"><tspan x="473.1485" dy="-3.9000000000000012">Token Optimizers</tspan><tspan x="473.1485" dy="16.900000000000002">(Headroom / RTK)</tspan></text>
100
+ </g>
101
+ <g class="node" data-id="ROUTER" data-label="Edge Router
102
+ (Zero-Mutation)" data-shape="rectangle">
103
+ <rect x="744.0190000000001" y="136.55" width="132.352" height="53.800000000000004" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
104
+ <text x="810.1950000000002" y="163.45000000000002" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)"><tspan x="810.1950000000002" dy="-3.9000000000000012">Edge Router</tspan><tspan x="810.1950000000002" dy="16.900000000000002">(Zero-Mutation)</tspan></text>
105
+ </g>
106
+ <g class="node" data-id="HEALER" data-label="Healer Engine
107
+ (Auto-Fix Schemas)" data-shape="rectangle">
108
+ <rect x="916.3710000000001" y="136.55" width="156.064" height="53.800000000000004" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
109
+ <text x="994.4030000000001" y="163.45000000000002" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)"><tspan x="994.4030000000001" dy="-3.9000000000000012">Healer Engine</tspan><tspan x="994.4030000000001" dy="16.900000000000002">(Auto-Fix Schemas)</tspan></text>
110
+ </g>
111
+ <g class="node" data-id="IR" data-label="Bi-Directional IR
112
+ (Event Stream Synth)" data-shape="rectangle">
113
+ <rect x="1112.4350000000002" y="136.55" width="163.474" height="53.800000000000004" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
114
+ <text x="1194.1720000000003" y="163.45000000000002" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)"><tspan x="1194.1720000000003" dy="-3.9000000000000012">Bi-Directional IR</tspan><tspan x="1194.1720000000003" dy="16.900000000000002">(Event Stream Synth)</tspan></text>
115
+ </g>
116
+ <g class="node" data-id="INTACT" data-label="intact Gateway
117
+ (Recommended Pooler)" data-shape="rectangle">
118
+ <rect x="1478.4630000000002" y="153.35000000000002" width="177.55299999999997" height="53.800000000000004" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
119
+ <text x="1567.2395000000001" y="180.25000000000003" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)"><tspan x="1567.2395000000001" dy="-3.9000000000000012">intact Gateway</tspan><tspan x="1567.2395000000001" dy="16.900000000000002">(Recommended Pooler)</tspan></text>
120
+ </g>
121
+ <g class="node" data-id="OTHER" data-label="9Router / Vertex / Other" data-shape="rectangle">
122
+ <rect x="1478.4630000000002" y="92.45" width="178.29399999999998" height="36.900000000000006" rx="0" ry="0" fill="var(--_node-fill)" stroke="var(--_node-stroke)" stroke-width="0.75" />
123
+ <text x="1567.6100000000001" y="110.9" text-anchor="middle" font-size="13" font-weight="500" fill="var(--_text)" dy="4.55">9Router / Vertex / Other</text>
124
+ </g>
125
+ </svg>