mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
This feature a new modal endpoint, and a complete new way to build the summary. ## SummaryBuilder The summary builder is based on conversational model, where an exchange between the model and the user is made. This allow more context inclusion and a better respect of the rules. It requires an endpoint with OpenAI-like completions endpoint (/v1/chat/completions) ## vLLM Hermes3 Unlike previous deployment, this one use vLLM, which gives OpenAI-like completions endpoint out of the box. It could also handle guided JSON generation, so jsonformer is not needed. But, the model is quite good to follow JSON schema if asked in the prompt. ## Conversion of long/short into summary builder The builder is identifying participants, find key subjects, get a summary for each, then get a quick recap. The quick recap is used as a short_summary, while the markdown including the quick recap + key subjects + summaries are used for the long_summary. This is why the nextjs component has to be updated, to correctly style h1 and keep the new line of the markdown.
161 lines
4.0 KiB
TypeScript
161 lines
4.0 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import React from "react";
|
|
import Markdown from "react-markdown";
|
|
import "../../../styles/markdown.css";
|
|
import {
|
|
GetTranscript,
|
|
GetTranscriptTopic,
|
|
UpdateTranscript,
|
|
} from "../../../api";
|
|
import useApi from "../../../lib/useApi";
|
|
import {
|
|
Flex,
|
|
Heading,
|
|
IconButton,
|
|
Button,
|
|
Textarea,
|
|
Spacer,
|
|
} from "@chakra-ui/react";
|
|
import { FaPen } from "react-icons/fa";
|
|
import { useError } from "../../../(errors)/errorContext";
|
|
import ShareAndPrivacy from "../shareAndPrivacy";
|
|
|
|
type FinalSummaryProps = {
|
|
transcriptResponse: GetTranscript;
|
|
topicsResponse: GetTranscriptTopic[];
|
|
onUpdate?: (newSummary) => void;
|
|
};
|
|
|
|
export default function FinalSummary(props: FinalSummaryProps) {
|
|
const finalSummaryRef = useRef<HTMLParagraphElement>(null);
|
|
|
|
const [isEditMode, setIsEditMode] = useState(false);
|
|
const [preEditSummary, setPreEditSummary] = useState("");
|
|
const [editedSummary, setEditedSummary] = useState("");
|
|
|
|
const api = useApi();
|
|
|
|
const { setError } = useError();
|
|
|
|
useEffect(() => {
|
|
setEditedSummary(props.transcriptResponse?.long_summary || "");
|
|
}, [props.transcriptResponse?.long_summary]);
|
|
|
|
if (!props.topicsResponse || !props.transcriptResponse) {
|
|
return null;
|
|
}
|
|
|
|
const updateSummary = async (newSummary: string, transcriptId: string) => {
|
|
try {
|
|
const requestBody: UpdateTranscript = {
|
|
long_summary: newSummary,
|
|
};
|
|
const updatedTranscript = await api?.v1TranscriptUpdate({
|
|
transcriptId,
|
|
requestBody,
|
|
});
|
|
if (props.onUpdate) {
|
|
props.onUpdate(newSummary);
|
|
}
|
|
console.log("Updated long summary:", updatedTranscript);
|
|
} catch (err) {
|
|
console.error("Failed to update long summary:", err);
|
|
setError(err, "Failed to update long summary.");
|
|
}
|
|
};
|
|
|
|
const onEditClick = () => {
|
|
setPreEditSummary(editedSummary);
|
|
setIsEditMode(true);
|
|
};
|
|
|
|
const onDiscardClick = () => {
|
|
setEditedSummary(preEditSummary);
|
|
setIsEditMode(false);
|
|
};
|
|
|
|
const onSaveClick = () => {
|
|
updateSummary(editedSummary, props.transcriptResponse.id);
|
|
setIsEditMode(false);
|
|
};
|
|
|
|
const handleTextAreaKeyDown = (e: React.KeyboardEvent) => {
|
|
if (e.key === "Escape") {
|
|
onDiscardClick();
|
|
}
|
|
|
|
if (e.key === "Enter" && e.shiftKey) {
|
|
onSaveClick();
|
|
e.preventDefault(); // prevent the default action of adding a new line
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Flex
|
|
direction="column"
|
|
maxH={"100%"}
|
|
h={"100%"}
|
|
overflowY={isEditMode ? "hidden" : "auto"}
|
|
pb={4}
|
|
position="relative"
|
|
>
|
|
<Flex
|
|
dir="row"
|
|
justify="start"
|
|
align="center"
|
|
wrap={"wrap-reverse"}
|
|
position={isEditMode ? "inherit" : "absolute"}
|
|
right="0"
|
|
>
|
|
{isEditMode && (
|
|
<>
|
|
<Heading size={{ base: "md" }}>Summary</Heading>
|
|
<Spacer />
|
|
<Button
|
|
onClick={onDiscardClick}
|
|
colorScheme="gray"
|
|
variant={"text"}
|
|
>
|
|
Discard
|
|
</Button>
|
|
<Button onClick={onSaveClick} colorScheme="blue">
|
|
Save
|
|
</Button>
|
|
</>
|
|
)}
|
|
{!isEditMode && (
|
|
<>
|
|
<Spacer />
|
|
<IconButton
|
|
icon={<FaPen />}
|
|
aria-label="Edit Summary"
|
|
onClick={onEditClick}
|
|
/>
|
|
<ShareAndPrivacy
|
|
finalSummaryRef={finalSummaryRef}
|
|
transcriptResponse={props.transcriptResponse}
|
|
topicsResponse={props.topicsResponse}
|
|
/>
|
|
</>
|
|
)}
|
|
</Flex>
|
|
|
|
{isEditMode ? (
|
|
<Textarea
|
|
value={editedSummary}
|
|
onChange={(e) => setEditedSummary(e.target.value)}
|
|
className="markdown"
|
|
onKeyDown={(e) => handleTextAreaKeyDown(e)}
|
|
h={"100%"}
|
|
resize={"none"}
|
|
mt={2}
|
|
/>
|
|
) : (
|
|
<div ref={finalSummaryRef} className="markdown">
|
|
<Markdown>{editedSummary}</Markdown>
|
|
</div>
|
|
)}
|
|
</Flex>
|
|
);
|
|
}
|