Skip to content

Commit da07ad2

Browse files
authored
feat: add Ask AI CE stubs and shared file wiring (#41692)
## Summary - Adds minimal CE stub files and EE re-export shims so the Ask AI feature (implemented entirely in the EE repo) can integrate with shared files - Updates existing CE stub signatures (`trigger.tsx`, `GPT/index.tsx`) to match the new API surface expected by shared files - Adds 9 new AI action type constants to `ReduxActionConstants.tsx` - Updates shared files (`CodeEditor`, `DynamicTextField`, IDE layouts) with the imports and conditional rendering hooks for Ask AI ## What this PR does NOT do This PR does **not** add any AI functionality to the community edition. All CE stubs return `null`, `false`, or `[]`. The actual Ask AI implementation lives entirely in the EE repo and will be enabled via the `license_ask_ai_config_enabled` feature flag. ## Changes ### New CE stub files (all return null/false) - `ce/selectors/aiAssistantSelectors.ts` - `ce/actions/aiAssistantActions.ts` - `ce/components/editorComponents/GPT/AISidePanel.tsx` - `ce/components/editorComponents/GlobalAISidePanel/index.tsx` ### New EE re-export shims - `ee/selectors/aiAssistantSelectors.ts` - `ee/actions/aiAssistantActions.ts` - `ee/components/editorComponents/GPT/AISidePanel.tsx` - `ee/components/editorComponents/GlobalAISidePanel/index.tsx` ### Updated existing CE files - `GPT/trigger.tsx` — added `isAISupportedMode()` export and `hasApiKey` parameter to `isAIEnabled()` - `GPT/index.tsx` — re-exports `AISidePanel` - `ReduxActionConstants.tsx` — 9 new AI action type constants ### Updated shared files - `CodeEditor/index.tsx` — AI selector/action imports, AI settings loading in `componentDidMount` - `CodeEditor/generateQuickCommands.tsx` — slash command updates - `DynamicTextField.tsx` — `AIAssisted` auto-detection for editor modes - `StaticLayout.tsx`, `AnimatedLayout.tsx` — render `<GlobalAISidePanel />` ## Test plan - [ ] CE build compiles without errors - [ ] No AI UI appears in CE (all stubs return null/false) - [ ] Existing CodeEditor functionality is unaffected - [ ] Shared files resolve `ee/` imports correctly through the re-export shims <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Global AI assistant side panel added to IDE layouts * AI settings and API key management added * Editors can open the AI panel with contextual editor information * **UI** * "Ask AI" command/button removed from beta state * AI trigger/button now shown only when AI is enabled and opens panel with context when available <!-- end of auto-generated comment: release notes by coderabbit.ai --> ## Automation /ok-to-test tags="@tag.All" <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/24498281185> > Commit: 73dce4d > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=24498281185&attempt=2" target="_blank">Cypress dashboard</a>. > Tags: `@tag.All` > Spec: > <hr>Thu, 16 Apr 2026 09:39:26 UTC <!-- end of auto-generated comment: Cypress test results -->
1 parent 6ca79d1 commit da07ad2

16 files changed

Lines changed: 380 additions & 133 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import type { ReduxAction } from "actions/ReduxActionTypes";
2+
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
3+
4+
export interface AIMessage {
5+
role: "user" | "assistant";
6+
content: string;
7+
timestamp: number;
8+
}
9+
10+
export interface UpdateAISettingsPayload {
11+
provider?: string;
12+
hasApiKey?: boolean;
13+
isEnabled?: boolean;
14+
}
15+
16+
export const updateAISettings = (
17+
payload: UpdateAISettingsPayload,
18+
): ReduxAction<UpdateAISettingsPayload> => ({
19+
type: ReduxActionTypes.UPDATE_AI_SETTINGS,
20+
payload,
21+
});
22+
23+
export interface FetchAIResponsePayload {
24+
prompt: string;
25+
context?: {
26+
functionName?: string;
27+
cursorLineNumber?: number;
28+
functionString?: string;
29+
mode?: string;
30+
currentValue?: string;
31+
/** Unpublished action id for server-side datasource schema resolution */
32+
entityId?: string;
33+
databaseSchema?: string;
34+
datasourceType?: string;
35+
};
36+
}
37+
38+
export const fetchAIResponse = (
39+
payload: FetchAIResponsePayload,
40+
): ReduxAction<FetchAIResponsePayload> => ({
41+
type: ReduxActionTypes.FETCH_AI_RESPONSE,
42+
payload,
43+
});
44+
45+
export const fetchAIResponseSuccess = (payload: {
46+
response: string;
47+
}): ReduxAction<{ response: string }> => ({
48+
type: ReduxActionTypes.FETCH_AI_RESPONSE_SUCCESS,
49+
payload,
50+
});
51+
52+
export const fetchAIResponseError = (payload: {
53+
error: string;
54+
}): ReduxAction<{ error: string }> => ({
55+
type: ReduxActionTypes.FETCH_AI_RESPONSE_ERROR,
56+
payload,
57+
});
58+
59+
export const loadAISettings = (): ReduxAction<undefined> => ({
60+
type: ReduxActionTypes.LOAD_AI_SETTINGS,
61+
payload: undefined,
62+
});
63+
64+
export const clearAIResponse = (): ReduxAction<undefined> => ({
65+
type: ReduxActionTypes.CLEAR_AI_RESPONSE,
66+
payload: undefined,
67+
});
68+
69+
export const openAIPanel = (): ReduxAction<undefined> => ({
70+
type: ReduxActionTypes.OPEN_AI_PANEL,
71+
payload: undefined,
72+
});
73+
74+
export const closeAIPanel = (): ReduxAction<undefined> => ({
75+
type: ReduxActionTypes.CLOSE_AI_PANEL,
76+
payload: undefined,
77+
});
78+
79+
export interface AIEditorContextPayload {
80+
functionName?: string;
81+
cursorLineNumber?: number;
82+
functionString?: string;
83+
mode?: string;
84+
currentValue?: string;
85+
editorId?: string;
86+
entityName?: string;
87+
/** Unpublished action id — sent to server for schema injection */
88+
entityId?: string;
89+
propertyPath?: string;
90+
}
91+
92+
export const updateAIContext = (
93+
context: AIEditorContextPayload,
94+
): ReduxAction<{ context: AIEditorContextPayload }> => ({
95+
type: ReduxActionTypes.UPDATE_AI_CONTEXT,
96+
payload: { context },
97+
});
98+
99+
// Combined action: updates context and opens panel
100+
export const openAIPanelWithContext = (
101+
context: AIEditorContextPayload,
102+
): ReduxAction<{ context: AIEditorContextPayload }> => ({
103+
type: ReduxActionTypes.OPEN_AI_PANEL_WITH_CONTEXT,
104+
payload: { context },
105+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface AISidePanelProps {
2+
onClose: () => void;
3+
}
4+
5+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
6+
export function AISidePanel(_props: AISidePanelProps) {
7+
return null;
8+
}

app/client/src/ce/components/editorComponents/GPT/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import type { EntityNavigationData } from "entities/DataTree/dataTreeTypes";
77
import React from "react";
88
import type CodeMirror from "codemirror";
99

10+
export { AISidePanel } from "./AISidePanel";
11+
1012
export type AIEditorContext = Partial<{
1113
functionName: string;
1214
cursorLineNumber: number;

app/client/src/ce/components/editorComponents/GPT/trigger.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@ import type { EntityTypeValue } from "ee/entities/DataTree/types";
55

66
export const APPSMITH_AI = "Appsmith AI";
77

8-
export function isAIEnabled(ff: FeatureFlags, mode: TEditorModes) {
8+
export function isAISupportedMode(mode: TEditorModes) {
9+
return false;
10+
}
11+
12+
export function isAIEnabled(
13+
ff: FeatureFlags,
14+
mode: TEditorModes,
15+
hasApiKey?: boolean,
16+
) {
917
return false;
1018
}
1119

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function GlobalAISidePanel() {
2+
return null;
3+
}

app/client/src/ce/constants/ReduxActionConstants.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,15 @@ const OneClickBindingActionTypes = {
13181318
const AIActionTypes = {
13191319
UPDATE_AI_CONTEXT: "UPDATE_AI_CONTEXT",
13201320
UPDATE_AI_TRIGGERED: "UPDATE_AI_TRIGGERED",
1321+
UPDATE_AI_SETTINGS: "UPDATE_AI_SETTINGS",
1322+
LOAD_AI_SETTINGS: "LOAD_AI_SETTINGS",
1323+
FETCH_AI_RESPONSE: "FETCH_AI_RESPONSE",
1324+
FETCH_AI_RESPONSE_SUCCESS: "FETCH_AI_RESPONSE_SUCCESS",
1325+
FETCH_AI_RESPONSE_ERROR: "FETCH_AI_RESPONSE_ERROR",
1326+
CLEAR_AI_RESPONSE: "CLEAR_AI_RESPONSE",
1327+
OPEN_AI_PANEL: "OPEN_AI_PANEL",
1328+
CLOSE_AI_PANEL: "CLOSE_AI_PANEL",
1329+
OPEN_AI_PANEL_WITH_CONTEXT: "OPEN_AI_PANEL_WITH_CONTEXT",
13211330
};
13221331

13231332
const PlatformActionErrorTypes = {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { DefaultRootState } from "react-redux";
2+
3+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
4+
export function getAIAssistantState(state: DefaultRootState) {
5+
return undefined;
6+
}
7+
8+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
9+
export function getHasAIApiKey(state: DefaultRootState): boolean {
10+
return false;
11+
}
12+
13+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
14+
export function getAIProvider(state: DefaultRootState): string | undefined {
15+
return undefined;
16+
}
17+
18+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
19+
export function getIsAILoading(state: DefaultRootState): boolean {
20+
return false;
21+
}
22+
23+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
24+
export function getAIMessages(state: DefaultRootState): never[] {
25+
return [];
26+
}
27+
28+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
29+
export function getAILastResponse(state: DefaultRootState): string | undefined {
30+
return undefined;
31+
}
32+
33+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
34+
export function getAIError(state: DefaultRootState): string | undefined {
35+
return undefined;
36+
}
37+
38+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
39+
export function getIsAIEnabled(state: DefaultRootState): boolean {
40+
return false;
41+
}
42+
43+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
44+
export function getIsAIConfigLoaded(state: DefaultRootState): boolean {
45+
return false;
46+
}
47+
48+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
49+
export function getIsAIPanelOpen(state: DefaultRootState): boolean {
50+
return false;
51+
}
52+
53+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
54+
export function getAIEditorContext(state: DefaultRootState): null {
55+
return null;
56+
}

app/client/src/components/editorComponents/CodeEditor/generateQuickCommands.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,6 @@ export const generateQuickCommands = (
400400
displayText: APPSMITH_AI,
401401
shortcut: Shortcuts.ASK_AI,
402402
triggerCompletionsPostPick: true,
403-
isBeta: true,
404403
action: () => {
405404
executeCommand({
406405
actionType: SlashCommand.ASK_AI,

0 commit comments

Comments
 (0)