Skip to content

Commit 22d4c24

Browse files
committed
frontend AI mode is now definitive, no more backend using rtinfo or defaulting
1 parent 4ee6c7d commit 22d4c24

4 files changed

Lines changed: 100 additions & 19 deletions

File tree

frontend/app/aipanel/aipanel.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,20 @@ const AIErrorMessage = memo(({ errorMessage, onClear }: AIErrorMessageProps) =>
219219

220220
AIErrorMessage.displayName = "AIErrorMessage";
221221

222+
const RTInfoModeFixer = memo(() => {
223+
const model = WaveAIModel.getInstance();
224+
const telemetryEnabled = jotai.useAtomValue(getSettingsKeyAtom("telemetry:enabled")) ?? false;
225+
const aiModeConfigs = jotai.useAtomValue(model.aiModeConfigs);
226+
227+
useEffect(() => {
228+
model.fixRTInfoMode();
229+
}, [telemetryEnabled, aiModeConfigs, model]);
230+
231+
return null;
232+
});
233+
234+
RTInfoModeFixer.displayName = "RTInfoModeFixer";
235+
222236
const AIPanelComponentInner = memo(() => {
223237
const [isDragOver, setIsDragOver] = useState(false);
224238
const [isReactDndDragOver, setIsReactDndDragOver] = useState(false);
@@ -248,6 +262,7 @@ const AIPanelComponentInner = memo(() => {
248262
msg,
249263
chatid: globalStore.get(model.chatId),
250264
widgetaccess: globalStore.get(model.widgetAccessAtom),
265+
aimode: globalStore.get(model.currentAIMode),
251266
};
252267
if (windowType === "builder") {
253268
body.builderid = globalStore.get(atoms.builderId);
@@ -529,6 +544,7 @@ const AIPanelComponentInner = memo(() => {
529544
onClick={handleClick}
530545
inert={!isPanelVisible ? true : undefined}
531546
>
547+
<RTInfoModeFixer />
532548
{(isDragOver || isReactDndDragOver) && allowAccess && <AIDragOverlay />}
533549
{showBlockMask && <AIBlockMask />}
534550
<AIPanelHeader />

frontend/app/aipanel/waveai-model.tsx

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ export class WaveAIModel {
5757
widgetAccessAtom!: jotai.Atom<boolean>;
5858
droppedFiles: jotai.PrimitiveAtom<DroppedFile[]> = jotai.atom([]);
5959
chatId!: jotai.PrimitiveAtom<string>;
60-
currentAIMode: jotai.PrimitiveAtom<string> = jotai.atom("waveai@balanced");
60+
currentAIMode!: jotai.PrimitiveAtom<string>;
6161
aiModeConfigs!: jotai.Atom<Record<string, AIModeConfigType>>;
62+
hasPremiumAtom!: jotai.Atom<boolean>;
63+
defaultModeAtom!: jotai.Atom<string>;
6264
errorMessage: jotai.PrimitiveAtom<string> = jotai.atom(null) as jotai.PrimitiveAtom<string>;
63-
modelAtom!: jotai.Atom<string>;
6465
containerWidth: jotai.PrimitiveAtom<number> = jotai.atom(0);
6566
codeBlockMaxWidth!: jotai.Atom<number>;
6667
inputAtom: jotai.PrimitiveAtom<string> = jotai.atom("");
@@ -77,16 +78,13 @@ export class WaveAIModel {
7778
private constructor(orefContext: ORef, inBuilder: boolean) {
7879
this.orefContext = orefContext;
7980
this.inBuilder = inBuilder;
80-
const defaultMode = globalStore.get(getSettingsKeyAtom("waveai:defaultmode")) ?? "waveai@balanced";
81-
this.currentAIMode = jotai.atom(defaultMode);
8281
this.chatId = jotai.atom(null) as jotai.PrimitiveAtom<string>;
83-
84-
this.modelAtom = jotai.atom((get) => {
85-
const modelMetaAtom = getOrefMetaKeyAtom(this.orefContext, "waveai:model");
86-
return get(modelMetaAtom) ?? "gpt-5.1";
87-
});
8882
this.aiModeConfigs = atoms.waveaiModeConfigAtom;
8983

84+
this.hasPremiumAtom = jotai.atom((get) => {
85+
const rateLimitInfo = get(atoms.waveAIRateLimitInfoAtom);
86+
return !rateLimitInfo || rateLimitInfo.unknown || rateLimitInfo.preq > 0;
87+
});
9088

9189
this.widgetAccessAtom = jotai.atom((get) => {
9290
if (this.inBuilder) {
@@ -115,6 +113,39 @@ export class WaveAIModel {
115113
}
116114
return get(WorkspaceLayoutModel.getInstance().panelVisibleAtom);
117115
});
116+
117+
this.defaultModeAtom = jotai.atom((get) => {
118+
const telemetryEnabled = get(getSettingsKeyAtom("telemetry:enabled")) ?? false;
119+
120+
if (this.inBuilder) {
121+
return telemetryEnabled ? "waveai@balanced" : "unknown";
122+
}
123+
124+
const aiModeConfigs = get(this.aiModeConfigs);
125+
const hasPremium = get(this.hasPremiumAtom);
126+
127+
const waveFallback = hasPremium ? "waveai@balanced" : "waveai@quick";
128+
let mode = get(getSettingsKeyAtom("waveai:defaultmode")) ?? waveFallback;
129+
130+
const modeExists = aiModeConfigs != null && mode in aiModeConfigs;
131+
132+
if (!modeExists) {
133+
if (telemetryEnabled) {
134+
mode = waveFallback;
135+
} else {
136+
return "unknown";
137+
}
138+
}
139+
140+
if (mode.startsWith("waveai@") && !telemetryEnabled) {
141+
return "unknown";
142+
}
143+
144+
return mode;
145+
});
146+
147+
const defaultMode = globalStore.get(this.defaultModeAtom);
148+
this.currentAIMode = jotai.atom(defaultMode);
118149
}
119150

120151
getPanelVisibleAtom(): jotai.Atom<boolean> {
@@ -350,6 +381,42 @@ export class WaveAIModel {
350381
});
351382
}
352383

384+
async fixRTInfoMode(): Promise<void> {
385+
const rtInfo = await RpcApi.GetRTInfoCommand(TabRpcClient, {
386+
oref: this.orefContext,
387+
});
388+
const mode = rtInfo?.["waveai:mode"];
389+
390+
if (mode == null) {
391+
return;
392+
}
393+
394+
let shouldClear = false;
395+
396+
if (mode.startsWith("waveai@")) {
397+
const telemetryEnabled = globalStore.get(getSettingsKeyAtom("telemetry:enabled")) ?? false;
398+
if (!telemetryEnabled) {
399+
shouldClear = true;
400+
}
401+
}
402+
403+
if (!shouldClear) {
404+
const aiModeConfigs = globalStore.get(this.aiModeConfigs);
405+
if (aiModeConfigs == null || !(mode in aiModeConfigs)) {
406+
shouldClear = true;
407+
}
408+
}
409+
410+
if (shouldClear) {
411+
const defaultMode = globalStore.get(this.defaultModeAtom);
412+
globalStore.set(this.currentAIMode, defaultMode);
413+
RpcApi.SetRTInfoCommand(TabRpcClient, {
414+
oref: this.orefContext,
415+
data: { "waveai:mode": null },
416+
});
417+
}
418+
}
419+
353420
async loadInitialChat(): Promise<WaveUIMessage[]> {
354421
const rtInfo = await RpcApi.GetRTInfoCommand(TabRpcClient, {
355422
oref: this.orefContext,

pkg/aiusechat/usechat-mode.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,6 @@ const (
3636

3737
func resolveAIMode(requestedMode string, premium bool) (string, *wconfig.AIModeConfigType, error) {
3838
mode := requestedMode
39-
if mode == "" {
40-
fullConfig := wconfig.GetWatcher().GetFullConfig()
41-
mode = fullConfig.Settings.WaveAiDefaultMode
42-
if mode == "" {
43-
mode = uctypes.AIModeBalanced
44-
}
45-
}
4639

4740
config, err := getAIModeConfig(mode)
4841
if err != nil {

pkg/aiusechat/usechat.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,15 @@ func isLocalEndpoint(endpoint string) bool {
7272
return strings.Contains(endpointLower, "localhost") || strings.Contains(endpointLower, "127.0.0.1")
7373
}
7474

75-
func getWaveAISettings(premium bool, builderMode bool, rtInfo waveobj.ObjRTInfo) (*uctypes.AIOptsType, error) {
75+
func getWaveAISettings(premium bool, builderMode bool, rtInfo waveobj.ObjRTInfo, aiModeName string) (*uctypes.AIOptsType, error) {
7676
maxTokens := DefaultMaxTokens
7777
if builderMode {
7878
maxTokens = BuilderMaxTokens
7979
}
8080
if rtInfo.WaveAIMaxOutputTokens > 0 {
8181
maxTokens = rtInfo.WaveAIMaxOutputTokens
8282
}
83-
aiMode, config, err := resolveAIMode(rtInfo.WaveAIMode, premium)
83+
aiMode, config, err := resolveAIMode(aiModeName, premium)
8484
if err != nil {
8585
return nil, err
8686
}
@@ -600,6 +600,7 @@ type PostMessageRequest struct {
600600
ChatID string `json:"chatid"`
601601
Msg uctypes.AIMessage `json:"msg"`
602602
WidgetAccess bool `json:"widgetaccess,omitempty"`
603+
AIMode string `json:"aimode,omitempty"`
603604
}
604605

605606
func WaveAIPostMessageHandler(w http.ResponseWriter, r *http.Request) {
@@ -642,7 +643,11 @@ func WaveAIPostMessageHandler(w http.ResponseWriter, r *http.Request) {
642643
// Get WaveAI settings
643644
premium := shouldUsePremium()
644645
builderMode := req.BuilderId != ""
645-
aiOpts, err := getWaveAISettings(premium, builderMode, *rtInfo)
646+
if req.AIMode == "" {
647+
http.Error(w, "aimode is required in request body", http.StatusBadRequest)
648+
return
649+
}
650+
aiOpts, err := getWaveAISettings(premium, builderMode, *rtInfo, req.AIMode)
646651
if err != nil {
647652
http.Error(w, fmt.Sprintf("WaveAI configuration error: %v", err), http.StatusInternalServerError)
648653
return

0 commit comments

Comments
 (0)