issue: outlines still flickering

This commit is contained in:
sauravniraula 2025-08-06 19:34:53 +05:45
parent 2b5d58be8c
commit c840b8fce9
No known key found for this signature in database
GPG key ID: 60FCC1B5A5E83326
7 changed files with 46 additions and 66 deletions

View file

@ -39,8 +39,6 @@ class LLMClient:
# ? Disable thinking
def disable_thinking(self) -> bool:
if self.llm_provider != LLMProvider.CUSTOM:
return False
return parse_bool_or_none(get_disable_thinking_env()) or False
# ? Clients
@ -122,15 +120,14 @@ class LLMClient:
model: str,
messages: List[LLMMessage],
max_tokens: Optional[int] = None,
extra_body: Optional[dict] = None,
):
client: AsyncOpenAI = self._client
response = await client.chat.completions.create(
model=model,
messages=[message.model_dump() for message in messages],
max_completion_tokens=max_tokens,
extra_body={
"enable_thinking": not self.disable_thinking(),
},
extra_body=extra_body,
)
return response.choices[0].message.content
@ -185,7 +182,10 @@ class LLMClient:
async def _generate_custom(
self, model: str, messages: List[LLMMessage], max_tokens: Optional[int] = None
):
return await self._generate_openai(model, messages, max_tokens)
extra_body = {"enable_thinking": not self.disable_thinking()}
return await self._generate_openai(
model, messages, max_tokens, extra_body=extra_body
)
async def generate(
self,
@ -220,6 +220,7 @@ class LLMClient:
response_format: dict,
strict: bool = False,
max_tokens: Optional[int] = None,
extra_body: Optional[dict] = None,
):
client: AsyncOpenAI = self._client
use_tool_calls = self.use_tool_calls()
@ -245,9 +246,7 @@ class LLMClient:
),
},
max_completion_tokens=max_tokens,
extra_body={
"enable_thinking": not self.disable_thinking(),
},
extra_body=extra_body,
)
content = response.choices[0].message.content
else:
@ -267,9 +266,7 @@ class LLMClient:
],
tool_choice="required",
max_completion_tokens=max_tokens,
extra_body={
"enable_thinking": not self.disable_thinking(),
},
extra_body=extra_body,
)
tool_calls = response.choices[0].message.tool_calls
if tool_calls:
@ -359,8 +356,9 @@ class LLMClient:
strict: bool = False,
max_tokens: Optional[int] = None,
):
extra_body = {"enable_thinking": not self.disable_thinking()}
return await self._generate_openai_structured(
model, messages, response_format, strict, max_tokens
model, messages, response_format, strict, max_tokens, extra_body
)
async def generate_structured(
@ -406,15 +404,14 @@ class LLMClient:
model: str,
messages: List[LLMMessage],
max_tokens: Optional[int] = None,
extra_body: Optional[dict] = None,
):
client: AsyncOpenAI = self._client
async with client.chat.completions.stream(
model=model,
messages=[message.model_dump() for message in messages],
max_completion_tokens=max_tokens,
extra_body={
"enable_thinking": not self.disable_thinking(),
},
extra_body=extra_body,
) as stream:
async for event in stream:
if event.type == "content.delta":
@ -474,7 +471,8 @@ class LLMClient:
messages: List[LLMMessage],
max_tokens: Optional[int] = None,
):
return self._stream_openai(model, messages, max_tokens)
extra_body = {"enable_thinking": not self.disable_thinking()}
return self._stream_openai(model, messages, max_tokens, extra_body)
def stream(
self, model: str, messages: List[LLMMessage], max_tokens: Optional[int] = None
@ -499,6 +497,7 @@ class LLMClient:
response_format: dict,
strict: bool = False,
max_tokens: Optional[int] = None,
extra_body: Optional[dict] = None,
):
client: AsyncOpenAI = self._client
use_tool_calls = self.use_tool_calls()
@ -524,9 +523,7 @@ class LLMClient:
},
}
),
extra_body={
"enable_thinking": not self.disable_thinking(),
},
extra_body=extra_body,
) as stream:
async for event in stream:
if event.type == "content.delta":
@ -548,9 +545,7 @@ class LLMClient:
}
],
tool_choice="required",
extra_body={
"enable_thinking": not self.disable_thinking(),
},
extra_body=extra_body,
) as stream:
async for event in stream:
if event.type == "tool_calls.function.arguments.delta":
@ -630,8 +625,9 @@ class LLMClient:
strict: bool = False,
max_tokens: Optional[int] = None,
):
extra_body = {"enable_thinking": not self.disable_thinking()}
return self._stream_openai_structured(
model, messages, response_format, strict, max_tokens
model, messages, response_format, strict, max_tokens, extra_body
)
def stream_structured(

View file

@ -1,10 +1,10 @@
import React from "react";
import { Button } from "@/components/ui/button";
import { LoadingState, StreamState, LayoutGroup } from "../types/index";
import { LoadingState, LayoutGroup } from "../types/index";
interface GenerateButtonProps {
loadingState: LoadingState;
streamState: StreamState;
streamState: { isStreaming: boolean, isLoading: boolean };
selectedLayoutGroup: LayoutGroup | null;
onSubmit: () => void;
}

View file

@ -32,7 +32,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
onDragEnd,
onAddSlide
}) => {
console.log('isLoading', isLoading)
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {

View file

@ -1,14 +0,0 @@
import React from "react";
const PageHeader: React.FC = () => (
<div className="mb-8">
{/* <h4 className="text-2xl font-bold mb-2 text-gray-900">
Customize Your Presentation
</h4> */}
{/* <p className="text-gray-600">
Review your outline and select a layout style for your presentation.
</p> */}
</div>
);
export default PageHeader;

View file

@ -3,18 +3,15 @@ import { useDispatch, useSelector } from "react-redux";
import { toast } from "sonner";
import { setOutlines } from "@/store/slices/presentationGeneration";
import { jsonrepair } from "jsonrepair";
import { StreamState } from "../types/index";
import { RootState } from "@/store/store";
const DEFAULT_STREAM_STATE: StreamState = {
isStreaming: false,
isLoading: true,
};
export const useOutlineStreaming = (presentationId: string | null) => {
const dispatch = useDispatch();
const { outlines } = useSelector((state: RootState) => state.presentationGeneration);
const [streamState, setStreamState] = useState<StreamState>(DEFAULT_STREAM_STATE);
const [isStreaming, setIsStreaming] = useState(true);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (!presentationId || outlines.length > 0) return;
@ -23,8 +20,8 @@ export const useOutlineStreaming = (presentationId: string | null) => {
let accumulatedChunks = "";
const initializeStream = async () => {
setStreamState({ isStreaming: true, isLoading: true });
setIsStreaming(true)
setIsLoading(true)
try {
eventSource = new EventSource(
`/api/v1/ppt/outlines/stream?presentation_id=${presentationId}`
@ -40,7 +37,7 @@ export const useOutlineStreaming = (presentationId: string | null) => {
const partialData = JSON.parse(repairedJson);
if (partialData.slides) {
dispatch(setOutlines(partialData.slides));
setStreamState(prev => ({ ...prev, isLoading: false }));
setIsLoading(false)
}
} catch (error) {
// JSON isn't complete yet, continue accumulating
@ -51,8 +48,9 @@ export const useOutlineStreaming = (presentationId: string | null) => {
try {
const outlinesData: string[] = data.presentation.outlines.slides;
dispatch(setOutlines(outlinesData));
setStreamState({ isStreaming: false, isLoading: false });
eventSource.close();
setIsStreaming(false)
setIsLoading(false)
eventSource.close();
} catch (error) {
console.error("Error parsing accumulated chunks:", error);
toast.error("Failed to parse presentation data");
@ -62,11 +60,13 @@ export const useOutlineStreaming = (presentationId: string | null) => {
break;
case "closing":
setStreamState({ isStreaming: false, isLoading: false });
setIsStreaming(false)
setIsLoading(false)
eventSource.close();
break;
case "error":
setStreamState({ isStreaming: false, isLoading: false });
setIsStreaming(false)
setIsLoading(false)
eventSource.close();
toast.error('Error in outline streaming',
{
@ -78,18 +78,21 @@ export const useOutlineStreaming = (presentationId: string | null) => {
});
eventSource.onerror = () => {
setStreamState({ isStreaming: false, isLoading: false });
setIsStreaming(false)
setIsLoading(false)
eventSource.close();
toast.error("Failed to connect to the server. Please try again.");
};
} catch (error) {
setStreamState({ isStreaming: false, isLoading: false });
setIsStreaming(false)
setIsLoading(false)
toast.error("Failed to initialize connection");
}finally{
setStreamState({ isStreaming: false, isLoading: false });
} finally {
setIsStreaming(false)
setIsLoading(false)
}
};
initializeStream();
initializeStream();
return () => {
if (eventSource) {
eventSource.close();
@ -97,5 +100,5 @@ export const useOutlineStreaming = (presentationId: string | null) => {
};
}, [presentationId, dispatch]);
return streamState;
return { isStreaming, isLoading };
};

View file

@ -14,10 +14,7 @@ export interface LoadingState {
duration: number;
}
export interface StreamState {
isStreaming: boolean;
isLoading: boolean;
}
export const TABS = {
OUTLINE: 'outline',

View file

@ -131,7 +131,6 @@ const UploadPage = () => {
config,
files: responses,
}));
dispatch(clearOutlines());
router.push("/documents-preview");
};
@ -155,7 +154,6 @@ const UploadPage = () => {
});
dispatch(setPresentationId(createResponse.id));
dispatch(clearOutlines());
router.push("/outline");
};