Skip to content

Commit a3a99fb

Browse files
committed
feat: add custom system prompt support for AI modes
Add ai:systemprompt and ai:systempromptmode fields to waveai.json schema allowing users to customize the system prompt for their AI modes. - ai:systemprompt: custom system prompt text - ai:systempromptmode: 'replace' (default) or 'append' Update documentation with examples for both modes.
1 parent 96c2526 commit a3a99fb

7 files changed

Lines changed: 118 additions & 29 deletions

File tree

docs/docs/waveai-modes.mdx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,56 @@ When using providers like `openai` or `openrouter`, the secret name is automatic
430430

431431
The `openai` provider automatically looks for the `OPENAI_KEY` secret. See the [Secrets documentation](./secrets.mdx) for more information on managing secrets securely in Wave.
432432

433+
## Custom System Prompts
434+
435+
You can customize the system prompt for any AI mode to control the AI's behavior and personality. This allows you to override or extend the default Wave AI system prompt.
436+
437+
### System Prompt Modes
438+
439+
There are two modes for applying custom system prompts:
440+
441+
| Mode | Description |
442+
|------|-------------|
443+
| `"replace"` (default) | Completely replaces the built-in Wave AI system prompt with your custom prompt |
444+
| `"append"` | Adds your custom prompt after the built-in Wave AI system prompt |
445+
446+
### Example: Replace Mode
447+
448+
Use this when you want full control over the AI's behavior:
449+
450+
```json
451+
{
452+
"my-custom-assistant": {
453+
"display:name": "My Coding Assistant",
454+
"ai:provider": "openai",
455+
"ai:model": "gpt-4o",
456+
"ai:systemprompt": "You are a specialized coding assistant focused on Go and TypeScript. Always provide concise, well-commented code examples. Prefer modern language features and best practices.",
457+
"ai:systempromptmode": "replace"
458+
}
459+
}
460+
```
461+
462+
### Example: Append Mode
463+
464+
Use this when you want to add context to the default Wave AI behavior:
465+
466+
```json
467+
{
468+
"ollama-with-context": {
469+
"display:name": "Ollama with Project Context",
470+
"ai:model": "llama3.3:70b",
471+
"ai:endpoint": "http://localhost:11434/v1/chat/completions",
472+
"ai:apitoken": "ollama",
473+
"ai:systemprompt": "Additional context: The user is working on a terminal emulator project called Wave Terminal. They may ask about SSH, file previews, or AI integration.",
474+
"ai:systempromptmode": "append"
475+
}
476+
}
477+
```
478+
479+
:::tip
480+
When using `"replace"` mode, your custom prompt completely replaces Wave's built-in prompt. Make sure to include any important instructions about tool usage and capabilities if you want the AI to maintain full Wave integration.
481+
:::
482+
433483
## Multiple Modes Example
434484

435485
You can define multiple AI modes and switch between them easily:
@@ -543,6 +593,8 @@ If you get "model not found" errors:
543593
| `ai:azureresourcename` | No | Azure resource name (for Azure providers) |
544594
| `ai:azuredeployment` | No | Azure deployment name (for `azure-legacy` provider) |
545595
| `ai:capabilities` | No | Array of supported capabilities: `"tools"`, `"images"`, `"pdfs"` |
596+
| `ai:systemprompt` | No | Custom system prompt to control AI behavior for this mode |
597+
| `ai:systempromptmode` | No | How to apply the custom system prompt: `"replace"` (default) or `"append"` |
546598
| `waveai:cloud` | No | Internal - for Wave Cloud AI configuration only |
547599
| `waveai:premium` | No | Internal - for Wave Cloud AI configuration only |
548600

