@raingor/pi-web-switch 0.4.2 → 0.4.4

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 (37) hide show
  1. package/dist/index.html +18 -0
  2. package/index.html +2 -2
  3. package/package.json +7 -38
  4. package/public/apple-touch-icon.png +0 -0
  5. package/public/icon-192.png +0 -0
  6. package/public/icon-512.png +0 -0
  7. package/public/manifest.webmanifest +2 -2
  8. package/public/pi.svg +41 -6
  9. package/public/sw.js +51 -51
  10. package/server/pi-reader.ts +353 -237
  11. package/src/App.tsx +2 -0
  12. package/src/components/dashboard/DashboardPage.tsx +341 -56
  13. package/src/components/layout/AppShell.tsx +45 -13
  14. package/src/components/layout/Sidebar.tsx +78 -85
  15. package/src/components/providers/ProvidersModelsPage.tsx +125 -131
  16. package/src/components/sessions/MemoryPage.tsx +34 -13
  17. package/src/components/sessions/SessionsPage.tsx +20 -40
  18. package/src/components/settings/SettingsPage.tsx +6 -1
  19. package/src/components/speedtest/ModelSpeedTestPage.tsx +429 -0
  20. package/src/components/ui/EmptyState.tsx +7 -6
  21. package/src/components/ui/Modal.tsx +33 -25
  22. package/src/components/ui/StatCard.tsx +10 -13
  23. package/src/data/builtin-providers.test.ts +109 -0
  24. package/src/data/builtin-providers.ts +67 -44
  25. package/src/data/model-catalog.test.ts +122 -0
  26. package/src/data/model-catalog.ts +697 -478
  27. package/src/index.css +624 -210
  28. package/src/lib/translations/en.ts +88 -14
  29. package/src/lib/translations/ja.ts +88 -14
  30. package/src/lib/translations/zh-CN.ts +88 -14
  31. package/src/lib/translations/zh-TW.ts +87 -14
  32. package/src/main.tsx +97 -44
  33. package/src/types/index.ts +0 -1
  34. package/vite.config.ts +65 -164
  35. package/dist-electron/main/main.cjs +0 -2453
  36. package/src/data/mock-config.ts +0 -247
  37. package/src/data/mock-usage.ts +0 -151
