mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
2 Commits
cursor/sco
...
cursor/enh
Author | SHA1 | Date | |
---|---|---|---|
![]() |
366bfca4cd | ||
![]() |
d58cd9c340 |
130
AI_PROJECT_RESTORE_IMPLEMENTATION.md
Normal file
130
AI_PROJECT_RESTORE_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# AI Project Restore Implementation
|
||||
|
||||
## Overview
|
||||
This implementation provides automatic project saving and restoration capabilities for AI agent requests, leveraging GDevelop's existing cloud save features and version history system.
|
||||
|
||||
## Key Features
|
||||
|
||||
### 1. Automatic Project Save Before AI Agent Requests
|
||||
- When a user starts a new AI agent request, the project is automatically saved to create a cloud version
|
||||
- This only works for cloud projects (projects saved to GDevelop Cloud)
|
||||
- The current version ID is captured and stored with the AI request
|
||||
|
||||
### 2. Version-Based Restoration
|
||||
- Uses GDevelop's existing cloud version system instead of custom serialization
|
||||
- Leverages the `onOpenCloudProjectOnSpecificVersion` function from MainFrame
|
||||
- Provides seamless restoration to the exact state before AI agent modifications
|
||||
|
||||
### 3. Smart UI Integration
|
||||
- Restore button appears at the top of AI agent chats that have a stored initial version
|
||||
- Only visible for cloud projects with stored version information
|
||||
- Button shows loading state during restoration process
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Backend API Changes
|
||||
- Extended `AiRequest` type to include `initialProjectVersionId?: string | null`
|
||||
- This field stores the cloud project version ID captured before starting the agent
|
||||
|
||||
### Frontend Changes
|
||||
|
||||
#### 1. AskAiEditorContainer.js
|
||||
- Added `onOpenCloudProjectOnSpecificVersion` prop to enable version restoration
|
||||
- Modified AI request creation to save project and capture version ID for cloud projects
|
||||
- Added `onRestoreInitialProject` callback that uses cloud version system
|
||||
- Only attempts version capture for cloud projects (`storageProvider.internalName === 'Cloud'`)
|
||||
|
||||
#### 2. AiRequestChat/index.js
|
||||
- Added restore button UI at the top of agent chats
|
||||
- Added `isCloudProject` prop to control button visibility
|
||||
- Added loading state for restore operation
|
||||
- Proper error handling during restoration
|
||||
|
||||
#### 3. EditorFunctions/index.js
|
||||
- Extended `EditorCallbacks` type to include optional `onSave` function
|
||||
- Enables AI components to trigger project saves when needed
|
||||
|
||||
#### 4. MainFrame/index.js
|
||||
- Added `onOpenCloudProjectOnSpecificVersion` to editor props
|
||||
- This connects the AI editor to the existing version restoration system
|
||||
|
||||
## User Experience
|
||||
|
||||
### Starting an Agent Request
|
||||
1. User opens AI agent and submits a request
|
||||
2. System automatically saves the current project (creates a new version)
|
||||
3. Version ID is stored with the AI request for later restoration
|
||||
4. AI agent proceeds with modifications
|
||||
|
||||
### Restoring to Initial State
|
||||
1. User sees "Click here to restore the project as it was at the beginning" button
|
||||
2. Clicking the button triggers cloud version restoration
|
||||
3. Project is restored to the exact state before AI agent started
|
||||
4. All changes made by the AI agent are discarded
|
||||
|
||||
## Technical Advantages
|
||||
|
||||
### 1. Leverages Existing Infrastructure
|
||||
- Uses GDevelop's mature cloud save and version system
|
||||
- No custom serialization/deserialization code needed
|
||||
- Inherits all cloud storage reliability and error handling
|
||||
|
||||
### 2. Scalable and Reliable
|
||||
- Cloud versions are professionally managed and backed up
|
||||
- No local storage limitations or browser storage issues
|
||||
- Consistent across different devices and sessions
|
||||
|
||||
### 3. Version History Integration
|
||||
- Restored versions appear in the project's version history
|
||||
- Users can access version history features for AI-generated content
|
||||
- Seamless integration with existing version management workflow
|
||||
|
||||
## Limitations and Considerations
|
||||
|
||||
### 1. Cloud Projects Only
|
||||
- Feature only works for projects saved to GDevelop Cloud
|
||||
- Local projects cannot use this restoration feature
|
||||
- Clear messaging is provided when feature is unavailable
|
||||
|
||||
### 2. Version Storage
|
||||
- Relies on cloud project version creation during save
|
||||
- Version IDs are stored locally in the AI request metadata
|
||||
- If local storage is cleared, version reference may be lost
|
||||
|
||||
### 3. Network Dependency
|
||||
- Restoration requires internet connection for cloud access
|
||||
- Standard cloud storage network limitations apply
|
||||
|
||||
## Error Handling
|
||||
|
||||
### 1. Save Failures
|
||||
- If initial save fails, AI request continues without version storage
|
||||
- User is informed that restoration won't be available
|
||||
- Graceful degradation ensures AI functionality remains available
|
||||
|
||||
### 2. Restoration Failures
|
||||
- Comprehensive error logging for debugging
|
||||
- UI provides feedback during restoration process
|
||||
- Button disabled during restoration to prevent conflicts
|
||||
|
||||
### 3. Non-Cloud Projects
|
||||
- Restore functionality hidden for non-cloud projects
|
||||
- Clear console warnings when attempting unsupported operations
|
||||
- No impact on existing AI functionality for local projects
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### 1. Backend Integration
|
||||
- Could extend backend API to store `initialProjectVersionId` server-side
|
||||
- Would enable restoration across sessions and devices
|
||||
- Currently relies on local client-side storage
|
||||
|
||||
### 2. Local Project Support
|
||||
- Could implement local project snapshots using browser storage
|
||||
- Would require custom serialization for non-cloud projects
|
||||
- Currently prioritizes cloud projects for reliability
|
||||
|
||||
### 3. Enhanced UI
|
||||
- Could add confirmation dialogs for restoration
|
||||
- Might include preview of changes before restoration
|
||||
- Could integrate with version history UI components
|
@@ -80,6 +80,8 @@ type Props = {
|
||||
isAutoProcessingFunctionCalls: boolean,
|
||||
setAutoProcessFunctionCalls: boolean => void,
|
||||
onStartNewChat: () => void,
|
||||
onRestoreInitialProject?: () => Promise<void>,
|
||||
isCloudProject?: boolean,
|
||||
|
||||
onProcessFunctionCalls: (
|
||||
functionCalls: Array<AiRequestMessageAssistantFunctionCall>,
|
||||
@@ -234,6 +236,8 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
onSendMessage,
|
||||
onSendFeedback,
|
||||
onStartNewChat,
|
||||
onRestoreInitialProject,
|
||||
isCloudProject,
|
||||
quota,
|
||||
increaseQuotaOffering,
|
||||
lastSendError,
|
||||
@@ -249,6 +253,7 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
}: Props,
|
||||
ref
|
||||
) => {
|
||||
const [isRestoring, setIsRestoring] = React.useState(false);
|
||||
// TODO: store the default mode in the user preferences?
|
||||
const [newAiRequestMode, setNewAiRequestMode] = React.useState<
|
||||
'chat' | 'agent'
|
||||
@@ -634,6 +639,41 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
})}
|
||||
>
|
||||
<ScrollView ref={scrollViewRef} style={styles.chatScrollView}>
|
||||
{aiRequest &&
|
||||
aiRequest.mode === 'agent' &&
|
||||
aiRequest.initialProjectVersionId &&
|
||||
onRestoreInitialProject &&
|
||||
isCloudProject && (
|
||||
<Paper background="dark" variant="outlined" style={{ marginBottom: 8 }}>
|
||||
<Column>
|
||||
<LineStackLayout
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text size="body" color="secondary" noMargin>
|
||||
<Trans>Click here to restore the project as it was at the beginning</Trans>
|
||||
</Text>
|
||||
<RaisedButton
|
||||
size="small"
|
||||
color="secondary"
|
||||
label={<Trans>Restore project</Trans>}
|
||||
disabled={isRestoring}
|
||||
onClick={async () => {
|
||||
if (!onRestoreInitialProject) return;
|
||||
setIsRestoring(true);
|
||||
try {
|
||||
await onRestoreInitialProject();
|
||||
} catch (error) {
|
||||
console.error('Error in restore button:', error);
|
||||
} finally {
|
||||
setIsRestoring(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</LineStackLayout>
|
||||
</Column>
|
||||
</Paper>
|
||||
)}
|
||||
<ChatMessages
|
||||
aiRequest={aiRequest}
|
||||
onSendFeedback={onSendFeedback}
|
||||
|
@@ -19,6 +19,8 @@ import AuthenticatedUserContext from '../Profile/AuthenticatedUserContext';
|
||||
import { Toolbar } from './Toolbar';
|
||||
import { AskAiHistory } from './AskAiHistory';
|
||||
import { makeSimplifiedProjectBuilder } from '../EditorFunctions/SimplifiedProject/SimplifiedProject';
|
||||
import { type MessageDescriptor } from '../Utils/i18n/MessageDescriptor.flow';
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
canUpgradeSubscription,
|
||||
hasValidSubscriptionPlan,
|
||||
@@ -454,6 +456,13 @@ type Props = {|
|
||||
storageProvider: ?StorageProvider,
|
||||
setToolbar: (?React.Node) => void,
|
||||
i18n: I18nType,
|
||||
onSave?: () => Promise<void>,
|
||||
onOpenCloudProjectOnSpecificVersion?: ({|
|
||||
fileMetadata: FileMetadata,
|
||||
versionId: string,
|
||||
ignoreUnsavedChanges: boolean,
|
||||
openingMessage: MessageDescriptor,
|
||||
|}) => Promise<void>,
|
||||
onCreateEmptyProject: (newProjectSetup: NewProjectSetup) => Promise<void>,
|
||||
onCreateProjectFromExample: (
|
||||
exampleShortHeader: ExampleShortHeader,
|
||||
@@ -509,6 +518,8 @@ export const AskAiEditor = React.memo<Props>(
|
||||
fileMetadata,
|
||||
storageProvider,
|
||||
i18n,
|
||||
onSave,
|
||||
onOpenCloudProjectOnSpecificVersion,
|
||||
onCreateEmptyProject,
|
||||
onCreateProjectFromExample,
|
||||
onOpenLayout,
|
||||
@@ -520,8 +531,9 @@ export const AskAiEditor = React.memo<Props>(
|
||||
const editorCallbacks: EditorCallbacks = React.useMemo(
|
||||
() => ({
|
||||
onOpenLayout,
|
||||
onSave,
|
||||
}),
|
||||
[onOpenLayout]
|
||||
[onOpenLayout, onSave]
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -554,6 +566,34 @@ export const AskAiEditor = React.memo<Props>(
|
||||
[setSelectedAiRequestId]
|
||||
);
|
||||
|
||||
const onRestoreInitialProject = React.useCallback(
|
||||
async () => {
|
||||
if (!selectedAiRequest || !selectedAiRequest.initialProjectVersionId || !fileMetadata || !onOpenCloudProjectOnSpecificVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only restore for cloud projects
|
||||
if (!storageProvider || storageProvider.internalName !== 'Cloud') {
|
||||
console.warn('Project restoration is only available for cloud projects');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await onOpenCloudProjectOnSpecificVersion({
|
||||
fileMetadata,
|
||||
versionId: selectedAiRequest.initialProjectVersionId,
|
||||
ignoreUnsavedChanges: true,
|
||||
openingMessage: i18n._(t`Restoring project to initial state...`),
|
||||
});
|
||||
|
||||
console.info('Project restored to initial version');
|
||||
} catch (error) {
|
||||
console.error('Error restoring project to initial version:', error);
|
||||
}
|
||||
},
|
||||
[selectedAiRequest, fileMetadata, onOpenCloudProjectOnSpecificVersion, storageProvider, i18n]
|
||||
);
|
||||
|
||||
const onOpenHistory = React.useCallback(() => {
|
||||
setIsHistoryOpen(true);
|
||||
}, []);
|
||||
@@ -695,6 +735,20 @@ export const AskAiEditor = React.memo<Props>(
|
||||
|
||||
// Request is now ready to be started.
|
||||
try {
|
||||
// For agent mode on cloud projects, save the project and store initial version
|
||||
let initialProjectVersionId = null;
|
||||
if (mode === 'agent' && project && onSave && fileMetadata && storageProvider?.internalName === 'Cloud') {
|
||||
try {
|
||||
// Save the project first to create a version
|
||||
await onSave();
|
||||
// Store the current version ID for restoration
|
||||
initialProjectVersionId = fileMetadata.version || null;
|
||||
} catch (error) {
|
||||
console.error('Error saving project before starting AI agent:', error);
|
||||
// Continue anyway, but without initial version storage
|
||||
}
|
||||
}
|
||||
|
||||
const simplifiedProjectBuilder = makeSimplifiedProjectBuilder(gd);
|
||||
const simplifiedProjectJson = project
|
||||
? JSON.stringify(
|
||||
@@ -728,7 +782,13 @@ export const AskAiEditor = React.memo<Props>(
|
||||
|
||||
console.info('Successfully created a new AI request:', aiRequest);
|
||||
setSendingAiRequest(null, false);
|
||||
updateAiRequest(aiRequest.id, aiRequest);
|
||||
|
||||
// Add the initial project version to the AI request for local storage
|
||||
const aiRequestWithInitialVersion = {
|
||||
...aiRequest,
|
||||
initialProjectVersionId,
|
||||
};
|
||||
updateAiRequest(aiRequest.id, aiRequestWithInitialVersion);
|
||||
|
||||
// Select the new AI request just created - unless the user switched to another one
|
||||
// in the meantime.
|
||||
@@ -1027,6 +1087,8 @@ export const AskAiEditor = React.memo<Props>(
|
||||
i18n={i18n}
|
||||
editorCallbacks={editorCallbacks}
|
||||
onStartNewChat={onStartNewChat}
|
||||
onRestoreInitialProject={onRestoreInitialProject}
|
||||
isCloudProject={storageProvider?.internalName === 'Cloud'}
|
||||
/>
|
||||
</div>
|
||||
</Paper>
|
||||
@@ -1068,6 +1130,8 @@ export const renderAskAiEditorContainer = (
|
||||
storageProvider={props.storageProvider}
|
||||
setToolbar={props.setToolbar}
|
||||
isActive={props.isActive}
|
||||
onSave={props.onSave}
|
||||
onOpenCloudProjectOnSpecificVersion={props.onOpenCloudProjectOnSpecificVersion}
|
||||
onCreateEmptyProject={props.onCreateEmptyProject}
|
||||
onCreateProjectFromExample={props.onCreateProjectFromExample}
|
||||
onOpenLayout={props.onOpenLayout}
|
||||
|
@@ -110,6 +110,7 @@ export type EditorCallbacks = {|
|
||||
| 'none',
|
||||
|}
|
||||
) => void,
|
||||
onSave?: () => Promise<void>,
|
||||
|};
|
||||
|
||||
/**
|
||||
|
@@ -4028,6 +4028,7 @@ const MainFrame = (props: Props) => {
|
||||
hotReloadPreviewButtonProps,
|
||||
resourceManagementProps,
|
||||
onSave: saveProject,
|
||||
onOpenCloudProjectOnSpecificVersion,
|
||||
canSave,
|
||||
onCreateEventsFunction,
|
||||
openInstructionOrExpression,
|
||||
|
@@ -88,6 +88,7 @@ export type AiRequest = {
|
||||
|
||||
lastUserMessagePriceInCredits?: number | null,
|
||||
totalPriceInCredits?: number | null,
|
||||
initialProjectVersionId?: string | null,
|
||||
};
|
||||
|
||||
export type AiGeneratedEventStats = {
|
||||
|
Reference in New Issue
Block a user