@f5-sales-demo/xcsh 21.49.3 → 21.50.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.
- package/package.json +8 -8
- package/src/commit/agentic/index.ts +2 -2
- package/src/commit/pipeline.ts +2 -2
- package/src/config/auto-config.ts +29 -4
- package/src/config/model-catalog.ts +13 -5
- package/src/config/model-registry.ts +74 -45
- package/src/config/model-resolver.ts +14 -0
- package/src/config/settings-schema.ts +14 -11
- package/src/context/profile.ts +2 -2
- package/src/internal-urls/api-catalog-discovery.ts +29 -2
- package/src/internal-urls/api-catalog-qmd-index.generated.ts +2 -2
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/main.ts +2 -2
- package/src/memories/index.ts +3 -3
- package/src/modes/components/footer.ts +3 -1
- package/src/modes/components/model-selector.ts +1 -0
- package/src/modes/components/status-line.ts +2 -2
- package/src/modes/controllers/selector-controller.ts +4 -4
- package/src/remote-control/host.ts +1 -1
- package/src/remote-control/managed-sessions.ts +1 -1
- package/src/remote-control/session.ts +4 -4
- package/src/sdk.ts +2 -2
- package/src/session/active-model.ts +2 -2
- package/src/session/agent-session.ts +12 -9
- package/src/session/compaction/branch-summarization.ts +2 -2
- package/src/session/compaction/compaction.ts +21 -4
- package/src/task/executor.ts +2 -2
- package/src/thinking.ts +5 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "21.
|
|
4
|
+
"version": "21.50.0",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/f5-sales-demo/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -67,13 +67,13 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@agentclientprotocol/sdk": "1.4.0",
|
|
70
|
-
"@f5-sales-demo/pi-agent-core": "21.
|
|
71
|
-
"@f5-sales-demo/pi-ai": "21.
|
|
72
|
-
"@f5-sales-demo/pi-natives": "21.
|
|
73
|
-
"@f5-sales-demo/pi-resource-management": "21.
|
|
74
|
-
"@f5-sales-demo/pi-tui": "21.
|
|
75
|
-
"@f5-sales-demo/pi-utils": "21.
|
|
76
|
-
"@f5-sales-demo/xcsh-stats": "21.
|
|
70
|
+
"@f5-sales-demo/pi-agent-core": "21.50.0",
|
|
71
|
+
"@f5-sales-demo/pi-ai": "21.50.0",
|
|
72
|
+
"@f5-sales-demo/pi-natives": "21.50.0",
|
|
73
|
+
"@f5-sales-demo/pi-resource-management": "21.50.0",
|
|
74
|
+
"@f5-sales-demo/pi-tui": "21.50.0",
|
|
75
|
+
"@f5-sales-demo/pi-utils": "21.50.0",
|
|
76
|
+
"@f5-sales-demo/xcsh-stats": "21.50.0",
|
|
77
77
|
"@mozilla/readability": "^0.6",
|
|
78
78
|
"@resvg/resvg-wasm": "2.6.2",
|
|
79
79
|
"@sinclair/typebox": "0.34.52",
|
|
@@ -31,8 +31,8 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
31
31
|
process.stdout.write("● Resolving model...\n");
|
|
32
32
|
const modelRegistry = new ModelRegistry(authStorage, undefined, {
|
|
33
33
|
getProviderOrder: () => settings.get("modelProviderOrder"),
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
getOpenAIContextTier: () => settings.get("providers.openaiContextTier"),
|
|
35
|
+
getLiteLLMContextTier: () => settings.get("providers.litellmContextTier"),
|
|
36
36
|
});
|
|
37
37
|
await modelRegistry.refresh();
|
|
38
38
|
const stagedFilesPromise = (async () => {
|
package/src/commit/pipeline.ts
CHANGED
|
@@ -44,8 +44,8 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
44
44
|
const authStorage = await discoverAuthStorage();
|
|
45
45
|
const modelRegistry = new ModelRegistry(authStorage, undefined, {
|
|
46
46
|
getProviderOrder: () => settings.get("modelProviderOrder"),
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
getOpenAIContextTier: () => settings.get("providers.openaiContextTier"),
|
|
48
|
+
getLiteLLMContextTier: () => settings.get("providers.litellmContextTier"),
|
|
49
49
|
});
|
|
50
50
|
await modelRegistry.refresh();
|
|
51
51
|
|
|
@@ -20,7 +20,7 @@ import { hardenAgentConfigFileSync, writeAgentConfigFileSync } from "./agent-con
|
|
|
20
20
|
import { DEFAULT_MODEL_ROLE } from "./settings-schema";
|
|
21
21
|
|
|
22
22
|
/** Current config schema version. Bump when the generated format changes. */
|
|
23
|
-
export const CURRENT_CONFIG_VERSION =
|
|
23
|
+
export const CURRENT_CONFIG_VERSION = 11;
|
|
24
24
|
const LITELLM_CONFIG_DIR_MODE = 0o700;
|
|
25
25
|
const LITELLM_MODELS_FILE_MODE = 0o600;
|
|
26
26
|
const GENERATED_LITELLM_MARKER = "# Auto-generated by xcsh for LiteLLM proxy";
|
|
@@ -123,6 +123,7 @@ export function generateModelsYml(baseUrl: string, options?: GenerateModelsYmlOp
|
|
|
123
123
|
" - gpt-6-astra",
|
|
124
124
|
" - gpt-5.6-luna",
|
|
125
125
|
" - gpt-5.6-sol",
|
|
126
|
+
" - gpt-5.5",
|
|
126
127
|
" models:",
|
|
127
128
|
" - id: gpt-6-luna",
|
|
128
129
|
" name: GPT-6 Luna",
|
|
@@ -130,7 +131,8 @@ export function generateModelsYml(baseUrl: string, options?: GenerateModelsYmlOp
|
|
|
130
131
|
` baseUrl: "${responsesBaseUrl}"`,
|
|
131
132
|
" - id: gpt-5.6-terra",
|
|
132
133
|
" name: GPT-5.6 Terra",
|
|
133
|
-
" api: openai-
|
|
134
|
+
" api: openai-responses",
|
|
135
|
+
` baseUrl: "${responsesBaseUrl}"`,
|
|
134
136
|
" - id: gpt-6-sol",
|
|
135
137
|
" name: GPT-6 Sol",
|
|
136
138
|
" api: openai-responses",
|
|
@@ -141,10 +143,16 @@ export function generateModelsYml(baseUrl: string, options?: GenerateModelsYmlOp
|
|
|
141
143
|
` baseUrl: "${responsesBaseUrl}"`,
|
|
142
144
|
" - id: gpt-5.6-luna",
|
|
143
145
|
" name: GPT-5.6 Luna",
|
|
144
|
-
" api: openai-
|
|
146
|
+
" api: openai-responses",
|
|
147
|
+
` baseUrl: "${responsesBaseUrl}"`,
|
|
145
148
|
" - id: gpt-5.6-sol",
|
|
146
149
|
" name: GPT-5.6 Sol",
|
|
147
|
-
" api: openai-
|
|
150
|
+
" api: openai-responses",
|
|
151
|
+
` baseUrl: "${responsesBaseUrl}"`,
|
|
152
|
+
" - id: gpt-5.5",
|
|
153
|
+
" name: GPT-5.5",
|
|
154
|
+
" api: openai-responses",
|
|
155
|
+
` baseUrl: "${responsesBaseUrl}"`,
|
|
148
156
|
" picker:",
|
|
149
157
|
" groupId: litellm",
|
|
150
158
|
" groupLabel: LiteLLM",
|
|
@@ -251,6 +259,23 @@ export function generateModelsYml(baseUrl: string, options?: GenerateModelsYmlOp
|
|
|
251
259
|
" supportsTemperature: false",
|
|
252
260
|
" gpt-5.6-luna:",
|
|
253
261
|
" reasoning: true",
|
|
262
|
+
" input:",
|
|
263
|
+
" - text",
|
|
264
|
+
" - image",
|
|
265
|
+
" contextWindow: 1050000",
|
|
266
|
+
" maxTokens: 128000",
|
|
267
|
+
" compat:",
|
|
268
|
+
" supportsTemperature: false",
|
|
269
|
+
" cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }",
|
|
270
|
+
" gpt-5.5:",
|
|
271
|
+
" reasoning: true",
|
|
272
|
+
" input:",
|
|
273
|
+
" - text",
|
|
274
|
+
" - image",
|
|
275
|
+
" contextWindow: 1050000",
|
|
276
|
+
" maxTokens: 128000",
|
|
277
|
+
" compat:",
|
|
278
|
+
" supportsTemperature: false",
|
|
254
279
|
" cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }",
|
|
255
280
|
];
|
|
256
281
|
|
|
@@ -10,9 +10,16 @@ function compareVersions(left: string, right: string): number {
|
|
|
10
10
|
return 0;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
const
|
|
15
|
-
|
|
13
|
+
/** Present the exact model-interaction contract when each provider advertises the model. */
|
|
14
|
+
const CODEX_INTERACTION_MODEL_IDS = new Set([
|
|
15
|
+
"gpt-6-astra",
|
|
16
|
+
"gpt-6-sol",
|
|
17
|
+
"gpt-6-luna",
|
|
18
|
+
"gpt-5.6-sol",
|
|
19
|
+
"gpt-5.6-terra",
|
|
20
|
+
"gpt-5.6-luna",
|
|
21
|
+
"gpt-5.5",
|
|
22
|
+
]);
|
|
16
23
|
|
|
17
24
|
/** Shared catalog boundary for the TUI and attached human interfaces. */
|
|
18
25
|
export function filterCurrentBrowserModels(models: readonly Model[]): Model[] {
|
|
@@ -39,9 +46,10 @@ export function filterCurrentBrowserModels(models: readonly Model[]): Model[] {
|
|
|
39
46
|
return models.filter(model => {
|
|
40
47
|
const gpt = model.id.match(/^gpt-(\d+(?:\.\d+)?)(?:-|$)/i);
|
|
41
48
|
if (gpt?.[1]) {
|
|
49
|
+
if (model.provider === "openai-codex" || model.provider === "litellm") {
|
|
50
|
+
return CODEX_INTERACTION_MODEL_IDS.has(model.id.toLowerCase());
|
|
51
|
+
}
|
|
42
52
|
if (compareVersions(gpt[1], "5.6") < 0) return false;
|
|
43
|
-
if (model.provider === "openai-codex") return DEFAULT_OPENAI_CODEX_MODEL_IDS.has(model.id.toLowerCase());
|
|
44
|
-
if (model.provider === "litellm") return CURRENT_LITELLM_OPENAI_MODEL_IDS.has(model.id.toLowerCase());
|
|
45
53
|
}
|
|
46
54
|
const claude = model.id.match(/^claude-(opus|sonnet|haiku)-(\d+)(?:[.-](\d+))?(?:-|$)/i);
|
|
47
55
|
if (claude?.[1] && claude[2]) {
|
|
@@ -2,7 +2,9 @@ import * as path from "node:path";
|
|
|
2
2
|
import {
|
|
3
3
|
type Api,
|
|
4
4
|
type AssistantMessageEventStream,
|
|
5
|
+
applyCodexInteractionMetadata,
|
|
5
6
|
applyGeneratedModelPolicies,
|
|
7
|
+
type CodexContextTier,
|
|
6
8
|
type Context,
|
|
7
9
|
createModelManager,
|
|
8
10
|
DEFAULT_LOCAL_TOKEN,
|
|
@@ -23,6 +25,7 @@ import {
|
|
|
23
25
|
readModelCache,
|
|
24
26
|
registerCustomApi,
|
|
25
27
|
registerOAuthProvider,
|
|
28
|
+
resolveCodexContextBudget,
|
|
26
29
|
type SimpleStreamOptions,
|
|
27
30
|
type ThinkingConfig,
|
|
28
31
|
unregisterCustomApis,
|
|
@@ -50,17 +53,14 @@ export type { CanonicalModelIndex, CanonicalModelRecord, CanonicalModelVariant,
|
|
|
50
53
|
|
|
51
54
|
export const kNoAuth = NO_AUTH_API_KEY;
|
|
52
55
|
|
|
53
|
-
const OPENAI_CODEX_STANDARD_CONTEXT_WINDOW = 272_000;
|
|
54
|
-
const OPENAI_CODEX_MAX_CONTEXT_WINDOW = 1_050_000;
|
|
55
|
-
const OPENAI_CODEX_MAX_CONTEXT_MODEL_IDS = new Set(["gpt-6-luna", "gpt-6-sol"]);
|
|
56
|
-
const LITELLM_STANDARD_CONTEXT_WINDOW = 272_000;
|
|
57
|
-
const LITELLM_MAX_CONTEXT_WINDOW = 1_050_000;
|
|
58
|
-
const LITELLM_MAX_CONTEXT_MODEL_IDS = new Set(["gpt-6-luna", "gpt-5.6-terra", "gpt-6-sol", "gpt-6-astra"]);
|
|
59
|
-
|
|
60
56
|
export function isAuthenticated(apiKey: string | undefined | null): apiKey is string {
|
|
61
57
|
return Boolean(apiKey) && apiKey !== kNoAuth;
|
|
62
58
|
}
|
|
63
59
|
|
|
60
|
+
function positiveInteger(value: unknown): number | undefined {
|
|
61
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
64
|
export type ModelRole = "default" | "smol" | "slow" | "vision" | "plan" | "designer" | "commit" | "task";
|
|
65
65
|
|
|
66
66
|
export interface ModelRoleInfo {
|
|
@@ -878,8 +878,8 @@ export class ModelRegistry {
|
|
|
878
878
|
#lastDiscoveryWarnings: Map<string, string> = new Map();
|
|
879
879
|
#hasProbed = false;
|
|
880
880
|
#refreshQueue: Promise<void> = Promise.resolve();
|
|
881
|
-
#
|
|
882
|
-
#
|
|
881
|
+
#openAIContextTier: CodexContextTier = "standard";
|
|
882
|
+
#litellmContextTier: CodexContextTier = "standard";
|
|
883
883
|
|
|
884
884
|
#enqueueRefresh(operation: () => Promise<void>): Promise<void> {
|
|
885
885
|
const pending = this.#refreshQueue.then(operation);
|
|
@@ -901,8 +901,8 @@ export class ModelRegistry {
|
|
|
901
901
|
modelsPath?: string,
|
|
902
902
|
private readonly options: {
|
|
903
903
|
getProviderOrder?: () => readonly string[];
|
|
904
|
-
|
|
905
|
-
|
|
904
|
+
getOpenAIContextTier?: () => CodexContextTier;
|
|
905
|
+
getLiteLLMContextTier?: () => CodexContextTier;
|
|
906
906
|
} = {},
|
|
907
907
|
) {
|
|
908
908
|
this.#modelsConfigFile = ModelsConfigFile.relocate(modelsPath);
|
|
@@ -1055,8 +1055,8 @@ export class ModelRegistry {
|
|
|
1055
1055
|
}
|
|
1056
1056
|
|
|
1057
1057
|
#loadModels() {
|
|
1058
|
-
this.#
|
|
1059
|
-
this.#
|
|
1058
|
+
this.#openAIContextTier = this.options.getOpenAIContextTier?.() ?? this.#openAIContextTier;
|
|
1059
|
+
this.#litellmContextTier = this.options.getLiteLLMContextTier?.() ?? this.#litellmContextTier;
|
|
1060
1060
|
// Load custom models from models.json first (to know which providers to override)
|
|
1061
1061
|
const {
|
|
1062
1062
|
models: customModels = [],
|
|
@@ -1090,42 +1090,39 @@ export class ModelRegistry {
|
|
|
1090
1090
|
const combined = this.#mergeCustomModels(withConfigModels, this.#runtimeModelOverlays);
|
|
1091
1091
|
|
|
1092
1092
|
this.#models = this.#applyProviderModelAllowlists(this.#applyModelOverrides(combined, this.#modelOverrides));
|
|
1093
|
-
this.#
|
|
1094
|
-
this.#
|
|
1093
|
+
this.#applyOpenAIContextTier();
|
|
1094
|
+
this.#applyLiteLLMContextTier();
|
|
1095
1095
|
this.#rebuildCanonicalIndex();
|
|
1096
1096
|
}
|
|
1097
1097
|
|
|
1098
|
-
#
|
|
1099
|
-
const contextWindow = this.#openAICodexMaxContext
|
|
1100
|
-
? OPENAI_CODEX_MAX_CONTEXT_WINDOW
|
|
1101
|
-
: OPENAI_CODEX_STANDARD_CONTEXT_WINDOW;
|
|
1098
|
+
#applyContextTier(provider: "openai-codex" | "litellm", tier: CodexContextTier): void {
|
|
1102
1099
|
for (const model of this.#models) {
|
|
1103
|
-
if (model.provider
|
|
1104
|
-
|
|
1105
|
-
|
|
1100
|
+
if (model.provider !== provider) continue;
|
|
1101
|
+
Object.assign(model, applyCodexInteractionMetadata(model));
|
|
1102
|
+
const budget = resolveCodexContextBudget(model.id, tier, model.providerContextWindow);
|
|
1103
|
+
if (budget) Object.assign(model, budget);
|
|
1106
1104
|
}
|
|
1107
1105
|
}
|
|
1108
1106
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1107
|
+
#applyOpenAIContextTier(): void {
|
|
1108
|
+
this.#applyContextTier("openai-codex", this.#openAIContextTier);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/** Update subscription context limits without rebuilding the registry. */
|
|
1112
|
+
setOpenAIContextTier(tier: CodexContextTier): void {
|
|
1113
|
+
this.#openAIContextTier = tier;
|
|
1114
|
+
this.#applyOpenAIContextTier();
|
|
1113
1115
|
this.#rebuildCanonicalIndex();
|
|
1114
1116
|
}
|
|
1115
1117
|
|
|
1116
|
-
#
|
|
1117
|
-
|
|
1118
|
-
for (const model of this.#models) {
|
|
1119
|
-
if (model.provider === "litellm" && LITELLM_MAX_CONTEXT_MODEL_IDS.has(model.id)) {
|
|
1120
|
-
model.contextWindow = contextWindow;
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1118
|
+
#applyLiteLLMContextTier(): void {
|
|
1119
|
+
this.#applyContextTier("litellm", this.#litellmContextTier);
|
|
1123
1120
|
}
|
|
1124
1121
|
|
|
1125
1122
|
/** Update current internal OpenAI context limits without changing subscription models. */
|
|
1126
|
-
|
|
1127
|
-
this.#
|
|
1128
|
-
this.#
|
|
1123
|
+
setLiteLLMContextTier(tier: CodexContextTier): void {
|
|
1124
|
+
this.#litellmContextTier = tier;
|
|
1125
|
+
this.#applyLiteLLMContextTier();
|
|
1129
1126
|
this.#rebuildCanonicalIndex();
|
|
1130
1127
|
}
|
|
1131
1128
|
|
|
@@ -1198,6 +1195,13 @@ export class ModelRegistry {
|
|
|
1198
1195
|
return merged;
|
|
1199
1196
|
}
|
|
1200
1197
|
|
|
1198
|
+
#filterOverlaysBySuccessfulDiscovery(models: CustomModelOverlay[]): CustomModelOverlay[] {
|
|
1199
|
+
return models.filter(model => {
|
|
1200
|
+
const discovery = this.#providerDiscoveryStates.get(model.provider);
|
|
1201
|
+
return discovery?.status !== "ok" || discovery.models.includes(model.id);
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1201
1205
|
#applyProviderModelAllowlists(models: Model<Api>[]): Model<Api>[] {
|
|
1202
1206
|
return models.filter(model => {
|
|
1203
1207
|
const allowlist = this.#modelAllowlists.get(model.provider);
|
|
@@ -1420,9 +1424,15 @@ export class ModelRegistry {
|
|
|
1420
1424
|
]);
|
|
1421
1425
|
const discovered = [...configuredDiscovered, ...builtInDiscovered];
|
|
1422
1426
|
// Replace only catalogs which completed successfully, including an empty response.
|
|
1427
|
+
const successfulDiscoveryProviders = providerFilter
|
|
1428
|
+
? [...providerFilter].filter(provider => this.#providerDiscoveryStates.get(provider)?.status === "ok")
|
|
1429
|
+
: [...this.#providerDiscoveryStates.values()]
|
|
1430
|
+
.filter(state => state.status === "ok")
|
|
1431
|
+
.map(state => state.provider);
|
|
1423
1432
|
const refreshed = new Set([
|
|
1424
1433
|
...selectedDiscoverableProviders.map(provider => provider.provider),
|
|
1425
1434
|
...discovered.map(model => model.provider),
|
|
1435
|
+
...successfulDiscoveryProviders,
|
|
1426
1436
|
]);
|
|
1427
1437
|
const discoveredModels = this.#applyHardcodedModelPolicies(
|
|
1428
1438
|
discovered.map(model => {
|
|
@@ -1452,12 +1462,18 @@ export class ModelRegistry {
|
|
|
1452
1462
|
model => !refreshed.has(model.provider) || this.#providerDiscoveryStates.get(model.provider)?.status !== "ok",
|
|
1453
1463
|
);
|
|
1454
1464
|
const resolved = this.#mergeResolvedModels(this.#models, discoveredModels);
|
|
1455
|
-
const withConfigModels = this.#mergeCustomModels(
|
|
1465
|
+
const withConfigModels = this.#mergeCustomModels(
|
|
1466
|
+
resolved,
|
|
1467
|
+
this.#filterOverlaysBySuccessfulDiscovery(this.#customModelOverlays),
|
|
1468
|
+
);
|
|
1456
1469
|
// Merge runtime extension models so they survive online discovery completion
|
|
1457
|
-
const combined = this.#mergeCustomModels(
|
|
1470
|
+
const combined = this.#mergeCustomModels(
|
|
1471
|
+
withConfigModels,
|
|
1472
|
+
this.#filterOverlaysBySuccessfulDiscovery(this.#runtimeModelOverlays),
|
|
1473
|
+
);
|
|
1458
1474
|
this.#models = this.#applyProviderModelAllowlists(this.#applyModelOverrides(combined, this.#modelOverrides));
|
|
1459
|
-
this.#
|
|
1460
|
-
this.#
|
|
1475
|
+
this.#applyOpenAIContextTier();
|
|
1476
|
+
this.#applyLiteLLMContextTier();
|
|
1461
1477
|
this.#rebuildCanonicalIndex();
|
|
1462
1478
|
}
|
|
1463
1479
|
|
|
@@ -2047,7 +2063,13 @@ export class ModelRegistry {
|
|
|
2047
2063
|
throw new Error(`HTTP ${response.status} from ${modelsUrl}`);
|
|
2048
2064
|
}
|
|
2049
2065
|
const payload = await response.json();
|
|
2050
|
-
const items: Array<{
|
|
2066
|
+
const items: Array<{
|
|
2067
|
+
id: string;
|
|
2068
|
+
contextWindow?: number;
|
|
2069
|
+
context_window?: unknown;
|
|
2070
|
+
max_input_tokens?: unknown;
|
|
2071
|
+
max_output_tokens?: unknown;
|
|
2072
|
+
}> =
|
|
2051
2073
|
providerConfig.provider === "vllm"
|
|
2052
2074
|
? parseVllmModelsPayload(payload)
|
|
2053
2075
|
: ((payload as { data?: Array<{ id: string }> }).data ?? []);
|
|
@@ -2055,6 +2077,10 @@ export class ModelRegistry {
|
|
|
2055
2077
|
for (const item of items) {
|
|
2056
2078
|
const id = item.id;
|
|
2057
2079
|
if (!id) continue;
|
|
2080
|
+
const advertisedContextWindow =
|
|
2081
|
+
positiveInteger(item.context_window) ?? positiveInteger(item.max_input_tokens) ?? item.contextWindow;
|
|
2082
|
+
const advertisedOutputLimit = positiveInteger(item.max_output_tokens);
|
|
2083
|
+
const contextWindow = advertisedContextWindow ?? 128000;
|
|
2058
2084
|
discovered.push(
|
|
2059
2085
|
enrichModelThinking({
|
|
2060
2086
|
id,
|
|
@@ -2065,11 +2091,13 @@ export class ModelRegistry {
|
|
|
2065
2091
|
reasoning: false,
|
|
2066
2092
|
input: ["text"],
|
|
2067
2093
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
2068
|
-
contextWindow
|
|
2094
|
+
contextWindow,
|
|
2069
2095
|
maxTokens:
|
|
2070
|
-
|
|
2096
|
+
advertisedOutputLimit ??
|
|
2097
|
+
(advertisedContextWindow === undefined
|
|
2071
2098
|
? 8192
|
|
2072
|
-
: Math.max(1, Math.min(8192, Math.floor(
|
|
2099
|
+
: Math.max(1, Math.min(8192, Math.floor(advertisedContextWindow / 4)))),
|
|
2100
|
+
providerContextWindow: advertisedContextWindow,
|
|
2073
2101
|
headers,
|
|
2074
2102
|
}),
|
|
2075
2103
|
);
|
|
@@ -2150,7 +2178,8 @@ export class ModelRegistry {
|
|
|
2150
2178
|
// bundled corrections. Reapply generated-model policy before a cached entry
|
|
2151
2179
|
// replaces its bundled peer; explicit user overrides still run afterward.
|
|
2152
2180
|
applyGeneratedModelPolicies(models, { preserveDiscoveredThinking });
|
|
2153
|
-
return models.map(
|
|
2181
|
+
return models.map(rawModel => {
|
|
2182
|
+
const model = applyCodexInteractionMetadata(rawModel);
|
|
2154
2183
|
if (model.id !== "gpt-5.4" || model.provider === "github-copilot") {
|
|
2155
2184
|
return model;
|
|
2156
2185
|
}
|
|
@@ -1116,6 +1116,20 @@ export function resolveCliModel(options: {
|
|
|
1116
1116
|
error: undefined,
|
|
1117
1117
|
};
|
|
1118
1118
|
}
|
|
1119
|
+
const thinkingSeparator = pattern.lastIndexOf(":");
|
|
1120
|
+
const exactModelId =
|
|
1121
|
+
thinkingSeparator !== -1 && parseThinkingLevel(pattern.slice(thinkingSeparator + 1))
|
|
1122
|
+
? pattern.slice(0, thinkingSeparator)
|
|
1123
|
+
: pattern;
|
|
1124
|
+
if (resolveProviderModelReference(provider, exactModelId, allModels)) {
|
|
1125
|
+
return {
|
|
1126
|
+
model: undefined,
|
|
1127
|
+
selector: undefined,
|
|
1128
|
+
thinkingLevel: undefined,
|
|
1129
|
+
warning: undefined,
|
|
1130
|
+
error: `Model "${provider}/${exactModelId}" is unavailable. Configure or sign in to provider "${provider}".`,
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1119
1133
|
}
|
|
1120
1134
|
|
|
1121
1135
|
const candidates = provider ? availableModels.filter(model => model.provider === provider) : availableModels;
|
|
@@ -2123,24 +2123,27 @@ export const SETTINGS_SCHEMA = {
|
|
|
2123
2123
|
},
|
|
2124
2124
|
},
|
|
2125
2125
|
|
|
2126
|
-
"providers.
|
|
2127
|
-
type: "
|
|
2128
|
-
|
|
2126
|
+
"providers.openaiContextTier": {
|
|
2127
|
+
type: "enum",
|
|
2128
|
+
values: ["standard", "codex-max", "provider-max"] as const,
|
|
2129
|
+
default: "standard",
|
|
2129
2130
|
ui: {
|
|
2130
2131
|
tab: "providers",
|
|
2131
|
-
label: "OpenAI
|
|
2132
|
-
description:
|
|
2133
|
-
|
|
2132
|
+
label: "OpenAI Context Tier",
|
|
2133
|
+
description: "Choose the standard, Codex maximum, or provider-advertised context window",
|
|
2134
|
+
submenu: true,
|
|
2134
2135
|
},
|
|
2135
2136
|
},
|
|
2136
2137
|
|
|
2137
|
-
"providers.
|
|
2138
|
-
type: "
|
|
2139
|
-
|
|
2138
|
+
"providers.litellmContextTier": {
|
|
2139
|
+
type: "enum",
|
|
2140
|
+
values: ["standard", "codex-max", "provider-max"] as const,
|
|
2141
|
+
default: "standard",
|
|
2140
2142
|
ui: {
|
|
2141
2143
|
tab: "providers",
|
|
2142
|
-
label: "LiteLLM
|
|
2143
|
-
description: "
|
|
2144
|
+
label: "LiteLLM Context Tier",
|
|
2145
|
+
description: "Choose the standard, Codex maximum, or live provider-advertised context window",
|
|
2146
|
+
submenu: true,
|
|
2144
2147
|
},
|
|
2145
2148
|
},
|
|
2146
2149
|
|
package/src/context/profile.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { getModelEffectiveContextWindow, type Model, type Usage } from "@f5-sales-demo/pi-ai";
|
|
2
2
|
|
|
3
3
|
export type ContextLoadingMode = "eager" | "progressive";
|
|
4
4
|
|
|
@@ -182,7 +182,7 @@ export function profileProviderPayload(payload: unknown, model: Model, call: num
|
|
|
182
182
|
const bytes = jsonBytes(message);
|
|
183
183
|
return { role: safeRole(message), bytes, estimatedTokens: estimateContextTokens(bytes) };
|
|
184
184
|
}),
|
|
185
|
-
contextWindow: model
|
|
185
|
+
contextWindow: getModelEffectiveContextWindow(model),
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { rmSync } from "node:fs";
|
|
3
|
+
import { mkdir, mkdtemp, rename, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
3
5
|
import path from "node:path";
|
|
4
6
|
import { gunzipSync } from "node:zlib";
|
|
5
7
|
import { createStore } from "@tobilu/qmd";
|
|
@@ -38,6 +40,17 @@ type QmdStore = Awaited<ReturnType<typeof createStore>>;
|
|
|
38
40
|
const qmdStores = new Map<string, Promise<QmdStore>>();
|
|
39
41
|
const verifiedIndexPaths = new Map<string, string>();
|
|
40
42
|
const indexExtractions = new Map<string, Promise<string>>();
|
|
43
|
+
const runtimeRoots = new Set<string>();
|
|
44
|
+
let runtimeCleanupRegistered = false;
|
|
45
|
+
|
|
46
|
+
function registerRuntimeRoot(root: string): void {
|
|
47
|
+
runtimeRoots.add(root);
|
|
48
|
+
if (runtimeCleanupRegistered) return;
|
|
49
|
+
runtimeCleanupRegistered = true;
|
|
50
|
+
process.once("exit", () => {
|
|
51
|
+
for (const runtimeRoot of runtimeRoots) rmSync(runtimeRoot, { recursive: true, force: true });
|
|
52
|
+
});
|
|
53
|
+
}
|
|
41
54
|
|
|
42
55
|
function indexCacheKey(options: QmdBm25CatalogDiscoveryOptions): string {
|
|
43
56
|
return `${options.cacheRoot}\u0000${options.prebuiltIndex.fingerprint}\u0000${options.prebuiltIndex.sqliteSha256}`;
|
|
@@ -46,7 +59,21 @@ function indexCacheKey(options: QmdBm25CatalogDiscoveryOptions): string {
|
|
|
46
59
|
async function getQmdStore(databasePath: string): Promise<QmdStore> {
|
|
47
60
|
let store = qmdStores.get(databasePath);
|
|
48
61
|
if (!store) {
|
|
49
|
-
store =
|
|
62
|
+
store = (async () => {
|
|
63
|
+
// QMD's FTS query path still needs a writable SQLite runtime. Search a
|
|
64
|
+
// disposable copy so the checksum-pinned cache stays immutable.
|
|
65
|
+
const runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "xcsh-api-catalog-runtime-"));
|
|
66
|
+
const runtimeDatabasePath = path.join(runtimeRoot, "index.sqlite");
|
|
67
|
+
try {
|
|
68
|
+
await Bun.write(runtimeDatabasePath, Bun.file(databasePath));
|
|
69
|
+
const runtimeStore = await createStore({ dbPath: runtimeDatabasePath });
|
|
70
|
+
registerRuntimeRoot(runtimeRoot);
|
|
71
|
+
return runtimeStore;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
await rm(runtimeRoot, { recursive: true, force: true });
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
50
77
|
qmdStores.set(databasePath, store);
|
|
51
78
|
}
|
|
52
79
|
try {
|