@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.
package/extensions/phi/init.ts
CHANGED
|
@@ -37,36 +37,110 @@ interface RoutingConfig {
|
|
|
37
37
|
default: { model: string; agent: string | null };
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
// ─── Model
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
69
|
-
return
|
|
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
|
}
|