frontend/types/gotypes.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ declare global {
3333
"ai:azuredeployment"?: string;
3434
"ai:capabilities"?: string[];
3535
"ai:switchcompat"?: string[];
36+
"ai:systemprompt"?: string;
37+
"ai:systempromptmode"?: string;
3638
"waveai:cloud"?: boolean;
3739
"waveai:premium"?: boolean;
3840
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"prettier-plugin-jsdoc": "^1.8.0",
6161
"prettier-plugin-organize-imports": "^4.3.0",
6262
"sass": "1.91.0",
63+
"sharp": "^0.34.5",
6364
"tailwindcss": "^4.2.1",
6465
"tailwindcss-animate": "^1.0.7",
6566
"ts-node": "^10.9.2",

pkg/aiusechat/uctypes/uctypes.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -251,20 +251,22 @@ type WaveContinueResponse struct {
251251

252252
// Wave Specific AI opts for configuration
253253
type AIOptsType struct {
254-
Provider string `json:"provider,omitempty"`
255-
APIType string `json:"apitype,omitempty"`
256-
Model string `json:"model"`
257-
APIToken string `json:"apitoken"`
258-
APIVersion string `json:"apiversion,omitempty"`
259-
Endpoint string `json:"endpoint,omitempty"`
260-
ProxyURL string `json:"proxyurl,omitempty"`
261-
MaxTokens int `json:"maxtokens,omitempty"`
262-
TimeoutMs int `json:"timeoutms,omitempty"`
263-
ThinkingLevel string `json:"thinkinglevel,omitempty"` // ThinkingLevelLow, ThinkingLevelMedium, or ThinkingLevelHigh
264-
Verbosity string `json:"verbosity,omitempty"` // Text verbosity level (OpenAI Responses API only, ignored by other backends)
265-
AIMode string `json:"aimode,omitempty"`
266-
Capabilities []string `json:"capabilities,omitempty"`
267-
WaveAIPremium bool `json:"waveaipremium,omitempty"`
254+
Provider string `json:"provider,omitempty"`
255+
APIType string `json:"apitype,omitempty"`
256+
Model string `json:"model"`
257+
APIToken string `json:"apitoken"`
258+
APIVersion string `json:"apiversion,omitempty"`
259+
Endpoint string `json:"endpoint,omitempty"`
260+
ProxyURL string `json:"proxyurl,omitempty"`
261+
MaxTokens int `json:"maxtokens,omitempty"`
262+
TimeoutMs int `json:"timeoutms,omitempty"`
263+
ThinkingLevel string `json:"thinkinglevel,omitempty"` // ThinkingLevelLow, ThinkingLevelMedium, or ThinkingLevelHigh
264+
Verbosity string `json:"verbosity,omitempty"` // Text verbosity level (OpenAI Responses API only, ignored by other backends)
265+
AIMode string `json:"aimode,omitempty"`
266+
Capabilities []string `json:"capabilities,omitempty"`
267+
WaveAIPremium bool `json:"waveaipremium,omitempty"`
268+
SystemPrompt string `json:"systemprompt,omitempty"` // Custom system prompt text
269+
SystemPromptMode string `json:"systempromptmode,omitempty"` // "replace" or "append" (defaults to "replace")
268270
}
269271

270272
func (opts AIOptsType) IsWaveProxy() bool {

pkg/aiusechat/usechat.go

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,34 @@ var (
4646
activeChats = ds.MakeSyncMap[bool]() // key is chatid
4747
)
4848

49-
func getSystemPrompt(apiType string, model string, isBuilder bool, hasToolsCapability bool, widgetAccess bool) []string {
49+
func getSystemPrompt(apiType string, model string, isBuilder bool, hasToolsCapability bool, widgetAccess bool, customPrompt string, customPromptMode string) []string {
5050
if isBuilder {
5151
return []string{}
5252
}
53+
// Normalize empty mode to "replace" (the default)
54+
if customPromptMode == "" {
55+
customPromptMode = "replace"
56+
}
57+
// If a custom system prompt is configured with "replace" mode, use it directly
58+
if customPrompt != "" && customPromptMode == "replace" {
59+
return []string{customPrompt}
60+
}
5361
useNoToolsPrompt := !hasToolsCapability || !widgetAccess
5462
basePrompt := SystemPromptText_OpenAI
5563
if useNoToolsPrompt {
5664
basePrompt = SystemPromptText_NoTools
5765
}
5866
modelLower := strings.ToLower(model)
5967
needsStrictToolAddOn, _ := regexp.MatchString(`(?i)\b(mistral|o?llama|qwen|mixtral|yi|phi|deepseek)\b`, modelLower)
68+
prompts := []string{basePrompt}
6069
if needsStrictToolAddOn && !useNoToolsPrompt {
61-
return []string{basePrompt, SystemPromptText_StrictToolAddOn}
70+
prompts = append(prompts, SystemPromptText_StrictToolAddOn)
6271
}
63-
return []string{basePrompt}
72+
// If a custom system prompt is configured with "append" mode, append it
73+
if customPrompt != "" && customPromptMode == "append" {
74+
prompts = append(prompts, customPrompt)
75+
}
76+
return prompts
6477
}
6578

6679
func isLocalEndpoint(endpoint string) bool {
@@ -106,6 +119,9 @@ func getWaveAISettings(premium bool, builderMode bool, rtInfo waveobj.ObjRTInfo,
106119
return nil, fmt.Errorf("no ai:endpoint configured for AI mode %s", aiMode)
107120
}
108121

122+
if config.SystemPromptMode != "" && config.SystemPromptMode != "replace" && config.SystemPromptMode != "append" {
123+
return nil, fmt.Errorf("invalid ai:systempromptmode %q: must be \"replace\" or \"append\"", config.SystemPromptMode)
124+
}
109125
thinkingLevel := config.ThinkingLevel
110126
if thinkingLevel == "" {
111127
thinkingLevel = uctypes.ThinkingLevelMedium
@@ -115,17 +131,19 @@ func getWaveAISettings(premium bool, builderMode bool, rtInfo waveobj.ObjRTInfo,
115131
verbosity = uctypes.VerbosityLevelMedium // default to medium
116132
}
117133
opts := &uctypes.AIOptsType{
118-
Provider: config.Provider,
119-
APIType: config.APIType,
120-
Model: config.Model,
121-
MaxTokens: maxTokens,
122-
ThinkingLevel: thinkingLevel,
123-
Verbosity: verbosity,
124-
AIMode: aiMode,
125-
Endpoint: baseUrl,
126-
ProxyURL: config.ProxyURL,
127-
Capabilities: config.Capabilities,
128-
WaveAIPremium: config.WaveAIPremium,
134+
Provider: config.Provider,
135+
APIType: config.APIType,
136+
Model: config.Model,
137+
MaxTokens: maxTokens,
138+
ThinkingLevel: thinkingLevel,
139+
Verbosity: verbosity,
140+
AIMode: aiMode,
141+
Endpoint: baseUrl,
142+
ProxyURL: config.ProxyURL,
143+
Capabilities: config.Capabilities,
144+
WaveAIPremium: config.WaveAIPremium,
145+
SystemPrompt: config.SystemPrompt,
146+
SystemPromptMode: config.SystemPromptMode,
129147
}
130148
if apiToken != "" {
131149
opts.APIToken = apiToken
@@ -692,7 +710,7 @@ func WaveAIPostMessageHandler(w http.ResponseWriter, r *http.Request) {
692710
BuilderId: req.BuilderId,
693711
BuilderAppId: req.BuilderAppId,
694712
}
695-
chatOpts.SystemPrompt = getSystemPrompt(chatOpts.Config.APIType, chatOpts.Config.Model, chatOpts.BuilderId != "", chatOpts.Config.HasCapability(uctypes.AICapabilityTools), chatOpts.WidgetAccess)
713+
chatOpts.SystemPrompt = getSystemPrompt(chatOpts.Config.APIType, chatOpts.Config.Model, chatOpts.BuilderId != "", chatOpts.Config.HasCapability(uctypes.AICapabilityTools), chatOpts.WidgetAccess, chatOpts.Config.SystemPrompt, chatOpts.Config.SystemPromptMode)
696714

697715
if req.TabId != "" {
698716
chatOpts.TabStateGenerator = func() (string, []uctypes.ToolDefinition, string, error) {

pkg/wconfig/settingsconfig.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ type AIModeConfigType struct {
301301
AzureDeployment string `json:"ai:azuredeployment,omitempty"`
302302
Capabilities []string `json:"ai:capabilities,omitempty" jsonschema:"enum=pdfs,enum=images,enum=tools"`
303303
SwitchCompat []string `json:"ai:switchcompat,omitempty"`
304+
SystemPrompt string `json:"ai:systemprompt,omitempty" jsonschema_description:"Custom system prompt to control AI behavior for this mode"`
305+
SystemPromptMode string `json:"ai:systempromptmode,omitempty" jsonschema:"enum=replace,enum=append" jsonschema_description:"How to apply the custom system prompt: replace (fully replace built-in prompt) or append (add after built-in prompt). Defaults to replace."`
304306
WaveAICloud bool `json:"waveai:cloud,omitempty"`
305307
WaveAIPremium bool `json:"waveai:premium,omitempty"`
306308
}

schema/waveai.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,18 @@
9595
},
9696
"type": "array"
9797
},
98+
"ai:systemprompt": {
99+
"type": "string",
100+
"description": "Custom system prompt to control AI behavior for this mode"
101+
},
102+
"ai:systempromptmode": {
103+
"type": "string",
104+
"enum": [
105+
"replace",
106+
"append"
107+
],
108+
"description": "How to apply the custom system prompt: replace (fully replace built-in prompt) or append (add after built-in prompt). Defaults to replace."
109+
},
98110
"waveai:cloud": {
99111
"type": "boolean"
100112
},

0 commit comments

Comments
 (0)