@phi-code-admin/phi-code 0.60.4 → 0.60.6

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.
@@ -37,36 +37,110 @@ interface RoutingConfig {
37
37
  default: { model: string; agent: string | null };
38
38
  }
39
39
 
40
- // ─── Model Database ─────────────────────────────────────────────────────
41
-
42
- const MODEL_DB: Record<string, { contextWindow: number; maxTokens: number; reasoning: boolean }> = {
43
- // Alibaba / DashScope
44
- "qwen3.5-plus": { contextWindow: 1000000, maxTokens: 16384, reasoning: true },
45
- "qwen3-max-2026-01-23": { contextWindow: 262144, maxTokens: 16384, reasoning: true },
46
- "qwen3-coder-plus": { contextWindow: 1000000, maxTokens: 16384, reasoning: true },
47
- "qwen3-coder-next": { contextWindow: 1000000, maxTokens: 16384, reasoning: true },
48
- "kimi-k2.5": { contextWindow: 262144, maxTokens: 16384, reasoning: true },
49
- "glm-5": { contextWindow: 200000, maxTokens: 128000, reasoning: true },
50
- "glm-4.7": { contextWindow: 200000, maxTokens: 128000, reasoning: true },
51
- "MiniMax-M2.5": { contextWindow: 1000000, maxTokens: 16384, reasoning: true },
52
- // OpenAI
53
- "gpt-4o": { contextWindow: 128000, maxTokens: 16384, reasoning: false },
54
- "gpt-4o-mini": { contextWindow: 128000, maxTokens: 16384, reasoning: false },
55
- "o1": { contextWindow: 200000, maxTokens: 100000, reasoning: true },
56
- "o3-mini": { contextWindow: 200000, maxTokens: 100000, reasoning: true },
57
- // Anthropic
58
- "claude-sonnet-4-20250514": { contextWindow: 200000, maxTokens: 64000, reasoning: true },
59
- "claude-3-5-haiku-20241022": { contextWindow: 200000, maxTokens: 8192, reasoning: false },
60
- // Google
61
- "gemini-2.5-pro": { contextWindow: 1000000, maxTokens: 65536, reasoning: true },
62
- "gemini-2.5-flash": { contextWindow: 1000000, maxTokens: 65536, reasoning: true },
63
- // Groq
64
- "llama-3.3-70b-versatile": { contextWindow: 128000, maxTokens: 32768, reasoning: false },
65
- "mixtral-8x7b-32768": { contextWindow: 32768, maxTokens: 4096, reasoning: false },
66
- };
40
+ // ─── Dynamic Model Specs via OpenRouter ──────────────────────────────────
41
+
42
+ interface ModelSpec {
43
+ contextWindow: number;
44
+ maxTokens: number;
45
+ reasoning: boolean;
46
+ }
47
+
48
+ // Cache for OpenRouter model data (fetched once per session)
49
+ let openRouterCache: Map<string, ModelSpec> | null = null;
50
+
51
+ /**
52
+ * Fetch model specs from OpenRouter's free API (no key needed).
53
+ * Returns a map of model base name → specs.
54
+ * Falls back to conservative defaults if unreachable.
55
+ */
56
+ async function fetchModelSpecs(): Promise<Map<string, ModelSpec>> {
57
+ if (openRouterCache) return openRouterCache;
58
+
59
+ const cache = new Map<string, ModelSpec>();
60
+
61
+ try {
62
+ const controller = new AbortController();
63
+ const timeout = setTimeout(() => controller.abort(), 5000);
64
+ const res = await fetch("https://openrouter.ai/api/v1/models", {
65
+ signal: controller.signal,
66
+ });
67
+ clearTimeout(timeout);
68
+
69
+ if (res.ok) {
70
+ const data = await res.json() as any;
71
+ for (const m of (data.data || [])) {
72
+ const contextLength = m.context_length || 128000;
73
+ const maxOutput = m.top_provider?.max_completion_tokens || Math.min(contextLength, 16384);
74
+ const hasReasoning = (m.supported_parameters || []).includes("reasoning");
75
+
76
+ // Store by full ID and by base name (for fuzzy matching)
77
+ const spec: ModelSpec = {
78
+ contextWindow: contextLength,
79
+ maxTokens: typeof maxOutput === "number" ? maxOutput : 16384,
80
+ reasoning: hasReasoning,
81
+ };
82
+
83
+ cache.set(m.id, spec);
84
+ // Also store by short name (e.g., "qwen3.5-plus" from "qwen/qwen3.5-plus-02-15")
85
+ const parts = m.id.split("/");
86
+ if (parts.length > 1) {
87
+ cache.set(parts[1], spec);
88
+ }
89
+ }
90
+ }
91
+ } catch {
92
+ // OpenRouter unreachable — cache stays empty, fallback used
93
+ }
67
94
 
68
- function getModelSpec(id: string): { contextWindow: number; maxTokens: number; reasoning: boolean } {
69
- return MODEL_DB[id] || { contextWindow: 128000, maxTokens: 16384, reasoning: true };
95
+ openRouterCache = cache;
96
+ return cache;
97
+ }
98
+
99
+ /**
100
+ * Get model spec by ID. Tries OpenRouter cache first with fuzzy matching,
101
+ * then falls back to conservative defaults.
102
+ */
103
+ async function getModelSpec(id: string): Promise<ModelSpec> {
104
+ const cache = await fetchModelSpecs();
105
+
106
+ // Exact match
107
+ if (cache.has(id)) return cache.get(id)!;
108
+
109
+ // Try common prefixed variants
110
+ const prefixes = ["qwen/", "moonshotai/", "z-ai/", "minimax/", "openai/", "anthropic/", "google/"];
111
+ for (const prefix of prefixes) {
112
+ const key = prefix + id;
113
+ if (cache.has(key)) return cache.get(key)!;
114
+ }
115
+
116
+ // Fuzzy: find by base name inclusion
117
+ const lower = id.toLowerCase().replace(/[-_.]/g, "");
118
+ for (const [key, spec] of cache) {
119
+ const keyLower = key.toLowerCase().replace(/[-_.]/g, "");
120
+ if (keyLower.includes(lower) || lower.includes(keyLower.split("/").pop() || "")) {
121
+ return spec;
122
+ }
123
+ }
124
+
125
+ // Conservative fallback
126
+ return { contextWindow: 128000, maxTokens: 16384, reasoning: true };
127
+ }
128
+
129
+ /**
130
+ * Synchronous fallback for non-async contexts.
131
+ * Uses cached data if available, otherwise returns defaults.
132
+ */
133
+ function getModelSpecSync(id: string): ModelSpec {
134
+ if (!openRouterCache) return { contextWindow: 128000, maxTokens: 16384, reasoning: true };
135
+
136
+ if (openRouterCache.has(id)) return openRouterCache.get(id)!;
137
+
138
+ const prefixes = ["qwen/", "moonshotai/", "z-ai/", "minimax/", "openai/", "anthropic/", "google/"];
139
+ for (const prefix of prefixes) {
140
+ if (openRouterCache.has(prefix + id)) return openRouterCache.get(prefix + id)!;
141
+ }
142
+
143
+ return { contextWindow: 128000, maxTokens: 16384, reasoning: true };
70
144
  }
71
145
 
72
146
  // ─── Provider Detection ──────────────────────────────────────────────────
@@ -640,6 +714,10 @@ _Edit this file to customize Phi Code's behavior for your project._
640
714
  ctx.ui.notify("║ Φ Phi Code Setup Wizard ║", "info");
641
715
  ctx.ui.notify("╚══════════════════════════════════════╝\n", "info");
642
716
 
717
+ // Pre-fetch model specs from OpenRouter (async, cached)
718
+ ctx.ui.notify("🔍 Fetching model specs from OpenRouter...", "info");
719
+ await fetchModelSpecs();
720
+
643
721
  // 1. Detect providers
644
722
  ctx.ui.notify("🔍 Detecting providers...\n", "info");
645
723
  const providers = detectProviders();
@@ -730,8 +808,8 @@ _Edit this file to customize Phi Code's behavior for your project._
730
808
  baseUrl: chosen.baseUrl,
731
809
  api: "openai-completions",
732
810
  apiKey: apiKey.trim(),
733
- models: chosen.models.map((id: string) => {
734
- const spec = getModelSpec(id);
811
+ models: await Promise.all(chosen.models.map(async (id: string) => {
812
+ const spec = await getModelSpec(id);
735
813
  return {
736
814
  id,
737
815
  name: id,
@@ -740,7 +818,7 @@ _Edit this file to customize Phi Code's behavior for your project._
740
818
  contextWindow: spec.contextWindow,
741
819
  maxTokens: spec.maxTokens,
742
820
  };
743
- }),
821
+ })),
744
822
  };
745
823
 
746
824
  await writeFile(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
@@ -948,13 +1026,13 @@ For persistence, set environment variables:
948
1026
  baseUrl: providerDef.baseUrl,
949
1027
  api: "openai-completions",
950
1028
  apiKey: key,
951
- models: providerDef.models.map((id: string) => {
952
- const spec = getModelSpec(id);
1029
+ models: await Promise.all(providerDef.models.map(async (id: string) => {
1030
+ const spec = await getModelSpec(id);
953
1031
  return {
954
1032
  id, name: id, reasoning: spec.reasoning, input: ["text"],
955
1033
  contextWindow: spec.contextWindow, maxTokens: spec.maxTokens,
956
1034
  };
957
- }),
1035
+ })),
958
1036
  };
959
1037
  writeFileSync(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
960
1038
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-code-admin/phi-code",
3
- "version": "0.60.4",
3
+ "version": "0.60.6",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {