@benvargas/pi-openai-verbosity 1.0.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/LICENSE +21 -0
- package/README.md +162 -0
- package/extensions/index.ts +323 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ben Vargas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# @benvargas/pi-openai-verbosity
|
|
2
|
+
|
|
3
|
+
Per-model text verbosity overrides for pi's `openai-codex` provider.
|
|
4
|
+
|
|
5
|
+
## Why This Exists
|
|
6
|
+
|
|
7
|
+
This extension was originally created because pi sent OpenAI Codex provider requests with the default Responses
|
|
8
|
+
API text verbosity, which made some models, especially `gpt-5.5`, noticeably more verbose than the Codex CLI.
|
|
9
|
+
The goal was to align `openai-codex/gpt-5.5` with Codex CLI behavior by setting:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"text": {
|
|
14
|
+
"verbosity": "low"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
pi has since shipped an upstream fix: OpenAI Codex Responses requests now default to `low` verbosity when no
|
|
20
|
+
explicit verbosity is provided.
|
|
21
|
+
|
|
22
|
+
That upstream change addresses the original `gpt-5.5` issue, but it also means pi now defaults every
|
|
23
|
+
`openai-codex` model to `low`. That may not be ideal for all model slugs. For example, you may prefer `low` on
|
|
24
|
+
`gpt-5.5`, but `medium` on an older or different model such as `gpt-5.3-codex`.
|
|
25
|
+
|
|
26
|
+
This extension now provides the missing user-facing control: per-slug verbosity settings for pi's
|
|
27
|
+
`openai-codex` provider.
|
|
28
|
+
|
|
29
|
+
Requires pi `0.57.0` or newer.
|
|
30
|
+
|
|
31
|
+
## What It Does
|
|
32
|
+
|
|
33
|
+
The extension uses pi's `before_provider_request` hook to rewrite outgoing provider payloads for configured
|
|
34
|
+
`openai-codex/<model>` keys.
|
|
35
|
+
|
|
36
|
+
For matching models, it sets:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"text": {
|
|
41
|
+
"verbosity": "low | medium | high"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Non-matching models are left unchanged.
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pi install npm:@benvargas/pi-openai-verbosity
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Or try without installing:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pi -e npm:@benvargas/pi-openai-verbosity
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
Run pi with the extension enabled:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pi -e npm:@benvargas/pi-openai-verbosity --model openai-codex/gpt-5.5
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use `/openai-verbosity status` inside pi to report the configured rewrite for the current model. The command also
|
|
69
|
+
reloads the config file.
|
|
70
|
+
|
|
71
|
+
## Config
|
|
72
|
+
|
|
73
|
+
Config files follow pi's project-over-global pattern:
|
|
74
|
+
|
|
75
|
+
- Project: `<repo>/.pi/extensions/pi-openai-verbosity.json`
|
|
76
|
+
- Global: `~/.pi/agent/extensions/pi-openai-verbosity.json`
|
|
77
|
+
|
|
78
|
+
If neither exists, the extension writes a default global config on first run.
|
|
79
|
+
|
|
80
|
+
Example config:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"models": {
|
|
85
|
+
"openai-codex/gpt-5.5": "low",
|
|
86
|
+
"openai-codex/gpt-5.4": "low",
|
|
87
|
+
"openai-codex/gpt-5.3-codex": "medium",
|
|
88
|
+
"openai-codex/gpt-5.3-codex-spark": "medium",
|
|
89
|
+
"openai-codex/gpt-5.2": "medium"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Settings:
|
|
95
|
+
|
|
96
|
+
- `models`: object mapping `openai-codex/<model-id>` strings to `low`, `medium`, or `high`.
|
|
97
|
+
|
|
98
|
+
Project config overrides global config per model key. Any model not listed is left unchanged, which means pi's
|
|
99
|
+
native default behavior applies.
|
|
100
|
+
|
|
101
|
+
## Default Config
|
|
102
|
+
|
|
103
|
+
By default, the extension preserves the original workaround behavior and sets known supported OpenAI Codex models
|
|
104
|
+
to `low`:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"models": {
|
|
109
|
+
"openai-codex/gpt-5.4": "low",
|
|
110
|
+
"openai-codex/gpt-5.5": "low",
|
|
111
|
+
"openai-codex/gpt-5.4-mini": "low",
|
|
112
|
+
"openai-codex/gpt-5.3-codex": "low",
|
|
113
|
+
"openai-codex/gpt-5.3-codex-spark": "low",
|
|
114
|
+
"openai-codex/gpt-5.2": "low",
|
|
115
|
+
"openai-codex/codex-auto-review": "low"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
You can change any value to `medium` or `high` to override pi's native low-verbosity default for that model.
|
|
121
|
+
|
|
122
|
+
## Debugging
|
|
123
|
+
|
|
124
|
+
Pi does not currently expose a simple CLI flag to print the final provider request body. To verify this extension is
|
|
125
|
+
matching and rewriting a request, set `PI_OPENAI_VERBOSITY_DEBUG_LOG` to a JSONL file path.
|
|
126
|
+
|
|
127
|
+
| Variable | Description |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `PI_OPENAI_VERBOSITY_DEBUG_LOG` | Set to a file path to enable debug logging. Matching requests write `"before"` and `"after"` JSON entries with the full provider payload. Non-matching requests write one `"skipped"` entry. |
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
PI_OPENAI_VERBOSITY_DEBUG_LOG=/tmp/pi-openai-verbosity.jsonl \
|
|
133
|
+
pi -e npm:@benvargas/pi-openai-verbosity \
|
|
134
|
+
--model openai-codex/gpt-5.3-codex \
|
|
135
|
+
-p "Reply in one short sentence."
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Then inspect the last entries:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
tail -n 5 /tmp/pi-openai-verbosity.jsonl | jq .
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
These entries include prompts, messages, tools, and the rest of the provider payload, so keep the file local and
|
|
145
|
+
delete it when you are done debugging.
|
|
146
|
+
|
|
147
|
+
## Notes
|
|
148
|
+
|
|
149
|
+
- This extension only changes outgoing provider request payloads.
|
|
150
|
+
- Existing `text` fields are preserved, and only `text.verbosity` is replaced.
|
|
151
|
+
- Only the `openai-codex` provider is supported.
|
|
152
|
+
- This extension is most useful if you want different verbosity settings for different OpenAI Codex model slugs.
|
|
153
|
+
|
|
154
|
+
## Uninstall
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pi remove npm:@benvargas/pi-openai-verbosity
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
MIT
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI verbosity for pi.
|
|
3
|
+
*
|
|
4
|
+
* Sets OpenAI Responses `text.verbosity` for configured models via the
|
|
5
|
+
* `before_provider_request` hook. Config precedence is project
|
|
6
|
+
* `.pi/extensions/pi-openai-verbosity.json` over global
|
|
7
|
+
* `~/.pi/agent/extensions/pi-openai-verbosity.json`.
|
|
8
|
+
*/
|
|
9
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
13
|
+
|
|
14
|
+
const VERBOSITY_COMMAND = "openai-verbosity";
|
|
15
|
+
const VERBOSITY_CONFIG_BASENAME = "pi-openai-verbosity.json";
|
|
16
|
+
const VERBOSITY_COMMAND_ARGS = ["status"] as const;
|
|
17
|
+
const DEBUG_LOG_ENV = "PI_OPENAI_VERBOSITY_DEBUG_LOG";
|
|
18
|
+
const SUPPORTED_PROVIDERS = ["openai-codex"] as const;
|
|
19
|
+
const DEFAULT_MODEL_VERBOSITY = {
|
|
20
|
+
"openai-codex/gpt-5.4": "low",
|
|
21
|
+
"openai-codex/gpt-5.5": "low",
|
|
22
|
+
"openai-codex/gpt-5.4-mini": "low",
|
|
23
|
+
"openai-codex/gpt-5.3-codex": "low",
|
|
24
|
+
"openai-codex/gpt-5.3-codex-spark": "low",
|
|
25
|
+
"openai-codex/gpt-5.2": "low",
|
|
26
|
+
"openai-codex/codex-auto-review": "low",
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
29
|
+
type TextVerbosity = "low" | "medium" | "high";
|
|
30
|
+
|
|
31
|
+
interface VerbosityConfigFile {
|
|
32
|
+
models?: Record<string, TextVerbosity>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface ResolvedVerbosityConfig {
|
|
36
|
+
configPath: string;
|
|
37
|
+
models: Record<string, TextVerbosity>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type VerbosityPayload = {
|
|
41
|
+
text?: unknown;
|
|
42
|
+
[key: string]: unknown;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const DEFAULT_CONFIG_FILE: VerbosityConfigFile = {
|
|
46
|
+
models: { ...DEFAULT_MODEL_VERBOSITY },
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
50
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isTextVerbosity(value: unknown): value is TextVerbosity {
|
|
54
|
+
return value === "low" || value === "medium" || value === "high";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function normalizeModelKey(value: string): string | undefined {
|
|
58
|
+
const trimmed = value.trim();
|
|
59
|
+
if (!trimmed) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const slashIndex = trimmed.indexOf("/");
|
|
63
|
+
if (slashIndex <= 0 || slashIndex >= trimmed.length - 1) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const provider = trimmed.slice(0, slashIndex).trim();
|
|
67
|
+
const id = trimmed.slice(slashIndex + 1).trim();
|
|
68
|
+
if (!provider || !id) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
if (!SUPPORTED_PROVIDERS.includes(provider as (typeof SUPPORTED_PROVIDERS)[number])) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return `${provider}/${id}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeModelVerbosityMap(value: unknown): Record<string, TextVerbosity> | undefined {
|
|
78
|
+
if (value === undefined) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
if (!isRecord(value)) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const normalized: Record<string, TextVerbosity> = {};
|
|
86
|
+
for (const [rawKey, rawVerbosity] of Object.entries(value)) {
|
|
87
|
+
const key = normalizeModelKey(rawKey);
|
|
88
|
+
if (!key || !isTextVerbosity(rawVerbosity)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
normalized[key] = rawVerbosity;
|
|
92
|
+
}
|
|
93
|
+
return normalized;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getConfigCwd(ctx: ExtensionContext): string {
|
|
97
|
+
return ctx.cwd || process.cwd();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getConfigPaths(
|
|
101
|
+
cwd: string,
|
|
102
|
+
homeDir: string = homedir(),
|
|
103
|
+
): {
|
|
104
|
+
projectConfigPath: string;
|
|
105
|
+
globalConfigPath: string;
|
|
106
|
+
} {
|
|
107
|
+
return {
|
|
108
|
+
projectConfigPath: join(cwd, ".pi", "extensions", VERBOSITY_CONFIG_BASENAME),
|
|
109
|
+
globalConfigPath: join(homeDir, ".pi", "agent", "extensions", VERBOSITY_CONFIG_BASENAME),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function readConfigFile(filePath: string): VerbosityConfigFile | null {
|
|
114
|
+
if (!existsSync(filePath)) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
119
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
120
|
+
if (!isRecord(parsed)) {
|
|
121
|
+
return {};
|
|
122
|
+
}
|
|
123
|
+
const models = normalizeModelVerbosityMap(parsed.models);
|
|
124
|
+
return models === undefined ? {} : { models };
|
|
125
|
+
} catch (error) {
|
|
126
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
127
|
+
console.warn(`[pi-openai-verbosity] Failed to read ${filePath}: ${message}`);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function writeConfigFile(filePath: string, config: VerbosityConfigFile): void {
|
|
133
|
+
try {
|
|
134
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
135
|
+
writeFileSync(filePath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
136
|
+
} catch (error) {
|
|
137
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
138
|
+
console.warn(`[pi-openai-verbosity] Failed to write ${filePath}: ${message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function ensureDefaultConfigFile(projectConfigPath: string, globalConfigPath: string): void {
|
|
143
|
+
if (existsSync(projectConfigPath) || existsSync(globalConfigPath)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
writeConfigFile(globalConfigPath, DEFAULT_CONFIG_FILE);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function resolveVerbosityConfig(cwd: string, homeDir: string = homedir()): ResolvedVerbosityConfig {
|
|
150
|
+
const { projectConfigPath, globalConfigPath } = getConfigPaths(cwd, homeDir);
|
|
151
|
+
ensureDefaultConfigFile(projectConfigPath, globalConfigPath);
|
|
152
|
+
|
|
153
|
+
const globalConfig = readConfigFile(globalConfigPath) ?? {};
|
|
154
|
+
const projectConfig = readConfigFile(projectConfigPath) ?? {};
|
|
155
|
+
const selectedConfigPath = existsSync(projectConfigPath) ? projectConfigPath : globalConfigPath;
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
configPath: selectedConfigPath,
|
|
159
|
+
models: {
|
|
160
|
+
...DEFAULT_MODEL_VERBOSITY,
|
|
161
|
+
...(globalConfig.models ?? {}),
|
|
162
|
+
...(projectConfig.models ?? {}),
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function getCurrentModelKey(model: ExtensionContext["model"]): string | undefined {
|
|
168
|
+
if (!model) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
return `${model.provider}/${model.id}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function getVerbosityForModel(
|
|
175
|
+
model: ExtensionContext["model"],
|
|
176
|
+
models: Record<string, TextVerbosity>,
|
|
177
|
+
): TextVerbosity | undefined {
|
|
178
|
+
const modelKey = getCurrentModelKey(model);
|
|
179
|
+
return modelKey ? models[modelKey] : undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function describeConfiguredModels(models: Record<string, TextVerbosity>): string {
|
|
183
|
+
const entries = Object.entries(models);
|
|
184
|
+
if (entries.length === 0) {
|
|
185
|
+
return "none configured";
|
|
186
|
+
}
|
|
187
|
+
return entries.map(([model, verbosity]) => `${model}=${verbosity}`).join(", ");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function describeCurrentState(ctx: ExtensionContext, config: ResolvedVerbosityConfig): string {
|
|
191
|
+
const model = getCurrentModelKey(ctx.model) ?? "none";
|
|
192
|
+
const verbosity = getVerbosityForModel(ctx.model, config.models);
|
|
193
|
+
if (verbosity) {
|
|
194
|
+
return `OpenAI verbosity sets text.verbosity=${verbosity} for ${model}.`;
|
|
195
|
+
}
|
|
196
|
+
return `OpenAI verbosity has no setting configured for ${model}. Configured models: ${describeConfiguredModels(
|
|
197
|
+
config.models,
|
|
198
|
+
)}.`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function applyTextVerbosity(payload: unknown, verbosity: TextVerbosity): unknown {
|
|
202
|
+
if (!isRecord(payload)) {
|
|
203
|
+
return payload;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const nextPayload: VerbosityPayload = { ...payload };
|
|
207
|
+
const text = isRecord(nextPayload.text) ? { ...nextPayload.text } : {};
|
|
208
|
+
text.verbosity = verbosity;
|
|
209
|
+
nextPayload.text = text;
|
|
210
|
+
return nextPayload;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function getPayloadTextVerbosity(payload: unknown): unknown {
|
|
214
|
+
if (!isRecord(payload) || !isRecord(payload.text)) {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
return payload.text.verbosity;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function writeDebugLog(
|
|
221
|
+
entry: Record<string, unknown>,
|
|
222
|
+
debugLogPath: string | undefined = process.env[DEBUG_LOG_ENV],
|
|
223
|
+
): void {
|
|
224
|
+
if (!debugLogPath) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
mkdirSync(dirname(debugLogPath), { recursive: true });
|
|
229
|
+
appendFileSync(debugLogPath, `${JSON.stringify({ timestamp: new Date().toISOString(), ...entry })}\n`, "utf-8");
|
|
230
|
+
} catch (error) {
|
|
231
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
232
|
+
console.warn(`[pi-openai-verbosity] Failed to write debug log ${debugLogPath}: ${message}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export default function piOpenAIVerbosity(pi: ExtensionAPI): void {
|
|
237
|
+
let cachedConfig: ResolvedVerbosityConfig | undefined;
|
|
238
|
+
|
|
239
|
+
function refreshConfig(ctx: ExtensionContext): ResolvedVerbosityConfig {
|
|
240
|
+
cachedConfig = resolveVerbosityConfig(getConfigCwd(ctx));
|
|
241
|
+
return cachedConfig;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function getConfig(ctx: ExtensionContext): ResolvedVerbosityConfig {
|
|
245
|
+
return cachedConfig ?? refreshConfig(ctx);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
pi.registerCommand(VERBOSITY_COMMAND, {
|
|
249
|
+
description: "Report configured GPT text verbosity rewrites",
|
|
250
|
+
getArgumentCompletions: (prefix) => {
|
|
251
|
+
const items = VERBOSITY_COMMAND_ARGS.filter((value) => value.startsWith(prefix)).map((value) => ({
|
|
252
|
+
value,
|
|
253
|
+
label: value,
|
|
254
|
+
}));
|
|
255
|
+
return items.length > 0 ? items : null;
|
|
256
|
+
},
|
|
257
|
+
handler: async (args, ctx) => {
|
|
258
|
+
const command = args.trim().toLowerCase();
|
|
259
|
+
if (command.length > 0 && command !== "status") {
|
|
260
|
+
ctx.ui.notify("Usage: /openai-verbosity [status]", "error");
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
ctx.ui.notify(describeCurrentState(ctx, refreshConfig(ctx)), "info");
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
268
|
+
const model = getCurrentModelKey(ctx.model) ?? null;
|
|
269
|
+
const beforeTextVerbosity = getPayloadTextVerbosity(event.payload);
|
|
270
|
+
const verbosity = getVerbosityForModel(ctx.model, getConfig(ctx).models);
|
|
271
|
+
if (!verbosity) {
|
|
272
|
+
writeDebugLog({
|
|
273
|
+
stage: "skipped",
|
|
274
|
+
model,
|
|
275
|
+
matched: false,
|
|
276
|
+
beforeTextVerbosity: beforeTextVerbosity ?? null,
|
|
277
|
+
payload: event.payload,
|
|
278
|
+
});
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
writeDebugLog({
|
|
282
|
+
stage: "before",
|
|
283
|
+
model,
|
|
284
|
+
matched: true,
|
|
285
|
+
configuredVerbosity: verbosity,
|
|
286
|
+
beforeTextVerbosity: beforeTextVerbosity ?? null,
|
|
287
|
+
payload: event.payload,
|
|
288
|
+
});
|
|
289
|
+
const nextPayload = applyTextVerbosity(event.payload, verbosity);
|
|
290
|
+
writeDebugLog({
|
|
291
|
+
stage: "after",
|
|
292
|
+
model,
|
|
293
|
+
matched: true,
|
|
294
|
+
configuredVerbosity: verbosity,
|
|
295
|
+
beforeTextVerbosity: beforeTextVerbosity ?? null,
|
|
296
|
+
afterTextVerbosity: getPayloadTextVerbosity(nextPayload) ?? null,
|
|
297
|
+
payload: nextPayload,
|
|
298
|
+
});
|
|
299
|
+
return nextPayload;
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export const _test = {
|
|
304
|
+
VERBOSITY_COMMAND,
|
|
305
|
+
VERBOSITY_CONFIG_BASENAME,
|
|
306
|
+
VERBOSITY_COMMAND_ARGS,
|
|
307
|
+
SUPPORTED_PROVIDERS,
|
|
308
|
+
DEFAULT_MODEL_VERBOSITY,
|
|
309
|
+
DEFAULT_CONFIG_FILE,
|
|
310
|
+
isTextVerbosity,
|
|
311
|
+
normalizeModelKey,
|
|
312
|
+
normalizeModelVerbosityMap,
|
|
313
|
+
getConfigPaths,
|
|
314
|
+
readConfigFile,
|
|
315
|
+
resolveVerbosityConfig,
|
|
316
|
+
getCurrentModelKey,
|
|
317
|
+
getVerbosityForModel,
|
|
318
|
+
describeConfiguredModels,
|
|
319
|
+
describeCurrentState,
|
|
320
|
+
applyTextVerbosity,
|
|
321
|
+
getPayloadTextVerbosity,
|
|
322
|
+
writeDebugLog,
|
|
323
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@benvargas/pi-openai-verbosity",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Per-model OpenAI Codex text verbosity overrides for pi",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi",
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi-extension",
|
|
9
|
+
"pi-coding-agent",
|
|
10
|
+
"openai",
|
|
11
|
+
"codex",
|
|
12
|
+
"openai-codex",
|
|
13
|
+
"gpt-5.5",
|
|
14
|
+
"gpt-5.3-codex",
|
|
15
|
+
"verbosity",
|
|
16
|
+
"text-verbosity",
|
|
17
|
+
"per-model"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"files": [
|
|
21
|
+
"extensions/",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"pi": {
|
|
26
|
+
"extensions": [
|
|
27
|
+
"./extensions/index.ts"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@mariozechner/pi-coding-agent": ">=0.57.0"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/ben-vargas/pi-packages.git",
|
|
36
|
+
"directory": "packages/pi-openai-verbosity"
|
|
37
|
+
},
|
|
38
|
+
"author": "Ben Vargas",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/ben-vargas/pi-packages/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/ben-vargas/pi-packages/tree/main/packages/pi-openai-verbosity#readme"
|
|
44
|
+
}
|