zyratlo commented on code in PR #7601:
URL: https://github.com/apache/texera/pull/7601#discussion_r3777989276
##########
frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.ts:
##########
@@ -312,6 +322,117 @@ export class UserWorkflowComponent implements
AfterViewInit {
});
}
+ public get pythonNotebookMigrationEnabled(): boolean {
+ return this.config.env.pythonNotebookMigrationEnabled;
+ }
+
+ /**
+ * Open the AI-generate import modal from the dashboard. The modal collects
the notebook file and
+ * model and shows a loading state while generation runs (the requestImport
callback below).
+ */
+ public openAiGenerateModal(): void {
+ this.modalService.create<NotebookImportModalComponent,
NotebookImportModalData>({
+ nzTitle: "AI Generate Workflow from Python Notebook",
+ nzContent: NotebookImportModalComponent,
+ nzWidth: 700,
+ nzFooter: null,
+ nzCentered: true,
+ nzData: {
+ requestImport: (file, model) =>
this.generateWorkflowFromNotebook(file, model),
+ },
+ });
+ }
+
+ /**
+ * Generate a workflow from the uploaded notebook and open it. Runs entirely
on the dashboard while
+ * the modal shows a loading state: parse the notebook, send it to the LLM,
save the result as a new
+ * workflow, store the notebook and cell mapping, then navigate to the new
workflow. The workspace
+ * lays the generated operators out (via the autolayout query param) and
opens the notebook panel
+ * (driven by the workflow id). Resolves true so the modal closes on
success, or false (leaving the
+ * modal open with the selection intact) when the file is not a notebook or
generation fails.
+ */
+ private async generateWorkflowFromNotebook(file: NzUploadFile, model:
string): Promise<boolean> {
+ const fileExtension = file.name.split(".").pop()?.toLowerCase();
+ if (fileExtension !== "ipynb") {
+ this.notificationService.error("Please upload a valid Jupyter Notebook
(.ipynb) file.");
+ return false;
+ }
+ let notebook: Notebook;
+ try {
+ notebook = await this.notebookMigrationService.parseAndTagNotebook(file
as unknown as File);
+ } catch (error) {
+ this.notificationService.error("Failed to read the notebook file. Please
upload a valid .ipynb file.");
+ console.error("Notebook parse failed:", error);
+ return false;
+ }
+
+ let generated: { workflowContent: WorkflowContent; mappingContent:
MappingContent };
+ try {
+ generated = await
this.notebookMigrationService.sendToAIGenerateWorkflow(notebook, model);
+ } catch (error) {
+ this.notificationService.error("Error while communicating with the LLM,
check console for details.");
+ console.error("LLM generation failed:", error);
+ return false;
+ }
+
+ // Create the workflow. This is the commit point: persisting captures the
expensive LLM
+ // result. If it fails nothing was created, so returning false (letting
the user retry) is safe.
+ let wid: number;
+ try {
+ const createdWorkflow = await firstValueFrom(
+ this.workflowPersistService.createWorkflow(
+ generated.workflowContent,
+ this.deriveWorkflowName(file.name) + "_GENERATED_BY_LLM"
+ )
+ );
+ if (!createdWorkflow.workflow.wid) {
+ throw new Error("Created workflow has no wid.");
+ }
+ wid = createdWorkflow.workflow.wid;
+ } catch (error) {
+ this.notificationService.error("Failed to save the generated workflow,
check console for details.");
+ console.error("Saving the generated workflow failed:", error);
+ return false;
+ }
+
+ // Past the commit point the follow-up steps are best-effort: a transient
failure must not
+ // discard the created workflow or the LLM result, so we log/warn and
still open the workflow
+ // rather than force a full re-generation.
+ if (this.pid) {
+ try {
+ await
firstValueFrom(this.userProjectService.addWorkflowToProject(this.pid, wid));
+ } catch (error) {
+ console.error("Adding the generated workflow to the project failed:",
error);
+ }
+ }
+ try {
+ await firstValueFrom(
+ this.notebookMigrationService.storeNotebookAndMapping(wid,
generated.mappingContent, notebook)
Review Comment:
This vid=1 intentionally matches the existing fetch side in
jupyter-panel.service.ts, which also hardcodes vid=1 and carries a "Future
work: add dynamic fetching of current workflow vId" comment. The store and
fetch paths only need to agree on the same key for the panel to reattach, and
they do. Resolving vid to the real per-workflow version is a backend plus
fetch-path change that this dashboard PR does not cover; changing only the
store side would break the match. I have created #7636 to track this issue.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]