@@ -1,247 +0,0 @@
1
- import type { PiConfig, Provider, Model, PiSettings, PiAuth, PiModelsJson } from "@/types";
2
-
3
- // ─── Built-in Providers ───────────────────────────────────
4
-
5
- const BUILTIN_PROVIDERS: Provider[] = [
6
- {
7
- id: "anthropic",
8
- name: "Anthropic",
9
- type: "builtin",
10
- api: "anthropic-messages",
11
- hasAuth: true,
12
- authMethod: "env",
13
- models: [
14
- { id: "claude-sonnet-4", name: "Claude 4 Sonnet", reasoning: true, input: ["text", "image"], contextWindow: 200000, maxTokens: 8192, cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }, enabled: true },
15
- { id: "claude-sonnet-4-5", name: "Claude 4.5 Sonnet", reasoning: true, input: ["text", "image"], contextWindow: 200000, maxTokens: 8192, cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }, enabled: true },
16
- { id: "claude-opus-4", name: "Claude 4 Opus", reasoning: true, input: ["text", "image"], contextWindow: 200000, maxTokens: 8192, cost: { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 }, enabled: false },
17
- { id: "claude-haiku-3-5", name: "Claude 3.5 Haiku", reasoning: false, input: ["text", "image"], contextWindow: 200000, maxTokens: 8192, cost: { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 }, enabled: true },
18
- ],
19
- },
20
- {
21
- id: "openai",
22
- name: "OpenAI",
23
- type: "builtin",
24
- api: "openai-completions",
25
- hasAuth: true,
26
- authMethod: "env",
27
- models: [
28
- { id: "gpt-4o", name: "GPT-4o", reasoning: false, input: ["text", "image"], contextWindow: 128000, maxTokens: 16384, cost: { input: 2.5, output: 10, cacheRead: 1.25, cacheWrite: 3.75 }, enabled: true },
29
- { id: "gpt-4o-mini", name: "GPT-4o Mini", reasoning: false, input: ["text", "image"], contextWindow: 128000, maxTokens: 16384, cost: { input: 0.15, output: 0.6, cacheRead: 0.075, cacheWrite: 0.225 }, enabled: true },
30
- { id: "gpt-5.1", name: "GPT-5.1", reasoning: true, input: ["text", "image"], contextWindow: 256000, maxTokens: 65536, cost: { input: 10, output: 40, cacheRead: 5, cacheWrite: 10 }, enabled: false },
31
- { id: "o3-mini", name: "o3-mini", reasoning: true, input: ["text"], contextWindow: 200000, maxTokens: 100000, cost: { input: 1.1, output: 4.4, cacheRead: 0.55, cacheWrite: 1.65 }, enabled: false },
32
- ],
33
- },
34
- {
35
- id: "deepseek",
36
- name: "DeepSeek",
37
- type: "builtin",
38
- api: "openai-completions",
39
- hasAuth: true,
40
- authMethod: "env",
41
- models: [
42
- { id: "deepseek-chat", name: "DeepSeek V3", reasoning: false, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 0.27, output: 1.1, cacheRead: 0.07, cacheWrite: 0.27 }, enabled: true },
43
- { id: "deepseek-reasoner", name: "DeepSeek R1", reasoning: true, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 0.55, output: 2.19, cacheRead: 0.14, cacheWrite: 0.55 }, enabled: true },
44
- ],
45
- },
46
- {
47
- id: "opencode",
48
- name: "OpenCode",
49
- type: "builtin",
50
- api: "openai-completions",
51
- hasAuth: true,
52
- authMethod: "file",
53
- models: [
54
- { id: "deepseek-v4-flash-free", name: "DeepSeek V4 Flash (Free)", reasoning: false, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, enabled: true },
55
- { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: false, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 0.3, output: 0.6, cacheRead: 0.15, cacheWrite: 0.3 }, enabled: true },
56
- ],
57
- },
58
- {
59
- id: "opencode-go",
60
- name: "OpenCode Go",
61
- type: "builtin",
62
- api: "openai-completions",
63
- hasAuth: true,
64
- authMethod: "file",
65
- models: [
66
- { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: false, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 0.3, output: 0.6, cacheRead: 0.15, cacheWrite: 0.3 }, enabled: true },
67
- { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 2, output: 8, cacheRead: 1, cacheWrite: 2 }, enabled: true },
68
- { id: "glm-5.1", name: "GLM 5.1", reasoning: false, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 0.5, output: 2, cacheRead: 0.25, cacheWrite: 0.5 }, enabled: true },
69
- { id: "qwen3.7-max", name: "Qwen 3.7 Max", reasoning: true, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 1.5, output: 6, cacheRead: 0.75, cacheWrite: 1.5 }, enabled: true },
70
- { id: "mimo-v2.5", name: "MiMo V2.5", reasoning: true, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 1.2, output: 4.8, cacheRead: 0.6, cacheWrite: 1.2 }, enabled: true },
71
- ],
72
- },
73
- {
74
- id: "google",
75
- name: "Google Gemini",
76
- type: "builtin",
77
- api: "google-generative-ai",
78
- hasAuth: true,
79
- authMethod: "env",
80
- models: [
81
- { id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", reasoning: false, input: ["text", "image"], contextWindow: 1048576, maxTokens: 8192, cost: { input: 0.15, output: 0.6, cacheRead: 0.075, cacheWrite: 0.15 }, enabled: true },
82
- { id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", reasoning: true, input: ["text", "image"], contextWindow: 1048576, maxTokens: 8192, cost: { input: 1.25, output: 10, cacheRead: 0.625, cacheWrite: 1.25 }, enabled: false },
83
- ],
84
- },
85
- {
86
- id: "openrouter",
87
- name: "OpenRouter",
88
- type: "builtin",
89
- api: "openai-completions",
90
- hasAuth: false,
91
- authMethod: "none",
92
- models: [
93
- { id: "openrouter/anthropic/claude-sonnet-4", name: "Claude 4 Sonnet (OpenRouter)", reasoning: true, input: ["text", "image"], contextWindow: 200000, maxTokens: 8192, cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }, enabled: false },
94
- { id: "openrouter/deepseek/deepseek-r1", name: "DeepSeek R1 (OpenRouter)", reasoning: true, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 0.55, output: 2.19, cacheRead: 0.14, cacheWrite: 0.55 }, enabled: false },
95
- ],
96
- },
97
- {
98
- id: "mistral",
99
- name: "Mistral",
100
- type: "builtin",
101
- api: "mistral-conversations",
102
- hasAuth: false,
103
- authMethod: "none",
104
- models: [
105
- { id: "mistral-large", name: "Mistral Large", reasoning: false, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 2, output: 6, cacheRead: 1, cacheWrite: 2 }, enabled: false },
106
- ],
107
- },
108
- {
109
- id: "github-copilot",
110
- name: "GitHub Copilot",
111
- type: "builtin",
112
- api: "openai-completions",
113
- hasAuth: false,
114
- authMethod: "none",
115
- models: [
116
- { id: "copilot-gpt-4o", name: "Copilot GPT-4o", reasoning: false, input: ["text"], contextWindow: 128000, maxTokens: 4096, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, enabled: false },
117
- ],
118
- },
119
- {
120
- id: "groq",
121
- name: "Groq",
122
- type: "builtin",
123
- api: "openai-completions",
124
- hasAuth: false,
125
- authMethod: "none",
126
- models: [
127
- { id: "llama-3.3-70b", name: "Llama 3.3 70B", reasoning: false, input: ["text"], contextWindow: 128000, maxTokens: 8192, cost: { input: 0.59, output: 0.79, cacheRead: 0, cacheWrite: 0 }, enabled: false },
128
- ],
129
- },
130
- ];
131
-
132
- // ─── Default Settings ─────────────────────────────────────
133
-
134
- export const DEFAULT_SETTINGS: PiSettings = {
135
- lastChangelogVersion: "0.80.3",
136
- defaultProvider: "opencode-go",
137
- defaultModel: "deepseek-v4-flash",
138
- defaultThinkingLevel: "high",
139
- defaultProjectTrust: "always",
140
- theme: "light/dark",
141
- hideThinkingBlock: true,
142
- retry: { enabled: true },
143
- packages: [
144
- "npm:pi-hermes-memory",
145
- "npm:@pi-unipi/notify",
146
- "npm:context-mode",
147
- "npm:pi-subagents",
148
- "npm:pi-web-access",
149
- "npm:pi-rtk-optimizer",
150
- "npm:pi-puppeteer",
151
- "npm:pi-intercom",
152
- "npm:pi-prompt-template-model",
153
- ],
154
- terminal: { showTerminalProgress: true },
155
- warnings: { anthropicExtraUsage: true },
156
- treeFilterMode: "default",
157
- doubleEscapeAction: "tree",
158
- enabledModels: [
159
- "opencode-go/deepseek-v4-flash",
160
- "opencode-go/deepseek-v4-pro",
161
- "opencode/deepseek-v4-flash-free",
162
- "opencode-go/glm-5.1",
163
- "opencode-go/qwen3.7-max",
164
- "opencode-go/mimo-v2.5",
165
- ],
166
- };
167
-
168
- // ─── Default Auth ─────────────────────────────────────────
169
-
170
- export const DEFAULT_AUTH: PiAuth = {
171
- opencode: {
172
- type: "api_key",
173
- key: "sk-hqW3wtminBBkiTbjR57V15MClbOoZutz1StHXtv64HU0tiICeRDiA3DfC9vb0NGW",
174
- },
175
- "opencode-go": {
176
- type: "api_key",
177
- key: "sk-hqW3wtminBBkiTbjR57V15MClbOoZutz1StHXtv64HU0tiICeRDiA3DfC9vb0NGW",
178
- },
179
- };
180
-
181
- // ─── Default models.json (empty — no custom providers yet) ─
182
-
183
- export const DEFAULT_MODELS_JSON: PiModelsJson = {
184
- providers: {},
185
- };
186
-
187
- // ─── Full Config ──────────────────────────────────────────
188
-
189
- export const DEFAULT_PI_CONFIG: PiConfig = {
190
- settings: DEFAULT_SETTINGS,
191
- auth: DEFAULT_AUTH,
192
- modelsJson: DEFAULT_MODELS_JSON,
193
- };
194
-
195
- // ─── Helper: get builtin providers ────────────────────────
196
-
197
- export function getBuiltinProviders(): Provider[] {
198
- return BUILTIN_PROVIDERS;
199
- }
200
-
201
- // ─── Helper: merge custom providers from models.json ──────
202
-
203
- export function getCustomProviders(modelsJson: PiModelsJson | null): Provider[] {
204
- if (!modelsJson) return [];
205
- return Object.entries(modelsJson.providers).map(([id, cfg]) => ({
206
- id,
207
- name: id.charAt(0).toUpperCase() + id.slice(1),
208
- type: "custom" as const,
209
- baseUrl: cfg.baseUrl,
210
- api: cfg.api,
211
- apiKey: cfg.apiKey,
212
- authHeader: cfg.authHeader,
213
- headers: cfg.headers,
214
- compat: cfg.compat,
215
- hasAuth: !!cfg.apiKey,
216
- authMethod: (cfg.apiKey ? "file" : "none") as "file" | "none",
217
- models: (cfg.models ?? []).map((m) => ({ ...m, enabled: true })),
218
- }));
219
- }
220
-
221
- // ─── Helper: all providers merged ─────────────────────────
222
-
223
- export function getAllProviders(config: PiConfig): Provider[] {
224
- const builtins = getBuiltinProviders().map((p) => {
225
- // Apply auth from settings
226
- const authEntry = config.auth[p.id];
227
- return {
228
- ...p,
229
- hasAuth: !!authEntry || p.hasAuth,
230
- authMethod: authEntry ? "file" : p.authMethod,
231
- };
232
- });
233
- const customs = getCustomProviders(config.modelsJson);
234
- return [...builtins, ...customs];
235
- }
236
-
237
- // ─── Helper: all models flat ──────────────────────────────
238
-
239
- export function getAllModels(config: PiConfig): (Model & { providerId: string; providerName: string })[] {
240
- return getAllProviders(config).flatMap((p) =>
241
- p.models.map((m) => ({
242
- ...m,
243
- providerId: p.id,
244
- providerName: p.name,
245
- }))
246
- );
247
- }
@@ -1,151 +0,0 @@
1
- import type { UsageRecord } from "@/types";
2
-
3
- // Generate 30 days of mock usage data
4
- function generateMockUsage(): UsageRecord[] {
5
- const records: UsageRecord[] = [];
6
- const today = new Date();
7
- const modelProviderPairs = [
8
- { providerId: "opencode-go", modelId: "deepseek-v4-flash", baseTokens: 1_200_000, baseCost: 0.36 },
9
- { providerId: "opencode-go", modelId: "deepseek-v4-pro", baseTokens: 400_000, baseCost: 0.80 },
10
- { providerId: "opencode-go", modelId: "glm-5.1", baseTokens: 250_000, baseCost: 0.125 },
11
- { providerId: "opencode-go", modelId: "qwen3.7-max", baseTokens: 180_000, baseCost: 0.27 },
12
- { providerId: "opencode-go", modelId: "mimo-v2.5", baseTokens: 300_000, baseCost: 0.36 },
13
- { providerId: "opencode", modelId: "deepseek-v4-flash-free", baseTokens: 800_000, baseCost: 0 },
14
- { providerId: "opencode", modelId: "deepseek-v4-flash", baseTokens: 200_000, baseCost: 0.06 },
15
- { providerId: "anthropic", modelId: "claude-sonnet-4", baseTokens: 150_000, baseCost: 0.45 },
16
- { providerId: "anthropic", modelId: "claude-haiku-3-5", baseTokens: 90_000, baseCost: 0.036 },
17
- { providerId: "openai", modelId: "gpt-4o", baseTokens: 80_000, baseCost: 0.20 },
18
- { providerId: "openai", modelId: "gpt-4o-mini", baseTokens: 120_000, baseCost: 0.018 },
19
- { providerId: "deepseek", modelId: "deepseek-chat", baseTokens: 60_000, baseCost: 0.0162 },
20
- { providerId: "google", modelId: "gemini-2.5-flash", baseTokens: 40_000, baseCost: 0.006 },
21
- ];
22
-
23
- for (let day = 29; day >= 0; day--) {
24
- const date = new Date(today);
25
- date.setDate(date.getDate() - day);
26
- const dateStr = date.toISOString().split("T")[0]!;
27
-
28
- for (const pair of modelProviderPairs) {
29
- // Add some daily variance (±40%)
30
- const variance = 0.6 + Math.random() * 0.8;
31
- const dayOfWeek = date.getDay();
32
- const weekendFactor = dayOfWeek === 0 || dayOfWeek === 6 ? 0.5 : 1.0;
33
- const trendFactor = 1 + (29 - day) * 0.01; // slight upward trend
34
-
35
- const inputRatio = 0.7 + Math.random() * 0.1;
36
- const baseTokens = pair.baseTokens * variance * weekendFactor * trendFactor;
37
- const inputTokens = Math.round(baseTokens * inputRatio);
38
- const outputTokens = Math.round(baseTokens * (1 - inputRatio));
39
- const cacheReadTokens = Math.round(inputTokens * 0.3 * Math.random());
40
- const cacheWriteTokens = Math.round(inputTokens * 0.1 * Math.random());
41
- const requests = Math.round(20 + Math.random() * 60);
42
-
43
- records.push({
44
- date: dateStr!,
45
- providerId: pair.providerId,
46
- modelId: pair.modelId,
47
- inputTokens,
48
- outputTokens,
49
- cacheReadTokens,
50
- cacheWriteTokens,
51
- requests,
52
- cost: pair.baseCost * variance * weekendFactor * trendFactor,
53
- });
54
- }
55
- }
56
-
57
- return records;
58
- }
59
-
60
- export const MOCK_USAGE_DATA = generateMockUsage();
61
-
62
- // Aggregate helpers
63
-
64
- export function getDailyAggregates(): {
65
- date: string;
66
- totalTokens: number;
67
- totalCost: number;
68
- totalRequests: number;
69
- inputTokens: number;
70
- outputTokens: number;
71
- }[] {
72
- const daily = new Map<string, {
73
- totalTokens: number; totalCost: number; totalRequests: number;
74
- inputTokens: number; outputTokens: number;
75
- }>();
76
-
77
- for (const r of MOCK_USAGE_DATA) {
78
- const d = daily.get(r.date) ?? {
79
- totalTokens: 0, totalCost: 0, totalRequests: 0,
80
- inputTokens: 0, outputTokens: 0,
81
- };
82
- d.totalTokens += r.inputTokens + r.outputTokens + r.cacheReadTokens + r.cacheWriteTokens;
83
- d.totalCost += r.cost;
84
- d.totalRequests += r.requests;
85
- d.inputTokens += r.inputTokens;
86
- d.outputTokens += r.outputTokens;
87
- daily.set(r.date, d);
88
- }
89
-
90
- return Array.from(daily.entries())
91
- .map(([date, agg]) => ({ date, ...agg }))
92
- .sort((a, b) => a.date.localeCompare(b.date));
93
- }
94
-
95
- export function getProviderSummaries() {
96
- const sums = new Map<string, {
97
- totalTokens: number; totalCost: number; totalRequests: number;
98
- }>();
99
-
100
- for (const r of MOCK_USAGE_DATA) {
101
- const s = sums.get(r.providerId) ?? { totalTokens: 0, totalCost: 0, totalRequests: 0 };
102
- s.totalTokens += r.inputTokens + r.outputTokens + r.cacheReadTokens + r.cacheWriteTokens;
103
- s.totalCost += r.cost;
104
- s.totalRequests += r.requests;
105
- sums.set(r.providerId, s);
106
- }
107
-
108
- return Array.from(sums.entries()).map(([providerId, s]) => ({ providerId, ...s }));
109
- }
110
-
111
- export function getModelSummaries() {
112
- const sums = new Map<string, {
113
- providerId: string; totalTokens: number; totalCost: number; totalRequests: number; count: number;
114
- }>();
115
-
116
- for (const r of MOCK_USAGE_DATA) {
117
- const key = `${r.providerId}/${r.modelId}`;
118
- const s = sums.get(key) ?? { providerId: r.providerId, totalTokens: 0, totalCost: 0, totalRequests: 0, count: 0 };
119
- s.totalTokens += r.inputTokens + r.outputTokens + r.cacheReadTokens + r.cacheWriteTokens;
120
- s.totalCost += r.cost;
121
- s.totalRequests += r.requests;
122
- s.count++;
123
- sums.set(key, s);
124
- }
125
-
126
- return Array.from(sums.entries()).map(([key, s]) => {
127
- const [providerId, modelId] = key.split("/");
128
- return {
129
- modelId: modelId!,
130
- providerId: s.providerId,
131
- totalTokens: s.totalTokens,
132
- totalCost: s.totalCost,
133
- totalRequests: s.totalRequests,
134
- avgTokensPerRequest: Math.round(s.totalTokens / s.totalRequests),
135
- };
136
- });
137
- }
138
-
139
- export function getTotals() {
140
- let totalTokens = 0;
141
- let totalCost = 0;
142
- let totalRequests = 0;
143
-
144
- for (const r of MOCK_USAGE_DATA) {
145
- totalTokens += r.inputTokens + r.outputTokens + r.cacheReadTokens + r.cacheWriteTokens;
146
- totalCost += r.cost;
147
- totalRequests += r.requests;
148
- }
149
-
150
- return { totalTokens, totalCost, totalRequests };
151
- }