fix: nextjs build issues and docling support for Powerpoint and Word

This commit is contained in:
sauravniraula 2025-08-05 23:19:59 +05:45
parent d0fd1b3aed
commit bf16491c73
No known key found for this signature in database
GPG key ID: 60FCC1B5A5E83326
11 changed files with 59 additions and 74 deletions

View file

@ -16,7 +16,7 @@ WORKDIR /app
# Set environment variables
ENV APP_DATA_DIRECTORY=/app_data
ENV TEMP_DIRECTORY=/tmp/presenton
ENV PYTHONPATH="${PYTHONPATH}:/app/servers/fastapi"
# ENV PYTHONPATH="${PYTHONPATH}:/app/servers/fastapi"
# Install ollama

View file

@ -19,7 +19,7 @@ RUN ls -a
# Set environment variables
ENV APP_DATA_DIRECTORY=/app_data
ENV TEMP_DIRECTORY=/tmp/presenton
ENV PYTHONPATH="${PYTHONPATH}:/app/servers/fastapi"
# ENV PYTHONPATH="${PYTHONPATH}:/app/servers/fastapi"
# Install ollama
RUN curl -fsSL http://ollama.com/install.sh | sh

View file

@ -1,4 +1,9 @@
from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.document_converter import (
DocumentConverter,
PdfFormatOption,
PowerpointFormatOption,
WordFormatOption,
)
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling.datamodel.base_models import InputFormat
@ -9,17 +14,18 @@ class DoclingService:
self.pipeline_options.do_ocr = False
self.converter = DocumentConverter(
allowed_formats=[InputFormat.PPTX, InputFormat.PDF, InputFormat.DOCX],
format_options={
InputFormat.DOCX: PdfFormatOption(
InputFormat.DOCX: WordFormatOption(
pipeline_options=self.pipeline_options,
),
InputFormat.PPTX: PdfFormatOption(
InputFormat.PPTX: PowerpointFormatOption(
pipeline_options=self.pipeline_options,
),
InputFormat.PDF: PdfFormatOption(
pipeline_options=self.pipeline_options,
),
}
},
)
def parse_to_markdown(self, file_path: str) -> str:

View file

@ -7,7 +7,7 @@ from models.presentation_structure_model import PresentationStructureModel
def get_presentation_outline_model_with_n_slides(n_slides: int):
class PresentationOutlineModelWithNSlides(PresentationOutlineModel):
slides: List[str] = Field(
description="Markdown content for each slide",
description="Markdown content for each slide in about 100 to 200 words",
min_items=n_slides,
max_items=n_slides,
)

View file

@ -1,12 +1,10 @@
import React from "react";
import { Button } from "@/components/ui/button";
import { SlideOutline } from "@/store/slices/presentationGeneration";
import { LoadingState, StreamState, LayoutGroup } from "../types/index";
interface GenerateButtonProps {
loadingState: LoadingState;
streamState: StreamState;
outlines: SlideOutline[] | null;
selectedLayoutGroup: LayoutGroup | null;
onSubmit: () => void;
}
@ -14,7 +12,6 @@ interface GenerateButtonProps {
const GenerateButton: React.FC<GenerateButtonProps> = ({
loadingState,
streamState,
outlines,
selectedLayoutGroup,
onSubmit
}) => {

View file

@ -85,7 +85,6 @@ const OutlinePage: React.FC = () => {
<GenerateButton
loadingState={loadingState}
streamState={streamState}
outlines={outlines}
selectedLayoutGroup={selectedLayoutGroup}
onSubmit={handleSubmit}
/>

View file

@ -1,9 +1,9 @@
import { useCallback } from "react";
import { useDispatch } from "react-redux";
import { arrayMove } from "@dnd-kit/sortable";
import { setOutlines, SlideOutline } from "@/store/slices/presentationGeneration";
import { setOutlines } from "@/store/slices/presentationGeneration";
export const useOutlineManagement = (outlines: SlideOutline[] | null) => {
export const useOutlineManagement = (outlines: string[] | null) => {
const dispatch = useDispatch();
const handleDragEnd = useCallback((event: any) => {
@ -12,8 +12,8 @@ export const useOutlineManagement = (outlines: SlideOutline[] | null) => {
if (!active || !over || !outlines) return;
if (active.id !== over.id) {
const oldIndex = outlines.findIndex((item) => item.title === active.id);
const newIndex = outlines.findIndex((item) => item.title === over.id);
const oldIndex = outlines.findIndex((item) => item === active.id);
const newIndex = outlines.findIndex((item) => item === over.id);
const reorderedArray = arrayMove(outlines, oldIndex, newIndex);
dispatch(setOutlines(reorderedArray));
}
@ -22,12 +22,7 @@ export const useOutlineManagement = (outlines: SlideOutline[] | null) => {
const handleAddSlide = useCallback(() => {
if (!outlines) return;
const newSlide: SlideOutline = {
title: "Outline title",
body: "Outline body",
};
const updatedOutlines = [...outlines, newSlide];
const updatedOutlines = [...outlines, "Outline title"];
dispatch(setOutlines(updatedOutlines));
}, [outlines, dispatch]);

View file

@ -2,7 +2,7 @@ import { useState, useCallback } from "react";
import { useDispatch } from "react-redux";
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { clearPresentationData, SlideOutline } from "@/store/slices/presentationGeneration";
import { clearPresentationData } from "@/store/slices/presentationGeneration";
import { PresentationGenerationApi } from "../../services/api/presentation-generation";
import { LayoutGroup, LoadingState, TABS } from "../types/index";
@ -15,7 +15,7 @@ const DEFAULT_LOADING_STATE: LoadingState = {
export const usePresentationGeneration = (
presentationId: string | null,
outlines: SlideOutline[] | null,
outlines: string[] | null,
selectedLayoutGroup: LayoutGroup | null,
setActiveTab: (tab: string) => void
) => {

View file

@ -3,19 +3,12 @@ import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "@/store/store";
import { Skeleton } from "@/components/ui/skeleton";
import { DashboardApi } from "@/app/(presentation-generator)/dashboard/api/dashboard";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { AlertCircle } from "lucide-react";
import { useGroupLayouts } from "../hooks/useGroupLayouts";
import { setPresentationData } from "@/store/slices/presentationGeneration";
import { DashboardApi } from "../services/api/dashboard";

View file

@ -1,11 +1,6 @@
import { Slide } from "@/app/(presentation-generator)/types/slide";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
export interface PresentationData {
id: string;
language: string;
@ -137,7 +132,7 @@ const presentationGenerationSlice = createSlice({
action.payload.slide;
}
},
// Update slide content at specific data path (for Tiptap text editing)
updateSlideContent: (
state,
@ -154,12 +149,12 @@ const presentationGenerationSlice = createSlice({
) {
const slide = state.presentationData.slides[action.payload.slideIndex];
const { dataPath, content } = action.payload;
// Helper function to set nested property value
const setNestedValue = (obj: any, path: string, value: string) => {
const keys = path.split(/[.\[\]]+/).filter(Boolean);
let current = obj;
// Navigate to the parent object
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
@ -178,7 +173,7 @@ const presentationGenerationSlice = createSlice({
current = current[index];
}
}
// Set the final value
const finalKey = keys[keys.length - 1];
if (isNaN(Number(finalKey))) {
@ -187,7 +182,7 @@ const presentationGenerationSlice = createSlice({
current[Number(finalKey)] = value;
}
};
// Update the slide content
if (dataPath && slide.content) {
setNestedValue(slide.content, dataPath, content);
@ -198,8 +193,8 @@ const presentationGenerationSlice = createSlice({
addNewSlide: (state, action: PayloadAction<{ slideData: any; index: number }>) => {
if (state.presentationData?.slides) {
// Insert the new slide at the specified index + 1 (after current slide)
state.presentationData.slides.splice(action.payload.index +1, 0, action.payload.slideData);
state.presentationData.slides.splice(action.payload.index + 1, 0, action.payload.slideData);
// Update indices for all slides to ensure they remain sequential
state.presentationData.slides = state.presentationData.slides.map(
(slide: any, idx: number) => ({
@ -227,12 +222,12 @@ const presentationGenerationSlice = createSlice({
) {
const slide = state.presentationData.slides[action.payload.slideIndex];
const { dataPath, imageUrl, prompt } = action.payload;
// Helper function to set nested property value for images
const setNestedImageValue = (obj: any, path: string, url: string, promptText?: string) => {
const keys = path.split(/[.\[\]]+/).filter(Boolean);
let current = obj;
// Navigate to the parent object
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
@ -249,33 +244,33 @@ const presentationGenerationSlice = createSlice({
current = current[index];
}
}
// Set the image properties
const finalKey = keys[keys.length - 1];
const target = isNaN(Number(finalKey)) ? current[finalKey] : current[Number(finalKey)];
// Preserve existing properties if the target already exists
const updatedValue = {
...(target && typeof target === 'object' ? target : {}),
__image_url__: url,
__image_prompt__: promptText || (target?.__image_prompt__) || ''
};
if (isNaN(Number(finalKey))) {
current[finalKey] = updatedValue;
} else {
current[Number(finalKey)] = updatedValue;
}
// Add debugging
console.log('Redux: Updated slide image at path:', path, 'with URL:', url);
};
// Update the slide image
if (dataPath && slide.content) {
setNestedImageValue(slide.content, dataPath, imageUrl, prompt);
}
// Also update the images array if it exists
if (slide.images && Array.isArray(slide.images)) {
const imageIndex = parseInt(dataPath.split('[')[1]?.split(']')[0]) || 0;
@ -293,7 +288,7 @@ const presentationGenerationSlice = createSlice({
itemIndex: number;
properties: any;
}>
) => {
) => {
if (
state.presentationData &&
state.presentationData.slides &&
@ -305,8 +300,8 @@ const presentationGenerationSlice = createSlice({
...slide.properties,
[itemIndex]: properties
};
}
}
},
// Update slide icon at specific data path
@ -326,12 +321,12 @@ const presentationGenerationSlice = createSlice({
) {
const slide = state.presentationData.slides[action.payload.slideIndex];
const { dataPath, iconUrl, query } = action.payload;
// Helper function to set nested property value for icons
const setNestedIconValue = (obj: any, path: string, url: string, queryText?: string) => {
const keys = path.split(/[.\[\]]+/).filter(Boolean);
let current = obj;
// Navigate to the parent object
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
@ -348,33 +343,33 @@ const presentationGenerationSlice = createSlice({
current = current[index];
}
}
// Set the icon properties
const finalKey = keys[keys.length - 1];
const target = isNaN(Number(finalKey)) ? current[finalKey] : current[Number(finalKey)];
// Preserve existing properties if the target already exists
const updatedValue = {
...(target && typeof target === 'object' ? target : {}),
__icon_url__: url,
__icon_query__: queryText || (target?.__icon_query__) || ''
};
if (isNaN(Number(finalKey))) {
current[finalKey] = updatedValue;
} else {
current[Number(finalKey)] = updatedValue;
}
// Add debugging
console.log('Redux: Updated slide icon at path:', path, 'with URL:', url);
};
// Update the slide icon
if (dataPath && slide.content) {
setNestedIconValue(slide.content, dataPath, iconUrl, query);
}
// Also update the icons array if it exists
if (slide.icons && Array.isArray(slide.icons)) {
const iconIndex = parseInt(dataPath.split('[')[1]?.split(']')[0]) || 0;

View file

@ -103,19 +103,19 @@ const startServers = async () => {
console.error("FastAPI process failed to start:", err);
});
const appmcpProcess = spawn(
"python",
["mcp_server.py", "--port", appmcpPort.toString()],
{
cwd: fastapiDir,
stdio: "inherit",
env: process.env,
},
);
// const appmcpProcess = spawn(
// "python",
// ["mcp_server.py", "--port", appmcpPort.toString()],
// {
// cwd: fastapiDir,
// stdio: "inherit",
// env: process.env,
// },
// );
appmcpProcess.on("error", (err) => {
console.error("App MCP process failed to start:", err);
});
// appmcpProcess.on("error", (err) => {
// console.error("App MCP process failed to start:", err);
// });
const nextjsProcess = spawn(
"npm",