Fallback to balanced smart context for mentioned apps because not sup… (#1886)
…ported in deep context Fixes https://github.com/dyad-sh/dyad/issues/1715 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Deep context now only applies with no app mentions; otherwise we fallback to balanced and pass the effective mode to the engine, with updated history limits and new e2e coverage. > > - **Smart Context behavior**: > - Deep mode activates only when no `@app:` mentions are present; otherwise uses `balanced`. > - Max chat turns set to 201 only for deep-without-mentions; otherwise use configured limit. > - **Engine integration**: > - Send effective smart context mode via `dyadSmartContextMode` and map to `dyad_options.smart_context_mode` in `llm_engine_provider`. > - Add `SmartContextMode` schema (`balanced|conservative|deep`) and use it in settings/types. > - Remove static `smartContextMode` from engine options; use per-request mode instead. > - **Tests**: > - Add e2e test `smart context deep - mention app should fallback to balanced` with snapshot asserting `smart_context_mode: "balanced"` and mentioned app files. > - **Misc**: > - Tighten app path validation regex in `rename-app` handler. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 5aada2bd246e297d7b35e36738f75c8531b897ae. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Fallback to balanced smart context when an app is mentioned in deep mode, since deep doesn’t support mentioned apps. This keeps context limits correct and updates engine config, with new e2e coverage. - **Bug Fixes** - Deep mode only applies when no apps are mentioned; otherwise we use balanced. - Max chat turns: 201 only for deep without mentions; otherwise use configured limit. - Plumb smart context mode via dyadSmartContextMode to the engine; add SmartContextMode schema. - Add e2e test to verify fallback in deep mode when an app is mentioned. <sup>Written for commit 5aada2bd246e297d7b35e36738f75c8531b897ae. Summary will update automatically on new commits.</sup> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -16,3 +16,25 @@ testSkipIfWindows("smart context deep - read write read", async ({ po }) => {
|
|||||||
await po.snapshotServerDump("request");
|
await po.snapshotServerDump("request");
|
||||||
await po.snapshotMessages({ replaceDumpPath: true });
|
await po.snapshotMessages({ replaceDumpPath: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testSkipIfWindows(
|
||||||
|
"smart context deep - mention app should fallback to balanced",
|
||||||
|
async ({ po }) => {
|
||||||
|
await po.setUpDyadPro();
|
||||||
|
|
||||||
|
// First, create an imported app.
|
||||||
|
await po.importApp("minimal-with-ai-rules");
|
||||||
|
|
||||||
|
await po.goToAppsTab();
|
||||||
|
const proModesDialog = await po.openProModesDialog({
|
||||||
|
location: "home-chat-input-container",
|
||||||
|
});
|
||||||
|
await proModesDialog.setSmartContextMode("deep");
|
||||||
|
await proModesDialog.close();
|
||||||
|
|
||||||
|
// Mentioned the imported app
|
||||||
|
await po.sendPrompt("[dump] @app:minimal-with-ai-rules hi");
|
||||||
|
|
||||||
|
await po.snapshotServerDump("request");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1205,7 +1205,7 @@ export function registerAppHandlers() {
|
|||||||
const pathChanged = appPath !== app.path;
|
const pathChanged = appPath !== app.path;
|
||||||
|
|
||||||
if (pathChanged) {
|
if (pathChanged) {
|
||||||
const invalidChars = /[<>:"|?*\/\\]/;
|
const invalidChars = /[<>:"|?*/\\]/;
|
||||||
const hasInvalidChars =
|
const hasInvalidChars =
|
||||||
invalidChars.test(appPath) || /[\x00-\x1f]/.test(appPath);
|
invalidChars.test(appPath) || /[\x00-\x1f]/.test(appPath);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
import { db } from "../../db";
|
import { db } from "../../db";
|
||||||
import { chats, messages } from "../../db/schema";
|
import { chats, messages } from "../../db/schema";
|
||||||
import { and, eq, isNull } from "drizzle-orm";
|
import { and, eq, isNull } from "drizzle-orm";
|
||||||
|
import type { SmartContextMode } from "../../lib/schemas";
|
||||||
import {
|
import {
|
||||||
constructSystemPrompt,
|
constructSystemPrompt,
|
||||||
readAiRules,
|
readAiRules,
|
||||||
@@ -516,6 +517,12 @@ ${componentSnippet}
|
|||||||
updatedChat.app.id, // Exclude current app
|
updatedChat.app.id, // Exclude current app
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isDeepContextEnabled =
|
||||||
|
isEngineEnabled &&
|
||||||
|
settings.proSmartContextOption === "deep" &&
|
||||||
|
mentionedAppsCodebases.length === 0;
|
||||||
|
logger.log(`isDeepContextEnabled: ${isDeepContextEnabled}`);
|
||||||
|
|
||||||
// Combine current app codebase with mentioned apps' codebases
|
// Combine current app codebase with mentioned apps' codebases
|
||||||
let otherAppsCodebaseInfo = "";
|
let otherAppsCodebaseInfo = "";
|
||||||
if (mentionedAppsCodebases.length > 0) {
|
if (mentionedAppsCodebases.length > 0) {
|
||||||
@@ -555,10 +562,9 @@ ${componentSnippet}
|
|||||||
//
|
//
|
||||||
// Limit chat history based on maxChatTurnsInContext setting
|
// Limit chat history based on maxChatTurnsInContext setting
|
||||||
// We add 1 because the current prompt counts as a turn.
|
// We add 1 because the current prompt counts as a turn.
|
||||||
const maxChatTurns =
|
const maxChatTurns = isDeepContextEnabled
|
||||||
isEngineEnabled && settings.proSmartContextOption === "deep"
|
? 201
|
||||||
? 201
|
: (settings.maxChatTurnsInContext || MAX_CHAT_TURNS_IN_CONTEXT) + 1;
|
||||||
: (settings.maxChatTurnsInContext || MAX_CHAT_TURNS_IN_CONTEXT) + 1;
|
|
||||||
|
|
||||||
// If we need to limit the context, we take only the most recent turns
|
// If we need to limit the context, we take only the most recent turns
|
||||||
let limitedMessageHistory = messageHistory;
|
let limitedMessageHistory = messageHistory;
|
||||||
@@ -812,19 +818,24 @@ This conversation includes one or more image attachments. When the user uploads
|
|||||||
logger.log("sending AI request");
|
logger.log("sending AI request");
|
||||||
}
|
}
|
||||||
let versionedFiles: VersionedFiles | undefined;
|
let versionedFiles: VersionedFiles | undefined;
|
||||||
if (isEngineEnabled && settings.proSmartContextOption === "deep") {
|
if (isDeepContextEnabled) {
|
||||||
versionedFiles = await getVersionedFiles({
|
versionedFiles = await getVersionedFiles({
|
||||||
files,
|
files,
|
||||||
chatMessages,
|
chatMessages,
|
||||||
appPath,
|
appPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const smartContextMode: SmartContextMode = isDeepContextEnabled
|
||||||
|
? "deep"
|
||||||
|
: // Keep in sync with getCurrentValue in ProModeSelector.tsx
|
||||||
|
"balanced";
|
||||||
// Build provider options with correct Google/Vertex thinking config gating
|
// Build provider options with correct Google/Vertex thinking config gating
|
||||||
const providerOptions: Record<string, any> = {
|
const providerOptions: Record<string, any> = {
|
||||||
"dyad-engine": {
|
"dyad-engine": {
|
||||||
dyadAppId: updatedChat.app.id,
|
dyadAppId: updatedChat.app.id,
|
||||||
dyadRequestId,
|
dyadRequestId,
|
||||||
dyadDisableFiles,
|
dyadDisableFiles,
|
||||||
|
dyadSmartContextMode: smartContextMode,
|
||||||
dyadFiles: versionedFiles ? undefined : files,
|
dyadFiles: versionedFiles ? undefined : files,
|
||||||
dyadVersionedFiles: versionedFiles,
|
dyadVersionedFiles: versionedFiles,
|
||||||
dyadMentionedApps: mentionedAppsCodebases.map(
|
dyadMentionedApps: mentionedAppsCodebases.map(
|
||||||
|
|||||||
@@ -92,8 +92,6 @@ export async function getModelClient(
|
|||||||
: settings.enableProLazyEditsMode &&
|
: settings.enableProLazyEditsMode &&
|
||||||
settings.proLazyEditsMode !== "v2",
|
settings.proLazyEditsMode !== "v2",
|
||||||
enableSmartFilesContext,
|
enableSmartFilesContext,
|
||||||
// Keep in sync with getCurrentValue in ProModeSelector.tsx
|
|
||||||
smartContextMode: settings.proSmartContextOption ?? "balanced",
|
|
||||||
enableWebSearch: settings.enableProWebSearch,
|
enableWebSearch: settings.enableProWebSearch,
|
||||||
},
|
},
|
||||||
settings,
|
settings,
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ or to provide a custom fetch implementation for e.g. testing.
|
|||||||
enableLazyEdits?: boolean;
|
enableLazyEdits?: boolean;
|
||||||
enableSmartFilesContext?: boolean;
|
enableSmartFilesContext?: boolean;
|
||||||
enableWebSearch?: boolean;
|
enableWebSearch?: boolean;
|
||||||
smartContextMode?: "balanced" | "conservative" | "deep";
|
|
||||||
};
|
};
|
||||||
settings: UserSettings;
|
settings: UserSettings;
|
||||||
}
|
}
|
||||||
@@ -149,6 +148,10 @@ export function createDyadEngine(
|
|||||||
if ("dyadMentionedApps" in parsedBody) {
|
if ("dyadMentionedApps" in parsedBody) {
|
||||||
delete parsedBody.dyadMentionedApps;
|
delete parsedBody.dyadMentionedApps;
|
||||||
}
|
}
|
||||||
|
const dyadSmartContextMode = parsedBody.dyadSmartContextMode;
|
||||||
|
if ("dyadSmartContextMode" in parsedBody) {
|
||||||
|
delete parsedBody.dyadSmartContextMode;
|
||||||
|
}
|
||||||
|
|
||||||
// Track and modify requestId with attempt number
|
// Track and modify requestId with attempt number
|
||||||
let modifiedRequestId = requestId;
|
let modifiedRequestId = requestId;
|
||||||
@@ -166,7 +169,7 @@ export function createDyadEngine(
|
|||||||
enable_lazy_edits: options.dyadOptions.enableLazyEdits,
|
enable_lazy_edits: options.dyadOptions.enableLazyEdits,
|
||||||
enable_smart_files_context:
|
enable_smart_files_context:
|
||||||
options.dyadOptions.enableSmartFilesContext,
|
options.dyadOptions.enableSmartFilesContext,
|
||||||
smart_context_mode: options.dyadOptions.smartContextMode,
|
smart_context_mode: dyadSmartContextMode,
|
||||||
enable_web_search: options.dyadOptions.enableWebSearch,
|
enable_web_search: options.dyadOptions.enableWebSearch,
|
||||||
app_id: dyadAppId,
|
app_id: dyadAppId,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -214,6 +214,12 @@ export type ReleaseChannel = z.infer<typeof ReleaseChannelSchema>;
|
|||||||
export const ZoomLevelSchema = z.enum(["90", "100", "110", "125", "150"]);
|
export const ZoomLevelSchema = z.enum(["90", "100", "110", "125", "150"]);
|
||||||
export type ZoomLevel = z.infer<typeof ZoomLevelSchema>;
|
export type ZoomLevel = z.infer<typeof ZoomLevelSchema>;
|
||||||
|
|
||||||
|
export const SmartContextModeSchema = z.enum([
|
||||||
|
"balanced",
|
||||||
|
"conservative",
|
||||||
|
"deep",
|
||||||
|
]);
|
||||||
|
export type SmartContextMode = z.infer<typeof SmartContextModeSchema>;
|
||||||
/**
|
/**
|
||||||
* Zod schema for user settings
|
* Zod schema for user settings
|
||||||
*/
|
*/
|
||||||
@@ -238,9 +244,7 @@ export const UserSettingsSchema = z.object({
|
|||||||
proLazyEditsMode: z.enum(["off", "v1", "v2"]).optional(),
|
proLazyEditsMode: z.enum(["off", "v1", "v2"]).optional(),
|
||||||
enableProSmartFilesContextMode: z.boolean().optional(),
|
enableProSmartFilesContextMode: z.boolean().optional(),
|
||||||
enableProWebSearch: z.boolean().optional(),
|
enableProWebSearch: z.boolean().optional(),
|
||||||
proSmartContextOption: z
|
proSmartContextOption: SmartContextModeSchema.optional(),
|
||||||
.enum(["balanced", "conservative", "deep"])
|
|
||||||
.optional(),
|
|
||||||
selectedTemplateId: z.string(),
|
selectedTemplateId: z.string(),
|
||||||
enableSupabaseWriteSqlMigration: z.boolean().optional(),
|
enableSupabaseWriteSqlMigration: z.boolean().optional(),
|
||||||
selectedChatMode: ChatModeSchema.optional(),
|
selectedChatMode: ChatModeSchema.optional(),
|
||||||
|
|||||||
Reference in New Issue
Block a user