WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit ab36c6e

Browse files
cursoragent4ian
andcommitted
Refactor AI project creation and add initialize_project function
Co-authored-by: florian <[email protected]>
1 parent 37bed36 commit ab36c6e

File tree

2 files changed

+178
-36
lines changed

2 files changed

+178
-36
lines changed

newIDE/app/src/AiGeneration/AskAiEditorContainer.js

Lines changed: 156 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ import {
5050
sendAiRequestMessageSent,
5151
sendAiRequestStarted,
5252
} from '../Utils/Analytics/EventSender';
53-
import { useCreateAiProjectDialog } from './UseCreateAiProjectDialog';
5453
import { type ExampleShortHeader } from '../Utils/GDevelopServices/Example';
54+
import { listAllExamples } from '../Utils/GDevelopServices/Example';
55+
import UrlStorageProvider from '../ProjectsStorage/UrlStorageProvider';
5556
import { prepareAiUserContent } from './PrepareAiUserContent';
5657
import { AiRequestContext } from './AiRequestContext';
5758
import { getAiConfigurationPresetsWithAvailability } from './AiConfiguration';
@@ -199,7 +200,7 @@ const useProcessFunctionCalls = ({
199200
editorFunctionCallResults: getEditorFunctionCallResults(
200201
selectedAiRequest.id
201202
),
202-
})
203+
}).filter(functionCall => functionCall.name !== 'initialize_project')
203204
: [],
204205
[selectedAiRequest, getEditorFunctionCallResults]
205206
);
@@ -334,7 +335,6 @@ type Props = {|
334335
storageProvider: ?StorageProvider,
335336
setToolbar: (?React.Node) => void,
336337
i18n: I18nType,
337-
onCreateEmptyProject: (newProjectSetup: NewProjectSetup) => Promise<void>,
338338
onCreateProjectFromExample: (
339339
exampleShortHeader: ExampleShortHeader,
340340
newProjectSetup: NewProjectSetup,
@@ -410,7 +410,6 @@ export const AskAiEditor = React.memo<Props>(
410410
fileMetadata,
411411
storageProvider,
412412
i18n,
413-
onCreateEmptyProject,
414413
onCreateProjectFromExample,
415414
onOpenLayout,
416415
onSceneEventsModifiedOutsideEditor,
@@ -502,10 +501,7 @@ export const AskAiEditor = React.memo<Props>(
502501
setLastSendError,
503502
} = aiRequestStorage;
504503

505-
const {
506-
createAiProject,
507-
renderCreateAiProjectDialog,
508-
} = useCreateAiProjectDialog();
504+
509505

510506
const updateToolbar = React.useCallback(
511507
() => {
@@ -597,28 +593,7 @@ export const AskAiEditor = React.memo<Props>(
597593
} = newAiRequestOptions;
598594
startNewAiRequest(null);
599595

600-
// If no project is opened, create a new empty one if the request is for
601-
// the AI agent.
602-
if (mode === 'agent' && !project) {
603-
try {
604-
console.info(
605-
'No project opened, opening the dialog to create a new project.'
606-
);
607-
const result = await createAiProject();
608-
if (result === 'canceled') {
609-
return;
610-
}
611-
console.info('New project created - starting AI request.');
612-
startNewAiRequest({
613-
mode,
614-
userRequest,
615-
aiConfigurationPresetId,
616-
});
617-
} catch (error) {
618-
console.error('Error creating a new empty project:', error);
619-
}
620-
return;
621-
}
596+
// Do not create a project automatically anymore.
622597

623598
// Ensure the user has enough credits to pay for the request, or ask them
624599
// to buy some more.
@@ -920,6 +895,107 @@ export const AskAiEditor = React.memo<Props>(
920895
[onSendMessage]
921896
);
922897

898+
const processInitializeProjectFunctionCalls = React.useCallback(
899+
async (
900+
functionCalls: Array<AiRequestMessageAssistantFunctionCall>
901+
) => {
902+
if (!selectedAiRequest) return;
903+
904+
// Mark all provided calls as working.
905+
addEditorFunctionCallResults(
906+
selectedAiRequest.id,
907+
functionCalls.map(functionCall => ({
908+
status: 'working',
909+
call_id: functionCall.call_id,
910+
}))
911+
);
912+
913+
const results: Array<EditorFunctionCallResult> = [];
914+
915+
for (const functionCall of functionCalls) {
916+
let args: any = null;
917+
try {
918+
args = JSON.parse(functionCall.arguments);
919+
} catch (error) {
920+
results.push({
921+
status: 'finished',
922+
call_id: functionCall.call_id,
923+
success: false,
924+
output: { message: 'Invalid arguments (not a valid JSON string).' },
925+
});
926+
continue;
927+
}
928+
929+
const name: ?string = args && args.name;
930+
const slug: ?string = args && args.slug;
931+
if (!name || !slug) {
932+
results.push({
933+
status: 'finished',
934+
call_id: functionCall.call_id,
935+
success: false,
936+
output: { message: 'Missing required arguments: name and slug.' },
937+
});
938+
continue;
939+
}
940+
941+
try {
942+
const fetchedAllExamples = await listAllExamples();
943+
const exampleShortHeader: ?ExampleShortHeader = fetchedAllExamples.exampleShortHeaders.find(
944+
exampleShortHeader => exampleShortHeader.slug === slug
945+
);
946+
947+
if (!exampleShortHeader) {
948+
results.push({
949+
status: 'finished',
950+
call_id: functionCall.call_id,
951+
success: false,
952+
output: { message: `Unable to find the example with slug "${slug}".` },
953+
});
954+
continue;
955+
}
956+
957+
const newProjectSetup: NewProjectSetup = {
958+
storageProvider: UrlStorageProvider,
959+
saveAsLocation: null,
960+
projectName: name,
961+
dontOpenAnySceneOrProjectManager: true,
962+
};
963+
await onCreateProjectFromExample(
964+
exampleShortHeader,
965+
newProjectSetup,
966+
i18n,
967+
false
968+
);
969+
970+
results.push({
971+
status: 'finished',
972+
call_id: functionCall.call_id,
973+
success: true,
974+
output: { message: `Initialized project "${name}" from example "${slug}".` },
975+
});
976+
} catch (error) {
977+
console.error('Error initializing project from example:', error);
978+
results.push({
979+
status: 'finished',
980+
call_id: functionCall.call_id,
981+
success: false,
982+
output: { message: error && error.message ? error.message : 'Unknown error while initializing project.' },
983+
});
984+
}
985+
}
986+
987+
addEditorFunctionCallResults(selectedAiRequest.id, results);
988+
await onSendEditorFunctionCallResults(null);
989+
},
990+
[
991+
selectedAiRequest,
992+
addEditorFunctionCallResults,
993+
onCreateProjectFromExample,
994+
i18n,
995+
onSendEditorFunctionCallResults,
996+
]
997+
);
998+
923999
const onSendFeedback = React.useCallback(
9241000
async (
9251001
aiRequestId,
@@ -965,6 +1041,55 @@ export const AskAiEditor = React.memo<Props>(
9651041
onExtensionInstalled,
9661042
});
9671043

1044+
const onProcessFunctionCallsWithInit = React.useCallback(
1045+
async (
1046+
functionCalls: Array<AiRequestMessageAssistantFunctionCall>,
1047+
options: ?{| ignore?: boolean |}
1048+
) => {
1049+
const initializeCalls = functionCalls.filter(
1050+
functionCall => functionCall.name === 'initialize_project'
1051+
);
1052+
const otherCalls = functionCalls.filter(
1053+
functionCall => functionCall.name !== 'initialize_project'
1054+
);
1055+
1056+
if (initializeCalls.length > 0) {
1057+
await processInitializeProjectFunctionCalls(initializeCalls);
1058+
}
1059+
if (otherCalls.length > 0) {
1060+
await onProcessFunctionCalls(otherCalls, options);
1061+
}
1062+
},
1063+
[processInitializeProjectFunctionCalls, onProcessFunctionCalls]
1064+
);
1065+
1066+
// Auto-process initialize_project calls even without an opened project.
1067+
React.useEffect(
1068+
() => {
1069+
(async () => {
1070+
if (!selectedAiRequest) return;
1071+
const functionCallsToProcess = getFunctionCallsToProcess({
1072+
aiRequest: selectedAiRequest,
1073+
editorFunctionCallResults: getEditorFunctionCallResults(
1074+
selectedAiRequest.id
1075+
),
1076+
});
1077+
const initializeCalls = functionCallsToProcess.filter(
1078+
functionCall => functionCall.name === 'initialize_project'
1079+
);
1080+
if (initializeCalls.length === 0) return;
1081+
1082+
console.info('Processing initialize_project AI function calls...');
1083+
await processInitializeProjectFunctionCalls(initializeCalls);
1084+
})();
1085+
},
1086+
[
1087+
selectedAiRequest,
1088+
getEditorFunctionCallResults,
1089+
processInitializeProjectFunctionCalls,
1090+
]
1091+
);
1092+
9681093
return (
9691094
<>
9701095
<Paper square background="dark" style={styles.paper}>
@@ -988,7 +1113,7 @@ export const AskAiEditor = React.memo<Props>(
9881113
? 'upgrade'
9891114
: 'none'
9901115
}
991-
onProcessFunctionCalls={onProcessFunctionCalls}
1116+
onProcessFunctionCalls={onProcessFunctionCallsWithInit}
9921117
editorFunctionCallResults={
9931118
(selectedAiRequest &&
9941119
getEditorFunctionCallResults(selectedAiRequest.id)) ||
@@ -1017,10 +1142,6 @@ export const AskAiEditor = React.memo<Props>(
10171142
/>
10181143
</div>
10191144
</Paper>
1020-
{renderCreateAiProjectDialog({
1021-
onCreateEmptyProject,
1022-
onCreateProjectFromExample,
1023-
})}
10241145
<AskAiHistory
10251146
open={isHistoryOpen}
10261147
onClose={onCloseHistory}
@@ -1055,7 +1176,6 @@ export const renderAskAiEditorContainer = (
10551176
storageProvider={props.storageProvider}
10561177
setToolbar={props.setToolbar}
10571178
isActive={props.isActive}
1058-
onCreateEmptyProject={props.onCreateEmptyProject}
10591179
onCreateProjectFromExample={props.onCreateProjectFromExample}
10601180
onOpenLayout={props.onOpenLayout}
10611181
onSceneEventsModifiedOutsideEditor={

newIDE/app/src/EditorFunctions/index.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3461,6 +3461,27 @@ const addOrEditVariable: EditorFunction = {
34613461
},
34623462
};
34633463

3464+
const initializeProject: EditorFunction = {
3465+
renderForEditor: ({ args }) => {
3466+
const name = extractRequiredString(args, 'name');
3467+
const slug = extractRequiredString(args, 'slug');
3468+
3469+
return {
3470+
text: (
3471+
<Trans>
3472+
Initialize a new project "{name}" from example "{slug}".
3473+
</Trans>
3474+
),
3475+
};
3476+
},
3477+
// The actual project creation is handled by the Ask AI editor container,
3478+
// which can process this command even if no project is currently open.
3479+
// Returning a generic success here ensures graceful handling if invoked.
3480+
launchFunction: async () => {
3481+
return makeGenericSuccess('Project initialization requested.');
3482+
},
3483+
};
3484+
34643485
export const editorFunctions: { [string]: EditorFunction } = {
34653486
create_object: createObject,
34663487
inspect_object_properties: inspectObjectProperties,
@@ -3479,4 +3500,5 @@ export const editorFunctions: { [string]: EditorFunction } = {
34793500
inspect_scene_properties_layers_effects: inspectScenePropertiesLayersEffects,
34803501
change_scene_properties_layers_effects: changeScenePropertiesLayersEffects,
34813502
add_or_edit_variable: addOrEditVariable,
3503+
initialize_project: initializeProject,
34823504
};

0 commit comments

Comments
 (0)