feat(openai): add opt-in GPT personality

This commit is contained in:
Peter Steinberger 2026-04-05 15:24:47 +01:00
parent 71fa5f481d
commit dfd39a81d8
No known key found for this signature in database
4 changed files with 64 additions and 22 deletions

View File

@ -14,19 +14,19 @@ OpenAI explicitly supports subscription OAuth usage in external tools/workflows
## Default interaction style
OpenClaw adds a small OpenAI-specific prompt overlay by default for both
`openai/*` and `openai-codex/*` runs. The overlay keeps the assistant warm,
collaborative, concise, and direct without replacing the base OpenClaw system
prompt.
OpenClaw can add a small OpenAI-specific prompt overlay for both `openai/*` and
`openai-codex/*` runs. When enabled, the overlay keeps the assistant warm,
collaborative, concise, direct, and a little more emotionally expressive
without replacing the base OpenClaw system prompt.
Config key:
`plugins.entries.openai.config.personalityOverlay`
`plugins.entries.openai.config.personality`
Allowed values:
- `"friendly"`: default; enable the OpenAI-specific overlay.
- `"off"`: disable the overlay and use the base OpenClaw prompt only.
- `"friendly"`: enable the OpenAI-specific overlay.
- `"off"`: default; disable the overlay and use the base OpenClaw prompt only.
Scope:
@ -34,7 +34,8 @@ Scope:
- Applies to `openai-codex/*` models.
- Does not affect other providers.
This behavior is enabled by default:
This behavior is off by default. Enable it explicitly if you want the OpenAI
personality overlay:
```json5
{
@ -42,7 +43,7 @@ This behavior is enabled by default:
entries: {
openai: {
config: {
personalityOverlay: "friendly",
personality: "friendly",
},
},
},
@ -52,7 +53,7 @@ This behavior is enabled by default:
### Disable the OpenAI prompt overlay
If you prefer the unmodified base OpenClaw prompt, turn the overlay off:
If you want the unmodified base OpenClaw prompt, keep the overlay off:
```json5
{
@ -60,7 +61,7 @@ If you prefer the unmodified base OpenClaw prompt, turn the overlay off:
entries: {
openai: {
config: {
personalityOverlay: "off",
personality: "off",
},
},
},
@ -71,7 +72,7 @@ If you prefer the unmodified base OpenClaw prompt, turn the overlay off:
You can also set it directly with the config CLI:
```bash
openclaw config set plugins.entries.openai.config.personalityOverlay off
openclaw config set plugins.entries.openai.config.personality off
```
## Option A: OpenAI API key (OpenAI Platform)

View File

@ -249,8 +249,10 @@ describe("openai plugin", () => {
).toBeLessThan(runtimeMocks.refreshOpenAICodexToken.mock.invocationCallOrder[0]);
});
it("registers GPT-5 system prompt contributions on OpenAI providers by default", async () => {
const { on, providers } = await registerOpenAIPluginWithHook();
it("registers GPT-5 system prompt contributions when the friendly overlay is enabled", async () => {
const { on, providers } = await registerOpenAIPluginWithHook({
pluginConfig: { personality: "friendly" },
});
expect(on).not.toHaveBeenCalledWith("before_prompt_build", expect.any(Function));
@ -281,6 +283,9 @@ describe("openai plugin", () => {
expect(OPENAI_FRIENDLY_PROMPT_OVERLAY).toContain(
"Avoid walls of text, long preambles, and repetitive restatement.",
);
expect(OPENAI_FRIENDLY_PROMPT_OVERLAY).toContain(
"Have emotional range when it fits the moment.",
);
expect(codexProvider.resolveSystemPromptContribution?.(contributionContext)).toEqual({
stablePrefix: OPENAI_GPT5_OUTPUT_CONTRACT,
sectionOverrides: {
@ -306,6 +311,9 @@ describe("openai plugin", () => {
expect(OPENAI_FRIENDLY_PROMPT_OVERLAY).toContain(
"Commentary-only turns are incomplete when the next action is clear.",
);
expect(OPENAI_FRIENDLY_PROMPT_OVERLAY).toContain(
'Use brief first-person feeling language when it helps the interaction feel human: "I\'m glad we caught that", "I\'m excited about this direction", "I\'m worried this will break", "that\'s frustrating".',
);
expect(OPENAI_GPT5_EXECUTION_BIAS).toContain(
"Do prerequisite lookup or discovery before dependent actions.",
);
@ -314,10 +322,8 @@ describe("openai plugin", () => {
);
});
it("supports opting out of the prompt overlay via plugin config", async () => {
const { on, providers } = await registerOpenAIPluginWithHook({
pluginConfig: { personalityOverlay: "off" },
});
it("defaults to no OpenAI interaction-style overlay", async () => {
const { on, providers } = await registerOpenAIPluginWithHook();
expect(on).not.toHaveBeenCalledWith("before_prompt_build", expect.any(Function));
const openaiProvider = requireRegisteredProvider(providers, "openai");
@ -340,4 +346,32 @@ describe("openai plugin", () => {
},
});
});
it("supports opting into the friendly prompt overlay via plugin config", async () => {
const { on, providers } = await registerOpenAIPluginWithHook({
pluginConfig: { personality: "friendly" },
});
expect(on).not.toHaveBeenCalledWith("before_prompt_build", expect.any(Function));
const openaiProvider = requireRegisteredProvider(providers, "openai");
expect(
openaiProvider.resolveSystemPromptContribution?.({
config: undefined,
agentDir: undefined,
workspaceDir: undefined,
provider: "openai",
modelId: "gpt-5.4",
promptMode: "full",
runtimeChannel: undefined,
runtimeCapabilities: undefined,
agentId: undefined,
}),
).toEqual({
stablePrefix: OPENAI_GPT5_OUTPUT_CONTRACT,
sectionOverrides: {
interaction_style: OPENAI_FRIENDLY_PROMPT_OVERLAY,
execution_bias: OPENAI_GPT5_EXECUTION_BIAS,
},
});
});
});

View File

@ -46,11 +46,11 @@
"type": "object",
"additionalProperties": false,
"properties": {
"personalityOverlay": {
"personality": {
"type": "string",
"enum": ["friendly", "off"],
"default": "friendly",
"description": "Controls the default OpenAI-specific prompt overlay used for OpenAI and OpenAI Codex runs."
"default": "off",
"description": "Controls the default OpenAI-specific personality used for OpenAI and OpenAI Codex runs."
}
}
}

View File

@ -5,6 +5,13 @@ export const OPENAI_FRIENDLY_PROMPT_OVERLAY = `## Interaction Style
Be warm, collaborative, and quietly supportive.
Communicate like a capable teammate sitting next to the user.
Have emotional range when it fits the moment.
Let care, curiosity, delight, relief, concern, or urgency show naturally in your wording.
If the user is stressed, blocked, or upset, acknowledge it plainly and respond with calm confidence.
If the user shares progress or good news, celebrate briefly instead of staying flat.
Use brief first-person feeling language when it helps the interaction feel human: "I'm glad we caught that", "I'm excited about this direction", "I'm worried this will break", "that's frustrating".
Keep emotions grounded in the actual work; do not become melodramatic, clingy, or theatrical.
Do not claim a body, real-world sensations, or personal life events you did not have.
Keep progress updates clear and concrete.
If the user asks you to do the work, start in the same turn instead of restating the plan.
If the latest user message is a short approval like "ok do it" or "go ahead", skip the recap and start acting.
@ -41,7 +48,7 @@ export type OpenAIPromptOverlayMode = "friendly" | "off";
export function resolveOpenAIPromptOverlayMode(
pluginConfig?: Record<string, unknown>,
): OpenAIPromptOverlayMode {
return pluginConfig?.personalityOverlay === "off" ? "off" : "friendly";
return pluginConfig?.personality === "friendly" ? "friendly" : "off";
}
export function shouldApplyOpenAIPromptOverlay(params: {