feat(ai): add max thinking level
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added the opt-in `max` thinking level across CLI, SDK, RPC, model selection, and themes. Custom themes can define `thinkingMax`; existing themes fall back to `thinkingXhigh`.
|
||||
|
||||
## [0.80.5] - 2026-07-09
|
||||
|
||||
## [0.80.4] - 2026-07-09
|
||||
|
||||
@@ -551,7 +551,7 @@ cat README.md | pi -p "Summarize this text"
|
||||
| `--provider <name>` | Provider (anthropic, openai, google, etc.) |
|
||||
| `--model <pattern>` | Model pattern or ID (supports `provider/id` and optional `:<thinking>`) |
|
||||
| `--api-key <key>` | API key (overrides env vars) |
|
||||
| `--thinking <level>` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh` |
|
||||
| `--thinking <level>` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` |
|
||||
| `--models <patterns>` | Comma-separated patterns for Ctrl+P cycling |
|
||||
| `--list-models [search]` | List available models |
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ The `api` field determines which streaming implementation is used:
|
||||
| `google-vertex` | Google Vertex AI API |
|
||||
| `bedrock-converse-stream` | Amazon Bedrock Converse API |
|
||||
|
||||
Most OpenAI-compatible providers work with `openai-completions`. Use model-level `thinkingLevelMap` for model-specific thinking levels, and `compat` for provider quirks:
|
||||
Most OpenAI-compatible providers work with `openai-completions`. Use model-level `thinkingLevelMap` for model-specific thinking levels, and `compat` for provider quirks. The `xhigh` and `max` levels are opt-in, require non-null map entries, and may be separated by unsupported holes:
|
||||
|
||||
```typescript
|
||||
models: [{
|
||||
@@ -216,7 +216,8 @@ models: [{
|
||||
low: null,
|
||||
medium: null,
|
||||
high: "default",
|
||||
xhigh: "max"
|
||||
xhigh: null,
|
||||
max: "max"
|
||||
},
|
||||
compat: {
|
||||
supportsDeveloperRole: false, // use "system" instead of "developer"
|
||||
@@ -684,7 +685,7 @@ interface ProviderModelConfig {
|
||||
reasoning: boolean;
|
||||
|
||||
/** Maps pi thinking levels to provider/model-specific values; null marks a level unsupported. */
|
||||
thinkingLevelMap?: Partial<Record<"off" | "minimal" | "low" | "medium" | "high" | "xhigh", string | null>>;
|
||||
thinkingLevelMap?: Partial<Record<"off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", string | null>>;
|
||||
|
||||
/** Supported input types. */
|
||||
input: ("text" | "image")[];
|
||||
|
||||
@@ -1657,7 +1657,7 @@ if (model) {
|
||||
Get or set the thinking level. Level is clamped to model capabilities (non-reasoning models always use "off"). Changes emit `thinking_level_select`.
|
||||
|
||||
```typescript
|
||||
const current = pi.getThinkingLevel(); // "off" | "minimal" | "low" | "medium" | "high" | "xhigh"
|
||||
const current = pi.getThinkingLevel(); // "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
|
||||
pi.setThinkingLevel("high");
|
||||
```
|
||||
|
||||
|
||||
@@ -214,13 +214,13 @@ Current behavior:
|
||||
|
||||
### Thinking Level Map
|
||||
|
||||
Use `thinkingLevelMap` on a model to describe model-specific thinking controls. Keys are pi thinking levels: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`.
|
||||
Use `thinkingLevelMap` on a model to describe model-specific thinking controls. Keys are pi thinking levels: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Maps may contain holes; for example, a model can expose `high` and `max` without exposing `xhigh`.
|
||||
|
||||
Values are tristate:
|
||||
|
||||
| Value | Meaning |
|
||||
|-------|---------|
|
||||
| omitted | Level is supported and uses the provider's default mapping |
|
||||
| omitted | Standard levels through `high` use the provider's default mapping; extended `xhigh` and `max` levels are unsupported |
|
||||
| string | Level is supported and this value is sent to the provider |
|
||||
| `null` | Level is unsupported and hidden/skipped/clamped away |
|
||||
|
||||
@@ -235,7 +235,8 @@ Example for a model that only supports off, high, and max reasoning:
|
||||
"low": null,
|
||||
"medium": null,
|
||||
"high": "high",
|
||||
"xhigh": "max"
|
||||
"xhigh": null,
|
||||
"max": "max"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -286,9 +286,9 @@ Set the reasoning/thinking level for models that support it.
|
||||
{"type": "set_thinking_level", "level": "high"}
|
||||
```
|
||||
|
||||
Levels: `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`
|
||||
Levels: `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"`
|
||||
|
||||
Note: `"xhigh"` is only supported by OpenAI codex-max models.
|
||||
`"xhigh"` and `"max"` are exposed only when supported by the selected model. Some models, including GPT-5.6, expose both.
|
||||
|
||||
Response:
|
||||
```json
|
||||
|
||||
@@ -387,7 +387,7 @@ const available = await modelRegistry.getAvailable();
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
model: opus,
|
||||
thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh
|
||||
thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh, max
|
||||
|
||||
// Models for cycling (Ctrl+P in interactive mode)
|
||||
scopedModels: [
|
||||
|
||||
@@ -29,7 +29,7 @@ Use `/trust` in interactive mode to save a project trust decision for future ses
|
||||
|---------|------|---------|-------------|
|
||||
| `defaultProvider` | string | - | Default provider (e.g., `"anthropic"`, `"openai"`) |
|
||||
| `defaultModel` | string | - | Default model ID |
|
||||
| `defaultThinkingLevel` | string | - | `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"` |
|
||||
| `defaultThinkingLevel` | string | - | `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"` |
|
||||
| `hideThinkingBlock` | boolean | `false` | Hide thinking blocks in output |
|
||||
| `showCacheMissNotices` | boolean | `false` | Show transcript notices for significant prompt-cache misses |
|
||||
| `thinkingBudgets` | object | - | Custom token budgets per thinking level |
|
||||
|
||||
@@ -109,6 +109,7 @@ vim ~/.pi/agent/themes/my-theme.json
|
||||
"thinkingMedium": "#00ffff",
|
||||
"thinkingHigh": "#ff00ff",
|
||||
"thinkingXhigh": "#ff0000",
|
||||
"thinkingMax": "#ff0088",
|
||||
"bashMode": "#ffaa00"
|
||||
}
|
||||
}
|
||||
@@ -139,13 +140,13 @@ vim ~/.pi/agent/themes/my-theme.json
|
||||
|
||||
- `name` is required, must be unique, and must not contain `/`.
|
||||
- `vars` is optional. Define reusable colors here, then reference them in `colors`.
|
||||
- `colors` must define all 51 required tokens.
|
||||
- `colors` must define all 51 required tokens. `thinkingMax` is optional and falls back to `thinkingXhigh`.
|
||||
|
||||
The `$schema` field enables editor auto-completion and validation.
|
||||
|
||||
## Color Tokens
|
||||
|
||||
Every theme must define all 51 color tokens. There are no optional colors.
|
||||
Every theme must define all 51 required color tokens. `thinkingMax` is optional for compatibility with existing themes; when omitted, it uses `thinkingXhigh`.
|
||||
|
||||
### Core UI (11 colors)
|
||||
|
||||
@@ -216,7 +217,7 @@ Every theme must define all 51 color tokens. There are no optional colors.
|
||||
| `syntaxOperator` | Operators |
|
||||
| `syntaxPunctuation` | Punctuation |
|
||||
|
||||
### Thinking Level Borders (6 colors)
|
||||
### Thinking Level Borders (6 required, 1 optional)
|
||||
|
||||
Editor border colors indicating thinking level (visual hierarchy from subtle to prominent):
|
||||
|
||||
@@ -228,6 +229,7 @@ Editor border colors indicating thinking level (visual hierarchy from subtle to
|
||||
| `thinkingMedium` | Medium thinking |
|
||||
| `thinkingHigh` | High thinking |
|
||||
| `thinkingXhigh` | Extra high thinking |
|
||||
| `thinkingMax` | Maximum thinking; optional, falls back to `thinkingXhigh` |
|
||||
|
||||
### Bash Mode (1 color)
|
||||
|
||||
|
||||
@@ -424,7 +424,7 @@ renderResult(result, options, theme, context) {
|
||||
| Diffs | `toolDiffAdded`, `toolDiffRemoved`, `toolDiffContext` |
|
||||
| Markdown | `mdHeading`, `mdLink`, `mdLinkUrl`, `mdCode`, `mdCodeBlock`, `mdCodeBlockBorder`, `mdQuote`, `mdQuoteBorder`, `mdHr`, `mdListBullet` |
|
||||
| Syntax | `syntaxComment`, `syntaxKeyword`, `syntaxFunction`, `syntaxVariable`, `syntaxString`, `syntaxNumber`, `syntaxType`, `syntaxOperator`, `syntaxPunctuation` |
|
||||
| Thinking | `thinkingOff`, `thinkingMinimal`, `thinkingLow`, `thinkingMedium`, `thinkingHigh`, `thinkingXhigh` |
|
||||
| Thinking | `thinkingOff`, `thinkingMinimal`, `thinkingLow`, `thinkingMedium`, `thinkingHigh`, `thinkingXhigh`, `thinkingMax` |
|
||||
| Modes | `bashMode` |
|
||||
|
||||
**Background colors** (`theme.bg(color, text)`):
|
||||
|
||||
@@ -183,7 +183,7 @@ cat README.md | pi -p "Summarize this text"
|
||||
| `--provider <name>` | Provider, such as `anthropic`, `openai`, or `google` |
|
||||
| `--model <pattern>` | Model pattern or ID; supports `provider/id` and optional `:<thinking>` |
|
||||
| `--api-key <key>` | API key, overriding environment variables |
|
||||
| `--thinking <level>` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh` |
|
||||
| `--thinking <level>` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` |
|
||||
| `--models <patterns>` | Comma-separated patterns for Ctrl+P cycling |
|
||||
| `--list-models [search]` | List available models |
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ interface Preset {
|
||||
/** Model ID (e.g., "claude-sonnet-4-5") */
|
||||
model?: string;
|
||||
/** Thinking level */
|
||||
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
||||
/** Tools to enable (replaces default set) */
|
||||
tools?: string[];
|
||||
/** Instructions to append to system prompt */
|
||||
@@ -100,7 +100,7 @@ function loadPresets(cwd: string): PresetsConfig {
|
||||
|
||||
interface OriginalState {
|
||||
model: Model<Api> | undefined;
|
||||
thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
||||
tools: string[];
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export interface Args {
|
||||
diagnostics: Array<{ type: "warning" | "error"; message: string }>;
|
||||
}
|
||||
|
||||
const VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
|
||||
const VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
||||
|
||||
export function isValidThinkingLevel(level: string): level is ThinkingLevel {
|
||||
return VALID_THINKING_LEVELS.includes(level as ThinkingLevel);
|
||||
@@ -258,7 +258,7 @@ ${chalk.bold("Options:")}
|
||||
Applies to built-in, extension, and custom tools
|
||||
--exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable
|
||||
Applies to built-in, extension, and custom tools
|
||||
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
|
||||
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh, max
|
||||
--extension, -e <path> Load an extension file (can be used multiple times)
|
||||
--no-extensions, -ne Disable extension discovery (explicit -e paths still work)
|
||||
--skill <path> Load a skill file or directory (can be used multiple times)
|
||||
|
||||
@@ -94,6 +94,7 @@ const ThinkingLevelMapSchema = Type.Object({
|
||||
medium: Type.Optional(ThinkingLevelMapValueSchema),
|
||||
high: Type.Optional(ThinkingLevelMapValueSchema),
|
||||
xhigh: Type.Optional(ThinkingLevelMapValueSchema),
|
||||
max: Type.Optional(ThinkingLevelMapValueSchema),
|
||||
});
|
||||
|
||||
const ChatTemplateKwargScalarSchema = Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()]);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
||||
import type { Transport } from "@earendil-works/pi-ai";
|
||||
import { randomUUID } from "crypto";
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||
@@ -83,7 +84,7 @@ export interface Settings {
|
||||
lastChangelogVersion?: string;
|
||||
defaultProvider?: string;
|
||||
defaultModel?: string;
|
||||
defaultThinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
defaultThinkingLevel?: ThinkingLevel;
|
||||
transport?: TransportSetting; // default: "auto"
|
||||
steeringMode?: "all" | "one-at-a-time";
|
||||
followUpMode?: "all" | "one-at-a-time";
|
||||
@@ -736,11 +737,11 @@ export class SettingsManager {
|
||||
this.save();
|
||||
}
|
||||
|
||||
getDefaultThinkingLevel(): "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | undefined {
|
||||
getDefaultThinkingLevel(): ThinkingLevel | undefined {
|
||||
return this.settings.defaultThinkingLevel;
|
||||
}
|
||||
|
||||
setDefaultThinkingLevel(level: "off" | "minimal" | "low" | "medium" | "high" | "xhigh"): void {
|
||||
setDefaultThinkingLevel(level: ThinkingLevel): void {
|
||||
this.globalSettings.defaultThinkingLevel = level;
|
||||
this.markModified("defaultThinkingLevel");
|
||||
this.save();
|
||||
|
||||
@@ -35,7 +35,8 @@ const THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {
|
||||
low: "Light reasoning (~2k tokens)",
|
||||
medium: "Moderate reasoning (~8k tokens)",
|
||||
high: "Deep reasoning (~16k tokens)",
|
||||
xhigh: "Maximum reasoning (~32k tokens)",
|
||||
xhigh: "Extra-high reasoning (~32k tokens)",
|
||||
max: "Maximum reasoning",
|
||||
};
|
||||
|
||||
const DEFAULT_PROJECT_TRUST_LABELS: Record<DefaultProjectTrust, string> = {
|
||||
|
||||
@@ -14,7 +14,8 @@ const LEVEL_DESCRIPTIONS: Record<ThinkingLevel, string> = {
|
||||
low: "Light reasoning (~2k tokens)",
|
||||
medium: "Moderate reasoning (~8k tokens)",
|
||||
high: "Deep reasoning (~16k tokens)",
|
||||
xhigh: "Maximum reasoning (~32k tokens)",
|
||||
xhigh: "Extra-high reasoning (~32k tokens)",
|
||||
max: "Maximum reasoning",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
"thinkingMedium": "#81a2be",
|
||||
"thinkingHigh": "#b294bb",
|
||||
"thinkingXhigh": "#d183e8",
|
||||
"thinkingMax": "#ff5fff",
|
||||
|
||||
"bashMode": "green"
|
||||
},
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"thinkingMedium": "teal",
|
||||
"thinkingHigh": "#875f87",
|
||||
"thinkingXhigh": "#8b008b",
|
||||
"thinkingMax": "#af005f",
|
||||
|
||||
"bashMode": "green"
|
||||
},
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"colors": {
|
||||
"type": "object",
|
||||
"description": "Theme color definitions (all required)",
|
||||
"description": "Theme color definitions (thinkingMax is optional and falls back to thinkingXhigh)",
|
||||
"required": [
|
||||
"accent",
|
||||
"border",
|
||||
@@ -287,7 +287,11 @@
|
||||
},
|
||||
"thinkingXhigh": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Thinking level border: xhigh (OpenAI codex-max only)"
|
||||
"description": "Thinking level border: xhigh"
|
||||
},
|
||||
"thinkingMax": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Thinking level border: max (falls back to thinkingXhigh when omitted)"
|
||||
},
|
||||
"bashMode": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
||||
import {
|
||||
type EditorTheme,
|
||||
getCapabilities,
|
||||
@@ -88,6 +89,7 @@ const ThemeJsonSchema = Type.Object({
|
||||
thinkingMedium: ColorValueSchema,
|
||||
thinkingHigh: ColorValueSchema,
|
||||
thinkingXhigh: ColorValueSchema,
|
||||
thinkingMax: Type.Optional(ColorValueSchema),
|
||||
// Bash Mode (1 color)
|
||||
bashMode: ColorValueSchema,
|
||||
}),
|
||||
@@ -149,6 +151,7 @@ export type ThemeColor =
|
||||
| "thinkingMedium"
|
||||
| "thinkingHigh"
|
||||
| "thinkingXhigh"
|
||||
| "thinkingMax"
|
||||
| "bashMode";
|
||||
|
||||
export type ThemeBg =
|
||||
@@ -316,6 +319,10 @@ function resolveThemeColors<T extends Record<string, ColorValue>>(
|
||||
return resolved as Record<keyof T, string | number>;
|
||||
}
|
||||
|
||||
function withThemeColorFallbacks(colors: ThemeJson["colors"]): ThemeJson["colors"] & { thinkingMax: ColorValue } {
|
||||
return { ...colors, thinkingMax: colors.thinkingMax ?? colors.thinkingXhigh };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Theme Class
|
||||
// ============================================================================
|
||||
@@ -339,7 +346,8 @@ export class Theme {
|
||||
this.sourceInfo = options.sourceInfo;
|
||||
this.mode = mode;
|
||||
this.fgColors = new Map();
|
||||
for (const [key, value] of Object.entries(fgColors) as [ThemeColor, string | number][]) {
|
||||
const colors = { ...fgColors, thinkingMax: fgColors.thinkingMax ?? fgColors.thinkingXhigh };
|
||||
for (const [key, value] of Object.entries(colors) as [ThemeColor, string | number][]) {
|
||||
this.fgColors.set(key, fgAnsi(value, mode));
|
||||
}
|
||||
this.bgColors = new Map();
|
||||
@@ -396,7 +404,7 @@ export class Theme {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
getThinkingBorderColor(level: "off" | "minimal" | "low" | "medium" | "high" | "xhigh"): (str: string) => string {
|
||||
getThinkingBorderColor(level: ThinkingLevel): (str: string) => string {
|
||||
// Map thinking levels to dedicated theme colors
|
||||
switch (level) {
|
||||
case "off":
|
||||
@@ -411,6 +419,8 @@ export class Theme {
|
||||
return (str: string) => this.fg("thinkingHigh", str);
|
||||
case "xhigh":
|
||||
return (str: string) => this.fg("thinkingXhigh", str);
|
||||
case "max":
|
||||
return (str: string) => this.fg("thinkingMax", str);
|
||||
default:
|
||||
return (str: string) => this.fg("thinkingOff", str);
|
||||
}
|
||||
@@ -586,7 +596,7 @@ function loadThemeJson(name: string): ThemeJson {
|
||||
|
||||
function createTheme(themeJson: ThemeJson, mode?: ColorMode, sourcePath?: string): Theme {
|
||||
const colorMode = mode ?? (getCapabilities().trueColor ? "truecolor" : "256color");
|
||||
const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
|
||||
const resolvedColors = resolveThemeColors(withThemeColorFallbacks(themeJson.colors), themeJson.vars);
|
||||
const fgColors: Record<ThemeColor, string | number> = {} as Record<ThemeColor, string | number>;
|
||||
const bgColors: Record<ThemeBg, string | number> = {} as Record<ThemeBg, string | number>;
|
||||
const bgColorKeys: Set<string> = new Set([
|
||||
@@ -1013,7 +1023,7 @@ export function getResolvedThemeColors(themeName?: string): Record<string, strin
|
||||
const name = themeName ?? currentThemeName ?? getDefaultTheme();
|
||||
const isLight = name === "light";
|
||||
const themeJson = loadThemeJson(name);
|
||||
const resolved = resolveThemeColors(themeJson.colors, themeJson.vars);
|
||||
const resolved = resolveThemeColors(withThemeColorFallbacks(themeJson.colors), themeJson.vars);
|
||||
|
||||
// Default text color for empty values (terminal uses default fg color)
|
||||
const defaultText = isLight ? "#000000" : "#e5e5e7";
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { isValidThinkingLevel } from "../src/cli/args.ts";
|
||||
import { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { loadThemeFromPath } from "../src/modes/interactive/theme/theme.ts";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe("max thinking level", () => {
|
||||
it("is accepted by CLI and settings", async () => {
|
||||
expect(isValidThinkingLevel("max")).toBe(true);
|
||||
|
||||
const settings = SettingsManager.inMemory();
|
||||
settings.setDefaultThinkingLevel("max");
|
||||
await settings.flush();
|
||||
expect(settings.getDefaultThinkingLevel()).toBe("max");
|
||||
});
|
||||
|
||||
it("falls back to thinkingXhigh for legacy themes", () => {
|
||||
const testDir = mkdtempSync(join(tmpdir(), "pi-max-theme-"));
|
||||
tempDirs.push(testDir);
|
||||
const currentDir = dirname(fileURLToPath(import.meta.url));
|
||||
const darkTheme = JSON.parse(
|
||||
readFileSync(join(currentDir, "../src/modes/interactive/theme/dark.json"), "utf8"),
|
||||
) as { name: string; colors: Record<string, unknown> };
|
||||
darkTheme.name = "legacy-theme";
|
||||
delete darkTheme.colors.thinkingMax;
|
||||
const themePath = join(testDir, "legacy-theme.json");
|
||||
writeFileSync(themePath, JSON.stringify(darkTheme));
|
||||
|
||||
const legacyTheme = loadThemeFromPath(themePath);
|
||||
expect(legacyTheme.getThinkingBorderColor("max")("border")).toBe(
|
||||
legacyTheme.getThinkingBorderColor("xhigh")("border"),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -107,7 +107,7 @@ describe("parseModelPattern", () => {
|
||||
});
|
||||
|
||||
test("all valid thinking levels work", () => {
|
||||
for (const level of ["off", "minimal", "low", "medium", "high", "xhigh"]) {
|
||||
for (const level of ["off", "minimal", "low", "medium", "high", "xhigh", "max"]) {
|
||||
const result = parseModelPattern(`sonnet:${level}`, allModels);
|
||||
expect(result.model?.id).toBe("claude-sonnet-4-5");
|
||||
expect(result.thinkingLevel).toBe(level);
|
||||
@@ -520,7 +520,7 @@ describe("resolveCliModel", () => {
|
||||
getAll: () => modelsWithNeuralwatt,
|
||||
} as unknown as Parameters<typeof resolveCliModel>[0]["modelRegistry"];
|
||||
|
||||
for (const level of ["off", "minimal", "low", "medium", "high", "xhigh"]) {
|
||||
for (const level of ["off", "minimal", "low", "medium", "high", "xhigh", "max"]) {
|
||||
const result = resolveCliModel({
|
||||
cliModel: `neuralwatt/zai-org/GLM-5.1-FP8:${level}`,
|
||||
modelRegistry: registry,
|
||||
|
||||
@@ -78,6 +78,26 @@ describe("AgentSession model and extension characterization", () => {
|
||||
expect(harness.session.cycleThinkingLevel()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("cycles xhigh before max when both are supported", async () => {
|
||||
const harness = await createHarness({ models: [{ id: "faux-1", reasoning: true }] });
|
||||
harnesses.push(harness);
|
||||
harness.getModel().thinkingLevelMap = { xhigh: "xhigh", max: "max" };
|
||||
|
||||
expect(harness.session.getAvailableThinkingLevels()).toEqual([
|
||||
"off",
|
||||
"minimal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh",
|
||||
"max",
|
||||
]);
|
||||
harness.session.setThinkingLevel("high");
|
||||
expect(harness.session.cycleThinkingLevel()).toBe("xhigh");
|
||||
expect(harness.session.cycleThinkingLevel()).toBe("max");
|
||||
expect(harness.session.cycleThinkingLevel()).toBe("off");
|
||||
});
|
||||
|
||||
it("throws when setModel is called without configured auth", async () => {
|
||||
const harness = await createHarness({
|
||||
models: [
|
||||
|
||||
Reference in New Issue
Block a user