@oh-my-pi/pi-coding-agent 16.2.5 → 16.2.7
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/CHANGELOG.md +41 -1
- package/dist/cli.js +3089 -3104
- package/dist/types/cli/bench-cli.d.ts +6 -1
- package/dist/types/commands/bench.d.ts +8 -0
- package/dist/types/config/model-discovery.d.ts +6 -1
- package/dist/types/config/service-tier.d.ts +39 -24
- package/dist/types/config/settings-schema.d.ts +37 -37
- package/dist/types/edit/index.d.ts +1 -0
- package/dist/types/edit/renderer.d.ts +4 -0
- package/dist/types/edit/snapshot-details.d.ts +33 -0
- package/dist/types/mcp/config-writer.d.ts +48 -0
- package/dist/types/mcp/oauth-flow.d.ts +4 -6
- package/dist/types/mcp/types.d.ts +3 -0
- package/dist/types/modes/controllers/tool-args-reveal.d.ts +9 -2
- package/dist/types/session/agent-session.d.ts +37 -13
- package/dist/types/session/messages.d.ts +1 -1
- package/dist/types/session/session-context.d.ts +2 -2
- package/dist/types/session/session-entries.d.ts +2 -2
- package/dist/types/session/session-manager.d.ts +5 -6
- package/dist/types/system-prompt.test.d.ts +1 -0
- package/dist/types/task/executor.d.ts +6 -6
- package/dist/types/task/types.d.ts +6 -0
- package/dist/types/task/worktree.d.ts +32 -6
- package/dist/types/tiny/title-client.d.ts +5 -1
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/utils/git.d.ts +17 -0
- package/dist/types/web/search/providers/duckduckgo.d.ts +2 -2
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +12 -12
- package/src/cli/args.ts +32 -1
- package/src/cli/bench-cli.ts +97 -22
- package/src/cli/tiny-models-cli.ts +18 -4
- package/src/cli/web-search-cli.ts +6 -1
- package/src/commands/bench.ts +10 -1
- package/src/config/mcp-schema.json +11 -2
- package/src/config/model-discovery.ts +66 -8
- package/src/config/model-registry.ts +13 -6
- package/src/config/service-tier.ts +85 -56
- package/src/config/settings-schema.ts +42 -36
- package/src/config/settings.ts +47 -0
- package/src/edit/hashline/execute.ts +15 -9
- package/src/edit/index.ts +19 -6
- package/src/edit/modes/patch.ts +3 -2
- package/src/edit/modes/replace.ts +3 -2
- package/src/edit/renderer.ts +4 -0
- package/src/edit/snapshot-details.ts +77 -0
- package/src/eval/agent-bridge.ts +4 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +2 -2
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/main.ts +1 -1
- package/src/mcp/config-writer.ts +121 -0
- package/src/mcp/config.ts +10 -6
- package/src/mcp/oauth-flow.ts +10 -8
- package/src/mcp/transports/stdio.ts +9 -17
- package/src/mcp/types.ts +3 -0
- package/src/modes/components/extensions/extension-dashboard.ts +46 -0
- package/src/modes/components/extensions/state-manager.ts +24 -3
- package/src/modes/controllers/event-controller.ts +24 -8
- package/src/modes/controllers/tool-args-reveal.ts +100 -22
- package/src/modes/utils/transcript-render-helpers.ts +2 -2
- package/src/prompts/bench.md +4 -10
- package/src/prompts/tools/irc.md +19 -29
- package/src/prompts/tools/job.md +8 -14
- package/src/prompts/tools/lsp.md +19 -30
- package/src/prompts/tools/task.md +42 -62
- package/src/sdk.ts +13 -11
- package/src/session/agent-session.ts +196 -76
- package/src/session/messages.ts +1 -1
- package/src/session/session-context.ts +4 -4
- package/src/session/session-entries.ts +2 -2
- package/src/session/session-listing.ts +9 -8
- package/src/session/session-loader.ts +98 -3
- package/src/session/session-manager.ts +43 -6
- package/src/slash-commands/builtin-registry.ts +2 -10
- package/src/subprocess/worker-client.ts +12 -4
- package/src/system-prompt.test.ts +158 -0
- package/src/system-prompt.ts +69 -26
- package/src/task/executor.ts +23 -16
- package/src/task/index.ts +7 -5
- package/src/task/isolation-runner.ts +15 -1
- package/src/task/types.ts +6 -0
- package/src/task/worktree.ts +219 -38
- package/src/tiny/title-client.ts +19 -13
- package/src/tools/index.ts +3 -3
- package/src/tools/irc.ts +9 -3
- package/src/tools/path-utils.ts +4 -2
- package/src/tools/read.ts +28 -28
- package/src/utils/file-mentions.ts +10 -1
- package/src/utils/git.ts +38 -0
- package/src/web/search/index.ts +14 -8
- package/src/web/search/providers/duckduckgo.ts +150 -78
- package/src/web/search/providers/gemini.ts +268 -185
- package/src/web/search/types.ts +1 -1
package/src/commands/bench.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
|
|
2
2
|
import { runBenchCommand } from "../cli/bench-cli";
|
|
3
|
+
import { SERVICE_TIER_OPENAI_VALUES } from "../config/service-tier";
|
|
3
4
|
|
|
4
5
|
export default class Bench extends Command {
|
|
5
6
|
static description =
|
|
@@ -14,16 +15,22 @@ export default class Bench extends Command {
|
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
static flags = {
|
|
17
|
-
runs: Flags.integer({ description: "Requests per model (results are averaged)", default:
|
|
18
|
+
runs: Flags.integer({ description: "Requests per model (results are averaged)", default: 10 }),
|
|
18
19
|
"max-tokens": Flags.integer({ description: "Max output tokens per request", default: 512 }),
|
|
19
20
|
prompt: Flags.string({ description: "Custom prompt text (default: bundled bench prompt)" }),
|
|
21
|
+
"service-tier": Flags.string({
|
|
22
|
+
description: "Service tier applied per model family (default: configured `tier.*` settings; `none` omits it)",
|
|
23
|
+
options: SERVICE_TIER_OPENAI_VALUES,
|
|
24
|
+
}),
|
|
20
25
|
json: Flags.boolean({ description: "Output JSON" }),
|
|
26
|
+
par: Flags.integer({ description: "Execute runs with N parallel queries/requests", default: 4 }),
|
|
21
27
|
};
|
|
22
28
|
|
|
23
29
|
static examples = [
|
|
24
30
|
"# Compare two models\n omp bench anthropic/claude-opus-4-5 openai/gpt-5.2",
|
|
25
31
|
"# Fuzzy selectors work\n omp bench opus sonnet",
|
|
26
32
|
"# Average over 3 runs each\n omp bench opus gpt-5.2 --runs 3",
|
|
33
|
+
"# Force priority serving tier\n omp bench openai-codex/gpt-5.5:low --runs 10 --service-tier priority",
|
|
27
34
|
"# Machine-readable output\n omp bench opus --json",
|
|
28
35
|
];
|
|
29
36
|
|
|
@@ -35,7 +42,9 @@ export default class Bench extends Command {
|
|
|
35
42
|
runs: flags.runs,
|
|
36
43
|
maxTokens: flags["max-tokens"],
|
|
37
44
|
prompt: flags.prompt,
|
|
45
|
+
serviceTier: flags["service-tier"],
|
|
38
46
|
json: flags.json,
|
|
47
|
+
par: flags.par,
|
|
39
48
|
},
|
|
40
49
|
});
|
|
41
50
|
}
|
|
@@ -22,7 +22,16 @@
|
|
|
22
22
|
},
|
|
23
23
|
"disabledServers": {
|
|
24
24
|
"type": "array",
|
|
25
|
-
"description": "User-level denylist for disabling discovered servers by name.",
|
|
25
|
+
"description": "User-level denylist for disabling discovered servers by name. Highest precedence: a server here is hidden regardless of any other source.",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"minLength": 1
|
|
29
|
+
},
|
|
30
|
+
"uniqueItems": true
|
|
31
|
+
},
|
|
32
|
+
"enabledServers": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"description": "User-level allowlist that overrides a discovered server's `enabled: false` flag (e.g. when the source config is owned by another tool such as opencode.json). The denylist still wins.",
|
|
26
35
|
"items": {
|
|
27
36
|
"type": "string",
|
|
28
37
|
"minLength": 1
|
|
@@ -92,7 +101,7 @@
|
|
|
92
101
|
},
|
|
93
102
|
"prompt": {
|
|
94
103
|
"type": "string",
|
|
95
|
-
"description": "OAuth `prompt` parameter sent during authorization (default:
|
|
104
|
+
"description": "OAuth `prompt` parameter sent during authorization (default: omit unless the requested scope contains `offline_access`, which sends \"consent\"; set to \"\" to always omit)."
|
|
96
105
|
}
|
|
97
106
|
},
|
|
98
107
|
"description": "Explicit OAuth client settings for servers that need them during /mcp reauth or initial connect."
|
|
@@ -122,6 +122,12 @@ type OllamaDiscoveredModelMetadata = {
|
|
|
122
122
|
type LlamaCppDiscoveredServerMetadata = {
|
|
123
123
|
contextWindow?: number;
|
|
124
124
|
input?: ("text" | "image")[];
|
|
125
|
+
maxTokens?: "contextWindow";
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
type LlamaCppDiscoveredModelRuntimeMetadata = {
|
|
129
|
+
contextWindow: number;
|
|
130
|
+
maxTokens: number;
|
|
125
131
|
};
|
|
126
132
|
|
|
127
133
|
type LlamaCppModelListEntry = {
|
|
@@ -143,6 +149,44 @@ function toPositiveNumberOrUndefined(value: unknown): number | undefined {
|
|
|
143
149
|
return undefined;
|
|
144
150
|
}
|
|
145
151
|
|
|
152
|
+
function isLlamaCppUnlimitedSentinel(value: unknown): boolean {
|
|
153
|
+
if (typeof value === "number") {
|
|
154
|
+
return value === -1;
|
|
155
|
+
}
|
|
156
|
+
if (typeof value === "string" && value.trim()) {
|
|
157
|
+
return Number(value) === -1;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* llama.cpp `/props.default_generation_settings.params.{max_tokens,n_predict}`
|
|
164
|
+
* are per-request defaults the server applies when a client omits the field —
|
|
165
|
+
* clients can still raise them per call. Positive values therefore are NOT
|
|
166
|
+
* hard model caps; only the `-1` unlimited sentinel reliably tells us the
|
|
167
|
+
* server bounds generation by the runtime context window. Anything else
|
|
168
|
+
* leaves the discovery default in place.
|
|
169
|
+
*/
|
|
170
|
+
function extractLlamaCppMaxTokens(payload: Record<string, unknown>): "contextWindow" | undefined {
|
|
171
|
+
const generationSettings = payload.default_generation_settings;
|
|
172
|
+
const params = isRecord(generationSettings) ? generationSettings.params : undefined;
|
|
173
|
+
const candidates = [
|
|
174
|
+
isRecord(params) ? params.max_tokens : undefined,
|
|
175
|
+
isRecord(params) ? params.n_predict : undefined,
|
|
176
|
+
isRecord(generationSettings) ? generationSettings.max_tokens : undefined,
|
|
177
|
+
isRecord(generationSettings) ? generationSettings.n_predict : undefined,
|
|
178
|
+
payload.max_tokens,
|
|
179
|
+
payload.n_predict,
|
|
180
|
+
];
|
|
181
|
+
return candidates.some(isLlamaCppUnlimitedSentinel) ? "contextWindow" : undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function resolveLlamaCppMaxTokens(contextWindow: number, maxTokens: "contextWindow" | undefined): number {
|
|
185
|
+
return maxTokens === "contextWindow"
|
|
186
|
+
? contextWindow
|
|
187
|
+
: Math.min(contextWindow, maxTokens ?? DISCOVERY_DEFAULT_MAX_TOKENS);
|
|
188
|
+
}
|
|
189
|
+
|
|
146
190
|
function extractOllamaRuntimeContextWindow(payload: Record<string, unknown>): number | undefined {
|
|
147
191
|
const parameters = payload.parameters;
|
|
148
192
|
if (typeof parameters !== "string") {
|
|
@@ -353,6 +397,7 @@ async function discoverLlamaCppServerMetadata(
|
|
|
353
397
|
}
|
|
354
398
|
return {
|
|
355
399
|
contextWindow: extractLlamaCppContextWindow(payload),
|
|
400
|
+
maxTokens: extractLlamaCppMaxTokens(payload),
|
|
356
401
|
input: extractLlamaCppInputCapabilities(payload),
|
|
357
402
|
};
|
|
358
403
|
} catch {
|
|
@@ -410,7 +455,7 @@ export async function discoverLlamaCppModels(
|
|
|
410
455
|
imageInputDecoder: "stb",
|
|
411
456
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
412
457
|
contextWindow,
|
|
413
|
-
maxTokens:
|
|
458
|
+
maxTokens: resolveLlamaCppMaxTokens(contextWindow, serverMetadata?.maxTokens),
|
|
414
459
|
headers,
|
|
415
460
|
compat: {
|
|
416
461
|
supportsStore: false,
|
|
@@ -423,24 +468,37 @@ export async function discoverLlamaCppModels(
|
|
|
423
468
|
return discovered;
|
|
424
469
|
}
|
|
425
470
|
|
|
426
|
-
export async function
|
|
471
|
+
export async function discoverLlamaCppModelRuntimeMetadata(
|
|
427
472
|
model: Pick<Model<Api>, "provider" | "id" | "baseUrl" | "headers">,
|
|
428
473
|
ctx: DiscoveryContext,
|
|
429
|
-
): Promise<
|
|
474
|
+
): Promise<LlamaCppDiscoveredModelRuntimeMetadata | undefined> {
|
|
430
475
|
const baseUrl = normalizeLlamaCppBaseUrl(model.baseUrl);
|
|
431
476
|
const modelsUrl = `${baseUrl}/models`;
|
|
432
477
|
const baseHeaders: Record<string, string> = { ...(model.headers ?? {}) };
|
|
433
478
|
const attempt = async (headers: Record<string, string>) => {
|
|
434
|
-
const response = await
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
479
|
+
const [response, serverMetadata] = await Promise.all([
|
|
480
|
+
ctx.fetch(modelsUrl, {
|
|
481
|
+
headers,
|
|
482
|
+
signal: AbortSignal.timeout(250),
|
|
483
|
+
}),
|
|
484
|
+
discoverLlamaCppServerMetadata(ctx, baseUrl, headers),
|
|
485
|
+
]);
|
|
438
486
|
if (!response.ok) {
|
|
439
487
|
return undefined;
|
|
440
488
|
}
|
|
441
489
|
const entries = parseLlamaCppModelList(await response.json());
|
|
442
490
|
const entry = entries.find(entry => entry.id === model.id);
|
|
443
|
-
|
|
491
|
+
if (!entry) {
|
|
492
|
+
return undefined;
|
|
493
|
+
}
|
|
494
|
+
const contextWindow = entry.runtimeContextWindow ?? serverMetadata?.contextWindow ?? entry.trainingContextWindow;
|
|
495
|
+
if (contextWindow === undefined) {
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
return {
|
|
499
|
+
contextWindow,
|
|
500
|
+
maxTokens: resolveLlamaCppMaxTokens(contextWindow, serverMetadata?.maxTokens),
|
|
501
|
+
};
|
|
444
502
|
};
|
|
445
503
|
try {
|
|
446
504
|
const apiKey = await ctx.getBearerApiKeyResolver(model.provider);
|
|
@@ -75,7 +75,7 @@ import {
|
|
|
75
75
|
DISCOVERY_DEFAULT_MAX_TOKENS,
|
|
76
76
|
type DiscoveryContext,
|
|
77
77
|
type DiscoveryProviderConfig,
|
|
78
|
-
|
|
78
|
+
discoverLlamaCppModelRuntimeMetadata,
|
|
79
79
|
discoverModelsByProviderType,
|
|
80
80
|
getImplicitOllamaBaseUrl,
|
|
81
81
|
getOllamaContextLengthOverride,
|
|
@@ -873,10 +873,11 @@ export class ModelRegistry {
|
|
|
873
873
|
if (!isLlamaCppDiscovery) {
|
|
874
874
|
return model;
|
|
875
875
|
}
|
|
876
|
-
const
|
|
877
|
-
if (
|
|
876
|
+
const runtimeMetadata = await discoverLlamaCppModelRuntimeMetadata(model, this.#nonResolvingDiscoveryContext());
|
|
877
|
+
if (runtimeMetadata === undefined) {
|
|
878
878
|
return this.find(model.provider, model.id) ?? model;
|
|
879
879
|
}
|
|
880
|
+
const { contextWindow, maxTokens } = runtimeMetadata;
|
|
880
881
|
const current = this.find(model.provider, model.id) ?? model;
|
|
881
882
|
const override = this.#resolveLiveModelOverride(current);
|
|
882
883
|
const customModel = this.#resolveLiveCustomModelOverlay(current);
|
|
@@ -888,13 +889,19 @@ export class ModelRegistry {
|
|
|
888
889
|
) {
|
|
889
890
|
patch.contextWindow = contextWindow;
|
|
890
891
|
}
|
|
891
|
-
const
|
|
892
|
+
const effectiveContextWindow =
|
|
893
|
+
override?.contextWindow ??
|
|
894
|
+
customModel?.contextWindow ??
|
|
895
|
+
patch.contextWindow ??
|
|
896
|
+
current.contextWindow ??
|
|
897
|
+
contextWindow;
|
|
898
|
+
const effectiveMaxTokens = Math.min(maxTokens, effectiveContextWindow);
|
|
892
899
|
if (
|
|
893
900
|
override?.maxTokens === undefined &&
|
|
894
901
|
customModel?.maxTokens === undefined &&
|
|
895
|
-
current.maxTokens !==
|
|
902
|
+
current.maxTokens !== effectiveMaxTokens
|
|
896
903
|
) {
|
|
897
|
-
patch.maxTokens =
|
|
904
|
+
patch.maxTokens = effectiveMaxTokens;
|
|
898
905
|
}
|
|
899
906
|
if (patch.contextWindow === undefined && patch.maxTokens === undefined) {
|
|
900
907
|
return current;
|
|
@@ -1,87 +1,116 @@
|
|
|
1
|
-
import type { ServiceTier } from "@oh-my-pi/pi-ai";
|
|
1
|
+
import type { ServiceTier, ServiceTierByFamily } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import type { SubmenuOption } from "./settings-schema";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Per-family service-tier setting values. `"none"` is the omit-the-parameter
|
|
6
|
+
* sentinel; the rest mirror the wire {@link ServiceTier} values each provider
|
|
7
|
+
* family actually realizes. OpenAI accepts the full set; Anthropic realizes
|
|
8
|
+
* only `priority` (fast mode); Google (Gemini API + Vertex) realizes
|
|
9
|
+
* `flex`/`priority`.
|
|
8
10
|
*/
|
|
9
|
-
export const
|
|
11
|
+
export const SERVICE_TIER_OPENAI_VALUES = ["none", "auto", "default", "flex", "scale", "priority"] as const;
|
|
12
|
+
export const SERVICE_TIER_ANTHROPIC_VALUES = ["none", "priority"] as const;
|
|
13
|
+
export const SERVICE_TIER_GOOGLE_VALUES = ["none", "flex", "priority"] as const;
|
|
14
|
+
|
|
15
|
+
export type ServiceTierOpenAISettingValue = (typeof SERVICE_TIER_OPENAI_VALUES)[number];
|
|
16
|
+
export type ServiceTierAnthropicSettingValue = (typeof SERVICE_TIER_ANTHROPIC_VALUES)[number];
|
|
17
|
+
export type ServiceTierGoogleSettingValue = (typeof SERVICE_TIER_GOOGLE_VALUES)[number];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Inherit-capable single value for the subagent/advisor tiers. The chosen tier
|
|
21
|
+
* is broadcast across families and applied to whichever family the spawned
|
|
22
|
+
* model belongs to (clamped to what that family realizes); `"inherit"` defers
|
|
23
|
+
* to the main agent's live per-family selection.
|
|
24
|
+
*/
|
|
25
|
+
export const SERVICE_TIER_INHERIT_SETTING_VALUES = [
|
|
26
|
+
"inherit",
|
|
10
27
|
"none",
|
|
11
28
|
"auto",
|
|
12
29
|
"default",
|
|
13
30
|
"flex",
|
|
14
31
|
"scale",
|
|
15
32
|
"priority",
|
|
16
|
-
"openai-only",
|
|
17
|
-
"claude-only",
|
|
18
33
|
] as const;
|
|
19
34
|
|
|
20
|
-
export type ServiceTierSettingValue = (typeof SERVICE_TIER_SETTING_VALUES)[number];
|
|
21
|
-
|
|
22
|
-
/** Variant value set for scoped service-tier settings (subagent/advisor) that can defer to the main agent. */
|
|
23
|
-
export const SERVICE_TIER_INHERIT_SETTING_VALUES = ["inherit", ...SERVICE_TIER_SETTING_VALUES] as const;
|
|
24
|
-
|
|
25
35
|
export type ServiceTierInheritSettingValue = (typeof SERVICE_TIER_INHERIT_SETTING_VALUES)[number];
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{ value: "
|
|
30
|
-
{ value: "
|
|
31
|
-
{ value: "
|
|
32
|
-
{ value: "
|
|
33
|
-
{ value: "
|
|
37
|
+
export const SERVICE_TIER_OPENAI_OPTIONS: ReadonlyArray<SubmenuOption<ServiceTierOpenAISettingValue>> = [
|
|
38
|
+
{ value: "none", label: "None", description: "Omit service_tier (standard processing)" },
|
|
39
|
+
{ value: "auto", label: "Auto", description: "Provider default tier selection" },
|
|
40
|
+
{ value: "default", label: "Default", description: "Standard priority processing" },
|
|
41
|
+
{ value: "flex", label: "Flex", description: "Lower cost, higher latency when available" },
|
|
42
|
+
{ value: "scale", label: "Scale", description: "Scale Tier credits when available" },
|
|
43
|
+
{ value: "priority", label: "Priority", description: "Faster, higher cost (premium request)" },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
export const SERVICE_TIER_ANTHROPIC_OPTIONS: ReadonlyArray<SubmenuOption<ServiceTierAnthropicSettingValue>> = [
|
|
47
|
+
{ value: "none", label: "None", description: "Standard processing" },
|
|
34
48
|
{
|
|
35
49
|
value: "priority",
|
|
36
50
|
label: "Priority",
|
|
37
|
-
description: "
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
value: "openai-only",
|
|
41
|
-
label: "Priority (OpenAI only)",
|
|
42
|
-
description: "Priority on OpenAI/OpenAI-Codex requests; ignored elsewhere",
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
value: "claude-only",
|
|
46
|
-
label: "Priority (Claude only)",
|
|
47
|
-
description: "Anthropic fast mode on direct Claude requests; ignored elsewhere (incl. Bedrock/Vertex)",
|
|
51
|
+
description: 'Fast mode (`speed: "fast"`) on supported direct Claude models; ignored on Bedrock/Vertex',
|
|
48
52
|
},
|
|
49
53
|
];
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
export const SERVICE_TIER_GOOGLE_OPTIONS: ReadonlyArray<SubmenuOption<ServiceTierGoogleSettingValue>> = [
|
|
56
|
+
{ value: "none", label: "None", description: "Standard processing" },
|
|
57
|
+
{ value: "flex", label: "Flex", description: "Lower cost, higher latency (Gemini API + Vertex)" },
|
|
58
|
+
{ value: "priority", label: "Priority", description: "Faster, higher reliability (Gemini API + Vertex)" },
|
|
59
|
+
];
|
|
60
|
+
|
|
52
61
|
export const SERVICE_TIER_INHERIT_OPTIONS: ReadonlyArray<SubmenuOption<ServiceTierInheritSettingValue>> = [
|
|
53
|
-
{ value: "inherit", label: "Inherit", description: "
|
|
54
|
-
|
|
62
|
+
{ value: "inherit", label: "Inherit", description: "Match the main agent's live per-family tiers" },
|
|
63
|
+
{ value: "none", label: "None", description: "Standard processing" },
|
|
64
|
+
{ value: "auto", label: "Auto", description: "Provider default tier selection (OpenAI family)" },
|
|
65
|
+
{ value: "default", label: "Default", description: "Standard priority processing (OpenAI family)" },
|
|
66
|
+
{ value: "flex", label: "Flex", description: "Flexible capacity tier (OpenAI/Google families)" },
|
|
67
|
+
{ value: "scale", label: "Scale", description: "Scale Tier credits (OpenAI family)" },
|
|
68
|
+
{ value: "priority", label: "Priority", description: "Priority on every supported family of the spawned model" },
|
|
55
69
|
];
|
|
56
70
|
|
|
71
|
+
/** Map a per-family setting value to a wire {@link ServiceTier}, or `undefined` to omit. */
|
|
72
|
+
export function serviceTierSettingToTier(value: string): ServiceTier | undefined {
|
|
73
|
+
if (value === "none" || value === "" || value === "inherit") return undefined;
|
|
74
|
+
return value as ServiceTier;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Assemble the live per-family tier map from the three `tier.*` setting values. */
|
|
78
|
+
export function buildServiceTierByFamily(openai: string, anthropic: string, google: string): ServiceTierByFamily {
|
|
79
|
+
const out: ServiceTierByFamily = {};
|
|
80
|
+
const o = serviceTierSettingToTier(openai);
|
|
81
|
+
if (o) out.openai = o;
|
|
82
|
+
const a = serviceTierSettingToTier(anthropic);
|
|
83
|
+
if (a) out.anthropic = a;
|
|
84
|
+
const g = serviceTierSettingToTier(google);
|
|
85
|
+
if (g) out.google = g;
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
57
89
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
90
|
+
* Broadcast a single chosen tier across families, clamped to what each family
|
|
91
|
+
* realizes: OpenAI takes any tier, Anthropic only `priority`, Google only
|
|
92
|
+
* `flex`/`priority`. Used by the subagent/advisor single-value settings and the
|
|
93
|
+
* `omp bench --service-tier` flag, which apply one tier to whatever family the
|
|
94
|
+
* target model belongs to.
|
|
60
95
|
*/
|
|
61
|
-
export function
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
96
|
+
export function serviceTierForAllFamilies(tier: ServiceTier | undefined): ServiceTierByFamily {
|
|
97
|
+
if (!tier) return {};
|
|
98
|
+
const out: ServiceTierByFamily = { openai: tier };
|
|
99
|
+
if (tier === "priority") out.anthropic = "priority";
|
|
100
|
+
if (tier === "flex" || tier === "priority") out.google = tier;
|
|
101
|
+
return out;
|
|
65
102
|
}
|
|
66
103
|
|
|
67
104
|
/**
|
|
68
|
-
* Resolve
|
|
69
|
-
* snapshot.
|
|
105
|
+
* Resolve a subagent/advisor service-tier setting to a per-family map.
|
|
70
106
|
*
|
|
71
|
-
* - A concrete
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* back to the parent's configured `serviceTier` setting so behavior matches a
|
|
77
|
-
* plain settings snapshot.
|
|
107
|
+
* - A concrete tier is broadcast across families (see
|
|
108
|
+
* {@link serviceTierForAllFamilies}).
|
|
109
|
+
* - `"none"` yields an empty map.
|
|
110
|
+
* - `"inherit"` defers to `inherited` — the parent's live per-family tiers when
|
|
111
|
+
* a live session supplied them, else the empty map.
|
|
78
112
|
*/
|
|
79
|
-
export function resolveSubagentServiceTier(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
inherited: ServiceTier | null | undefined,
|
|
83
|
-
): ServiceTierSettingValue {
|
|
84
|
-
if (subagentSetting !== "inherit") return subagentSetting as ServiceTierSettingValue;
|
|
85
|
-
if (inherited === undefined) return configuredTier;
|
|
86
|
-
return inherited ?? "none";
|
|
113
|
+
export function resolveSubagentServiceTier(setting: string, inherited: ServiceTierByFamily): ServiceTierByFamily {
|
|
114
|
+
if (setting === "inherit") return inherited;
|
|
115
|
+
return serviceTierForAllFamilies(serviceTierSettingToTier(setting));
|
|
87
116
|
}
|
|
@@ -36,10 +36,14 @@ import {
|
|
|
36
36
|
import { EDIT_MODES } from "../utils/edit-mode";
|
|
37
37
|
import { SEARCH_PROVIDER_OPTIONS, SEARCH_PROVIDER_PREFERENCES, type SearchProviderId } from "../web/search/types";
|
|
38
38
|
import {
|
|
39
|
+
SERVICE_TIER_ANTHROPIC_OPTIONS,
|
|
40
|
+
SERVICE_TIER_ANTHROPIC_VALUES,
|
|
41
|
+
SERVICE_TIER_GOOGLE_OPTIONS,
|
|
42
|
+
SERVICE_TIER_GOOGLE_VALUES,
|
|
39
43
|
SERVICE_TIER_INHERIT_OPTIONS,
|
|
40
44
|
SERVICE_TIER_INHERIT_SETTING_VALUES,
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
SERVICE_TIER_OPENAI_OPTIONS,
|
|
46
|
+
SERVICE_TIER_OPENAI_VALUES,
|
|
43
47
|
} from "./service-tier";
|
|
44
48
|
|
|
45
49
|
/** Unified settings schema - single source of truth for all settings.
|
|
@@ -1214,72 +1218,74 @@ export const SETTINGS_SCHEMA = {
|
|
|
1214
1218
|
},
|
|
1215
1219
|
},
|
|
1216
1220
|
|
|
1217
|
-
|
|
1221
|
+
"tier.openai": {
|
|
1218
1222
|
type: "enum",
|
|
1219
|
-
values:
|
|
1223
|
+
values: SERVICE_TIER_OPENAI_VALUES,
|
|
1220
1224
|
default: "none",
|
|
1221
1225
|
ui: {
|
|
1222
1226
|
tab: "model",
|
|
1223
1227
|
group: "Sampling",
|
|
1224
|
-
label: "Service Tier",
|
|
1228
|
+
label: "Service Tier — OpenAI",
|
|
1225
1229
|
description:
|
|
1226
|
-
|
|
1227
|
-
options:
|
|
1230
|
+
"Processing tier for OpenAI / OpenAI-Codex requests, and OpenAI-family models routed via OpenRouter (none = omit). Sent as `service_tier`.",
|
|
1231
|
+
options: SERVICE_TIER_OPENAI_OPTIONS,
|
|
1228
1232
|
},
|
|
1229
1233
|
},
|
|
1230
1234
|
|
|
1231
|
-
|
|
1235
|
+
"tier.anthropic": {
|
|
1232
1236
|
type: "enum",
|
|
1233
|
-
values:
|
|
1234
|
-
default: "
|
|
1237
|
+
values: SERVICE_TIER_ANTHROPIC_VALUES,
|
|
1238
|
+
default: "none",
|
|
1235
1239
|
ui: {
|
|
1236
1240
|
tab: "model",
|
|
1237
1241
|
group: "Sampling",
|
|
1238
|
-
label: "Service Tier
|
|
1242
|
+
label: "Service Tier — Anthropic",
|
|
1239
1243
|
description:
|
|
1240
|
-
|
|
1241
|
-
options:
|
|
1244
|
+
'Processing tier for Claude requests. `priority` realizes fast mode (`speed: "fast"`) on supported direct Anthropic models; ignored on Bedrock/Vertex Claude and via OpenRouter.',
|
|
1245
|
+
options: SERVICE_TIER_ANTHROPIC_OPTIONS,
|
|
1242
1246
|
},
|
|
1243
1247
|
},
|
|
1244
1248
|
|
|
1245
|
-
|
|
1249
|
+
"tier.google": {
|
|
1246
1250
|
type: "enum",
|
|
1247
|
-
values:
|
|
1251
|
+
values: SERVICE_TIER_GOOGLE_VALUES,
|
|
1248
1252
|
default: "none",
|
|
1249
1253
|
ui: {
|
|
1250
1254
|
tab: "model",
|
|
1251
1255
|
group: "Sampling",
|
|
1252
|
-
label: "Service Tier
|
|
1256
|
+
label: "Service Tier — Google",
|
|
1257
|
+
description:
|
|
1258
|
+
"Processing tier for Gemini (Google AI Studio + Vertex) requests, and Google-family models routed via OpenRouter (none = omit). Sent as the top-level `serviceTier` field.",
|
|
1259
|
+
options: SERVICE_TIER_GOOGLE_OPTIONS,
|
|
1260
|
+
},
|
|
1261
|
+
},
|
|
1262
|
+
|
|
1263
|
+
"tier.subagent": {
|
|
1264
|
+
type: "enum",
|
|
1265
|
+
values: SERVICE_TIER_INHERIT_SETTING_VALUES,
|
|
1266
|
+
default: "inherit",
|
|
1267
|
+
ui: {
|
|
1268
|
+
tab: "model",
|
|
1269
|
+
group: "Sampling",
|
|
1270
|
+
label: "Service Tier — Subagent",
|
|
1253
1271
|
description:
|
|
1254
|
-
"Service Tier for
|
|
1272
|
+
"Service Tier for spawned task/eval subagents. Inherit = match the main agent's live per-family tiers (tracks /fast); pick a value to apply it to whichever family the subagent's model belongs to.",
|
|
1255
1273
|
options: SERVICE_TIER_INHERIT_OPTIONS,
|
|
1256
|
-
condition: "advisorEnabled",
|
|
1257
1274
|
},
|
|
1258
1275
|
},
|
|
1259
1276
|
|
|
1260
|
-
|
|
1277
|
+
"tier.advisor": {
|
|
1261
1278
|
type: "enum",
|
|
1262
|
-
values:
|
|
1263
|
-
default: "
|
|
1279
|
+
values: SERVICE_TIER_INHERIT_SETTING_VALUES,
|
|
1280
|
+
default: "none",
|
|
1264
1281
|
ui: {
|
|
1265
1282
|
tab: "model",
|
|
1266
1283
|
group: "Sampling",
|
|
1267
|
-
label: "
|
|
1284
|
+
label: "Service Tier — Advisor",
|
|
1268
1285
|
description:
|
|
1269
|
-
|
|
1270
|
-
options:
|
|
1271
|
-
|
|
1272
|
-
{
|
|
1273
|
-
value: "openai",
|
|
1274
|
-
label: "OpenAI only",
|
|
1275
|
-
description: "Priority on OpenAI/OpenAI-Codex requests; ignored elsewhere",
|
|
1276
|
-
},
|
|
1277
|
-
{
|
|
1278
|
-
value: "claude",
|
|
1279
|
-
label: "Claude only",
|
|
1280
|
-
description: "Anthropic fast mode on direct Claude requests; ignored elsewhere",
|
|
1281
|
-
},
|
|
1282
|
-
],
|
|
1286
|
+
"Service Tier for the advisor model. None = standard processing; Inherit = match the main agent's live per-family tiers; pick a value to apply it to the advisor model's family.",
|
|
1287
|
+
options: SERVICE_TIER_INHERIT_OPTIONS,
|
|
1288
|
+
condition: "advisorEnabled",
|
|
1283
1289
|
},
|
|
1284
1290
|
},
|
|
1285
1291
|
|
package/src/config/settings.ts
CHANGED
|
@@ -1148,6 +1148,53 @@ export class Settings {
|
|
|
1148
1148
|
// the incoherent "hashline edits without addressable anchors" state.
|
|
1149
1149
|
delete raw.readHashLines;
|
|
1150
1150
|
|
|
1151
|
+
// serviceTier (single enum with scoped openai-only/claude-only sentinels)
|
|
1152
|
+
// → per-family tier.openai/tier.anthropic/tier.google; serviceTierSubagent
|
|
1153
|
+
// → tier.subagent; serviceTierAdvisor → tier.advisor. `fastModeScope` is
|
|
1154
|
+
// dropped — per-family scoping is now expressed by the three tier settings.
|
|
1155
|
+
const tierObj = isRecord(raw.tier) ? raw.tier : {};
|
|
1156
|
+
let tierTouched = false;
|
|
1157
|
+
const setTier = (family: string, value: unknown): void => {
|
|
1158
|
+
if (value !== undefined && !(family in tierObj)) {
|
|
1159
|
+
tierObj[family] = value;
|
|
1160
|
+
tierTouched = true;
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
if (typeof raw.serviceTier === "string") {
|
|
1164
|
+
switch (raw.serviceTier) {
|
|
1165
|
+
case "priority":
|
|
1166
|
+
setTier("openai", "priority");
|
|
1167
|
+
setTier("anthropic", "priority");
|
|
1168
|
+
setTier("google", "priority");
|
|
1169
|
+
break;
|
|
1170
|
+
case "openai-only":
|
|
1171
|
+
setTier("openai", "priority");
|
|
1172
|
+
break;
|
|
1173
|
+
case "claude-only":
|
|
1174
|
+
setTier("anthropic", "priority");
|
|
1175
|
+
break;
|
|
1176
|
+
case "auto":
|
|
1177
|
+
case "default":
|
|
1178
|
+
case "flex":
|
|
1179
|
+
case "scale":
|
|
1180
|
+
setTier("openai", raw.serviceTier);
|
|
1181
|
+
break;
|
|
1182
|
+
}
|
|
1183
|
+
delete raw.serviceTier;
|
|
1184
|
+
}
|
|
1185
|
+
const mapInheritTier = (value: unknown): unknown =>
|
|
1186
|
+
value === "openai-only" || value === "claude-only" ? "priority" : value;
|
|
1187
|
+
if ("serviceTierSubagent" in raw) {
|
|
1188
|
+
setTier("subagent", mapInheritTier(raw.serviceTierSubagent));
|
|
1189
|
+
delete raw.serviceTierSubagent;
|
|
1190
|
+
}
|
|
1191
|
+
if ("serviceTierAdvisor" in raw) {
|
|
1192
|
+
setTier("advisor", mapInheritTier(raw.serviceTierAdvisor));
|
|
1193
|
+
delete raw.serviceTierAdvisor;
|
|
1194
|
+
}
|
|
1195
|
+
if (tierTouched) raw.tier = tierObj;
|
|
1196
|
+
delete raw.fastModeScope;
|
|
1197
|
+
|
|
1151
1198
|
return raw;
|
|
1152
1199
|
}
|
|
1153
1200
|
|