Compare commits
No commits in common. "0216515492318a2f1953a223f73ea1c0e329345c" and "c946cc202a4dc8db8b0f1a4a7bd3157601a61a2d" have entirely different histories.
0216515492
...
c946cc202a
9
frontend/package-lock.json
generated
9
frontend/package-lock.json
generated
@ -24,7 +24,6 @@
|
|||||||
"@types/react": "^19.0.12",
|
"@types/react": "^19.0.12",
|
||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.0.4",
|
||||||
"@uiw/react-json-view": "^2.0.0-alpha.31",
|
"@uiw/react-json-view": "^2.0.0-alpha.31",
|
||||||
"jsonrepair": "^3.12.0",
|
|
||||||
"mermaid": "^11.6.0",
|
"mermaid": "^11.6.0",
|
||||||
"mui-markdown": "^2.0.1",
|
"mui-markdown": "^2.0.1",
|
||||||
"prism-react-renderer": "^2.4.1",
|
"prism-react-renderer": "^2.4.1",
|
||||||
@ -14171,14 +14170,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsonrepair": {
|
|
||||||
"version": "3.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.12.0.tgz",
|
|
||||||
"integrity": "sha512-SWfjz8SuQ0wZjwsxtSJ3Zy8vvLg6aO/kxcp9TWNPGwJKgTZVfhNEQBMk/vPOpYCDFWRxD6QWuI6IHR1t615f0w==",
|
|
||||||
"bin": {
|
|
||||||
"jsonrepair": "bin/cli.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jsx-ast-utils": {
|
"node_modules/jsx-ast-utils": {
|
||||||
"version": "3.3.5",
|
"version": "3.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
"@types/react": "^19.0.12",
|
"@types/react": "^19.0.12",
|
||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.0.4",
|
||||||
"@uiw/react-json-view": "^2.0.0-alpha.31",
|
"@uiw/react-json-view": "^2.0.0-alpha.31",
|
||||||
"jsonrepair": "^3.12.0",
|
|
||||||
"mermaid": "^11.6.0",
|
"mermaid": "^11.6.0",
|
||||||
"mui-markdown": "^2.0.1",
|
"mui-markdown": "^2.0.1",
|
||||||
"prism-react-renderer": "^2.4.1",
|
"prism-react-renderer": "^2.4.1",
|
||||||
|
@ -19,12 +19,11 @@ interface ChatBubbleProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
expanded?: boolean;
|
expanded?: boolean;
|
||||||
expandable?: boolean;
|
|
||||||
onExpand?: () => void;
|
onExpand?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ChatBubble(props: ChatBubbleProps) {
|
function ChatBubble(props: ChatBubbleProps) {
|
||||||
const { role, children, sx, className, title, onExpand, expandable }: ChatBubbleProps = props;
|
const { role, children, sx, className, title, onExpand }: ChatBubbleProps = props;
|
||||||
const [expanded, setExpanded] = useState<boolean>((props.expanded === undefined) ? true : props.expanded);
|
const [expanded, setExpanded] = useState<boolean>((props.expanded === undefined) ? true : props.expanded);
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -49,7 +48,15 @@ function ChatBubble(props: ChatBubbleProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles: any = {
|
const styles = {
|
||||||
|
'user': {
|
||||||
|
...defaultStyle,
|
||||||
|
backgroundColor: theme.palette.background.default, // Warm Gray (#D3CDBF)
|
||||||
|
border: `1px solid ${theme.palette.custom.highlight}`, // Golden Ochre (#D4A017)
|
||||||
|
borderRadius: `${defaultRadius} ${defaultRadius} 0 ${defaultRadius}`, // Rounded, flat bottom-right for user
|
||||||
|
alignSelf: 'flex-end', // Right-aligned for user
|
||||||
|
color: theme.palette.primary.main, // Midnight Blue (#1A2536) for text
|
||||||
|
},
|
||||||
'assistant': {
|
'assistant': {
|
||||||
...defaultStyle,
|
...defaultStyle,
|
||||||
backgroundColor: theme.palette.primary.main, // Midnight Blue (#1A2536)
|
backgroundColor: theme.palette.primary.main, // Midnight Blue (#1A2536)
|
||||||
@ -57,35 +64,17 @@ function ChatBubble(props: ChatBubbleProps) {
|
|||||||
borderRadius: `${defaultRadius} ${defaultRadius} ${defaultRadius} 0`, // Rounded, flat bottom-left for assistant
|
borderRadius: `${defaultRadius} ${defaultRadius} ${defaultRadius} 0`, // Rounded, flat bottom-left for assistant
|
||||||
color: theme.palette.primary.contrastText, // Warm Gray (#D3CDBF) for text
|
color: theme.palette.primary.contrastText, // Warm Gray (#D3CDBF) for text
|
||||||
},
|
},
|
||||||
'content': {
|
'system': {
|
||||||
...defaultStyle,
|
...defaultStyle,
|
||||||
backgroundColor: '#F5F2EA', // Light cream background for easy reading
|
backgroundColor: '#EDEAE0', // Soft warm gray that plays nice with #D3CDBF
|
||||||
border: `1px solid ${theme.palette.custom.highlight}`, // Golden Ochre border
|
border: `1px dashed ${theme.palette.custom.highlight}`, // Golden Ochre
|
||||||
borderRadius: 0,
|
|
||||||
alignSelf: 'center', // Centered in the chat
|
|
||||||
color: theme.palette.text.primary, // Charcoal Black for maximum readability
|
|
||||||
padding: '8px 8px', // More generous padding for better text framing
|
|
||||||
marginBottom: '0px', // Space between content and conversation
|
|
||||||
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.05)', // Subtle elevation
|
|
||||||
fontSize: '0.9rem', // Slightly smaller than default
|
|
||||||
lineHeight: '1.3', // More compact line height
|
|
||||||
fontFamily: theme.typography.fontFamily, // Consistent font with your theme
|
|
||||||
},
|
|
||||||
'error': {
|
|
||||||
...defaultStyle,
|
|
||||||
backgroundColor: '#F8E7E7', // Soft light red background
|
|
||||||
border: `1px solid #D83A3A`, // Prominent red border
|
|
||||||
borderRadius: defaultRadius,
|
borderRadius: defaultRadius,
|
||||||
maxWidth: '90%',
|
maxWidth: '90%',
|
||||||
minWidth: '90%',
|
minWidth: '90%',
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
color: '#8B2525', // Deep red text for good contrast
|
color: theme.palette.text.primary, // Charcoal Black
|
||||||
padding: '10px 16px',
|
fontStyle: 'italic',
|
||||||
boxShadow: '0 1px 3px rgba(216, 58, 58, 0.15)', // Subtle shadow with red tint
|
|
||||||
},
|
},
|
||||||
'fact-check': 'qualifications',
|
|
||||||
'job-description': 'content',
|
|
||||||
'job-requirements': 'qualifications',
|
|
||||||
'info': {
|
'info': {
|
||||||
...defaultStyle,
|
...defaultStyle,
|
||||||
backgroundColor: '#BFD8D8', // Softened Dusty Teal
|
backgroundColor: '#BFD8D8', // Softened Dusty Teal
|
||||||
@ -94,16 +83,6 @@ function ChatBubble(props: ChatBubbleProps) {
|
|||||||
color: theme.palette.text.primary, // Charcoal Black (#2E2E2E) — much better contrast
|
color: theme.palette.text.primary, // Charcoal Black (#2E2E2E) — much better contrast
|
||||||
opacity: 0.95,
|
opacity: 0.95,
|
||||||
},
|
},
|
||||||
'processing': "status",
|
|
||||||
'qualifications': {
|
|
||||||
...defaultStyle,
|
|
||||||
backgroundColor: theme.palette.primary.light, // Lighter shade, e.g., Soft Blue (#2A3B56)
|
|
||||||
border: `1px solid ${theme.palette.secondary.main}`, // Keep Dusty Teal (#4A7A7D) for contrast
|
|
||||||
borderRadius: `${defaultRadius} ${defaultRadius} ${defaultRadius} 0`, // Unchanged
|
|
||||||
color: theme.palette.primary.contrastText, // Warm Gray (#D3CDBF) for readable text
|
|
||||||
},
|
|
||||||
'resume': 'content',
|
|
||||||
'searching': 'status',
|
|
||||||
'status': {
|
'status': {
|
||||||
...defaultStyle,
|
...defaultStyle,
|
||||||
backgroundColor: 'rgba(74, 122, 125, 0.15)', // Translucent dusty teal
|
backgroundColor: 'rgba(74, 122, 125, 0.15)', // Translucent dusty teal
|
||||||
@ -119,45 +98,57 @@ function ChatBubble(props: ChatBubbleProps) {
|
|||||||
opacity: 0.9,
|
opacity: 0.9,
|
||||||
transition: 'opacity 0.3s ease-in-out', // Smooth fade effect for appearing/disappearing
|
transition: 'opacity 0.3s ease-in-out', // Smooth fade effect for appearing/disappearing
|
||||||
},
|
},
|
||||||
'streaming': "assistant",
|
'error': {
|
||||||
'system': {
|
|
||||||
...defaultStyle,
|
...defaultStyle,
|
||||||
backgroundColor: '#EDEAE0', // Soft warm gray that plays nice with #D3CDBF
|
backgroundColor: '#F8E7E7', // Soft light red background
|
||||||
border: `1px dashed ${theme.palette.custom.highlight}`, // Golden Ochre
|
border: `1px solid #D83A3A`, // Prominent red border
|
||||||
borderRadius: defaultRadius,
|
borderRadius: defaultRadius,
|
||||||
maxWidth: '90%',
|
maxWidth: '90%',
|
||||||
minWidth: '90%',
|
minWidth: '90%',
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
color: theme.palette.text.primary, // Charcoal Black
|
color: '#8B2525', // Deep red text for good contrast
|
||||||
fontStyle: 'italic',
|
padding: '10px 16px',
|
||||||
|
boxShadow: '0 1px 3px rgba(216, 58, 58, 0.15)', // Subtle shadow with red tint
|
||||||
},
|
},
|
||||||
'thinking': "status",
|
'content': {
|
||||||
'user': {
|
|
||||||
...defaultStyle,
|
...defaultStyle,
|
||||||
backgroundColor: theme.palette.background.default, // Warm Gray (#D3CDBF)
|
backgroundColor: '#F5F2EA', // Light cream background for easy reading
|
||||||
border: `1px solid ${theme.palette.custom.highlight}`, // Golden Ochre (#D4A017)
|
border: `1px solid ${theme.palette.custom.highlight}`, // Golden Ochre border
|
||||||
borderRadius: `${defaultRadius} ${defaultRadius} 0 ${defaultRadius}`, // Rounded, flat bottom-right for user
|
borderRadius: 0,
|
||||||
alignSelf: 'flex-end', // Right-aligned for user
|
alignSelf: 'center', // Centered in the chat
|
||||||
color: theme.palette.primary.main, // Midnight Blue (#1A2536) for text
|
color: theme.palette.text.primary, // Charcoal Black for maximum readability
|
||||||
|
padding: '8px 8px', // More generous padding for better text framing
|
||||||
|
marginBottom: '0px', // Space between content and conversation
|
||||||
|
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.05)', // Subtle elevation
|
||||||
|
fontSize: '0.9rem', // Slightly smaller than default
|
||||||
|
lineHeight: '1.3', // More compact line height
|
||||||
|
fontFamily: theme.typography.fontFamily, // Consistent font with your theme
|
||||||
|
},
|
||||||
|
'thinking': {
|
||||||
|
...defaultStyle
|
||||||
|
},
|
||||||
|
'streaming': {
|
||||||
|
...defaultStyle
|
||||||
|
},
|
||||||
|
'processing': {
|
||||||
|
...defaultStyle
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
for (const [key, value] of Object.entries(styles)) {
|
styles["thinking"] = styles["status"]
|
||||||
if (typeof (value) === "string") {
|
styles["streaming"] = styles["assistant"]
|
||||||
(styles as any)[key] = styles[value];
|
styles["processing"] = styles["status"]
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const icons: any = {
|
const icons: any = {
|
||||||
"error": <ErrorOutline color='error' />,
|
|
||||||
"info": <InfoOutline color='info' />,
|
|
||||||
"processing": <LocationSearchingIcon />,
|
|
||||||
// "streaming": <Stream />,
|
|
||||||
"searching": <Memory />,
|
"searching": <Memory />,
|
||||||
"thinking": <Psychology />,
|
"thinking": <Psychology />,
|
||||||
|
// "streaming": <Stream />,
|
||||||
"tooling": <LocationSearchingIcon />,
|
"tooling": <LocationSearchingIcon />,
|
||||||
|
"processing": <LocationSearchingIcon />,
|
||||||
|
"error": <ErrorOutline color='error' />,
|
||||||
|
"info": <InfoOutline color='info' />,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (expandable || (role === 'content' && title)) {
|
if (role === 'content' && title) {
|
||||||
return (
|
return (
|
||||||
<Accordion
|
<Accordion
|
||||||
expanded={expanded}
|
expanded={expanded}
|
||||||
@ -169,7 +160,7 @@ function ChatBubble(props: ChatBubbleProps) {
|
|||||||
expandIcon={<ExpandMoreIcon />}
|
expandIcon={<ExpandMoreIcon />}
|
||||||
slotProps={{ content: { sx: { fontWeight: 'bold', fontSize: '1.1rem', m: 0, p: 0, display: 'flex', justifyItems: 'center' } } }}
|
slotProps={{ content: { sx: { fontWeight: 'bold', fontSize: '1.1rem', m: 0, p: 0, display: 'flex', justifyItems: 'center' } } }}
|
||||||
>
|
>
|
||||||
{title || ""}
|
{title}
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails sx={{ mt: 0, mb: 0, p: 0, pl: 2, pr: 2 }}>
|
<AccordionDetails sx={{ mt: 0, mb: 0, p: 0, pl: 2, pr: 2 }}>
|
||||||
{children}
|
{children}
|
||||||
@ -179,7 +170,7 @@ function ChatBubble(props: ChatBubbleProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className={className} sx={{ ...(role in styles ? styles[role] : styles["status"]), gap: 1, display: "flex", ...sx, flexDirection: "row" }}>
|
<Box className={className} sx={{ ...(styles[role] !== undefined ? styles[role] : styles["status"]), gap: 1, display: "flex", ...sx, flexDirection: "row" }}>
|
||||||
{icons[role] !== undefined && icons[role]}
|
{icons[role] !== undefined && icons[role]}
|
||||||
<Box sx={{ p: 0, m: 0, gap: 0, display: "flex", flexGrow: 1, flexDirection: "column" }}>
|
<Box sx={{ p: 0, m: 0, gap: 0, display: "flex", flexGrow: 1, flexDirection: "column" }}>
|
||||||
{children}
|
{children}
|
||||||
|
@ -6,7 +6,6 @@ interface DocumentProps {
|
|||||||
title: string;
|
title: string;
|
||||||
expanded?: boolean;
|
expanded?: boolean;
|
||||||
filepath: string;
|
filepath: string;
|
||||||
content?: string;
|
|
||||||
setSnack: SetSnackType;
|
setSnack: SetSnackType;
|
||||||
submitQuery?: MessageSubmitQuery;
|
submitQuery?: MessageSubmitQuery;
|
||||||
connectionBase: string;
|
connectionBase: string;
|
||||||
@ -15,13 +14,13 @@ interface DocumentProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Document = (props: DocumentProps) => {
|
const Document = (props: DocumentProps) => {
|
||||||
const { setSnack, submitQuery, connectionBase, filepath, content, title, expanded, disableCopy, onExpand } = props;
|
const { setSnack, submitQuery, connectionBase, filepath, title, expanded, disableCopy, onExpand } = props;
|
||||||
|
|
||||||
const [document, setDocument] = useState<string>("");
|
const [document, setDocument] = useState<string>("");
|
||||||
|
|
||||||
// Get the markdown
|
// Get the markdown
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (document !== "" || !filepath) {
|
if (document !== "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fetchDocument = async () => {
|
const fetchDocument = async () => {
|
||||||
@ -47,6 +46,7 @@ const Document = (props: DocumentProps) => {
|
|||||||
}, [document, setDocument, filepath])
|
}, [document, setDocument, filepath])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Message
|
<Message
|
||||||
{...{
|
{...{
|
||||||
sx: {
|
sx: {
|
||||||
@ -56,7 +56,7 @@ const Document = (props: DocumentProps) => {
|
|||||||
m: 0,
|
m: 0,
|
||||||
flexGrow: 0,
|
flexGrow: 0,
|
||||||
},
|
},
|
||||||
message: { role: 'content', title: title, content: document || content || "" },
|
message: { role: 'content', title: title, content: document },
|
||||||
connectionBase,
|
connectionBase,
|
||||||
submitQuery,
|
submitQuery,
|
||||||
setSnack,
|
setSnack,
|
||||||
@ -64,6 +64,8 @@ const Document = (props: DocumentProps) => {
|
|||||||
disableCopy,
|
disableCopy,
|
||||||
onExpand,
|
onExpand,
|
||||||
}} />
|
}} />
|
||||||
|
{/* <Box sx={{ display: "flex", flexGrow: 1, p: 0, m: 0 }} /> */}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ const Mermaid: React.FC<MermaidProps> = (props: MermaidProps) => {
|
|||||||
const renderMermaid = async () => {
|
const renderMermaid = async () => {
|
||||||
if (containerRef.current && visible && chart) {
|
if (containerRef.current && visible && chart) {
|
||||||
try {
|
try {
|
||||||
|
console.log("Rendering Mermaid");
|
||||||
await mermaid.initialize(mermaidConfig || defaultMermaidConfig);
|
await mermaid.initialize(mermaidConfig || defaultMermaidConfig);
|
||||||
await mermaid.run({ nodes: [containerRef.current] });
|
await mermaid.run({ nodes: [containerRef.current] });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -29,22 +29,7 @@ import { SetSnackType } from './Snack';
|
|||||||
import { CopyBubble } from './CopyBubble';
|
import { CopyBubble } from './CopyBubble';
|
||||||
import { Scrollable } from './Scrollable';
|
import { Scrollable } from './Scrollable';
|
||||||
|
|
||||||
type MessageRoles =
|
type MessageRoles = 'info' | 'user' | 'assistant' | 'system' | 'status' | 'error' | 'content' | 'thinking' | 'processing' | 'streaming';
|
||||||
'assistant' |
|
|
||||||
'content' |
|
|
||||||
'error' |
|
|
||||||
'fact-check' |
|
|
||||||
'info' |
|
|
||||||
'job-description' |
|
|
||||||
'job-requirements' |
|
|
||||||
'processing' |
|
|
||||||
'qualifications' |
|
|
||||||
'resume' |
|
|
||||||
'status' |
|
|
||||||
'streaming' |
|
|
||||||
'system' |
|
|
||||||
'thinking' |
|
|
||||||
'user';
|
|
||||||
|
|
||||||
type MessageData = {
|
type MessageData = {
|
||||||
role: MessageRoles,
|
role: MessageRoles,
|
||||||
@ -59,9 +44,7 @@ type MessageData = {
|
|||||||
id?: string,
|
id?: string,
|
||||||
isProcessing?: boolean,
|
isProcessing?: boolean,
|
||||||
actions?: string[],
|
actions?: string[],
|
||||||
metadata?: MessageMetaData,
|
metadata?: MessageMetaData
|
||||||
expanded?: boolean,
|
|
||||||
expandable?: boolean,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface MessageMetaData {
|
interface MessageMetaData {
|
||||||
@ -90,7 +73,7 @@ type MessageList = MessageData[];
|
|||||||
interface MessageProps {
|
interface MessageProps {
|
||||||
sx?: SxProps<Theme>,
|
sx?: SxProps<Theme>,
|
||||||
message: MessageData,
|
message: MessageData,
|
||||||
// expanded?: boolean, // Provided as part of MessageData
|
expanded?: boolean,
|
||||||
onExpand?: () => void,
|
onExpand?: () => void,
|
||||||
submitQuery?: MessageSubmitQuery,
|
submitQuery?: MessageSubmitQuery,
|
||||||
sessionId?: string,
|
sessionId?: string,
|
||||||
@ -243,6 +226,7 @@ const MessageMeta = (props: MessageMetaProps) => {
|
|||||||
|
|
||||||
const Message = (props: MessageProps) => {
|
const Message = (props: MessageProps) => {
|
||||||
const { message, submitQuery, sx, className, onExpand } = props;
|
const { message, submitQuery, sx, className, onExpand } = props;
|
||||||
|
const messageExpanded = props.expanded;
|
||||||
const [expanded, setExpanded] = useState<boolean>(false);
|
const [expanded, setExpanded] = useState<boolean>(false);
|
||||||
const textFieldRef = useRef(null);
|
const textFieldRef = useRef(null);
|
||||||
|
|
||||||
@ -264,7 +248,9 @@ const Message = (props: MessageProps) => {
|
|||||||
return (
|
return (
|
||||||
<ChatBubble
|
<ChatBubble
|
||||||
className={className || "Message"}
|
className={className || "Message"}
|
||||||
{...message}
|
role={message.role}
|
||||||
|
title={message.title}
|
||||||
|
expanded={messageExpanded}
|
||||||
onExpand={onExpand}
|
onExpand={onExpand}
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -11,8 +11,6 @@ import { MessageList, MessageData } from './Message';
|
|||||||
import { SetSnackType } from './Snack';
|
import { SetSnackType } from './Snack';
|
||||||
import { Conversation } from './Conversation';
|
import { Conversation } from './Conversation';
|
||||||
|
|
||||||
import './ResumeBuilder.css';
|
|
||||||
|
|
||||||
interface ResumeBuilderProps {
|
interface ResumeBuilderProps {
|
||||||
connectionBase: string,
|
connectionBase: string,
|
||||||
sessionId: string | undefined,
|
sessionId: string | undefined,
|
||||||
@ -69,88 +67,133 @@ const ResumeBuilder: React.FC<ResumeBuilderProps> = ({
|
|||||||
if (messages === undefined || messages.length === 0) {
|
if (messages === undefined || messages.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
console.log("filterJobDescriptionMessages disabled", messages)
|
||||||
if (messages.length > 2) {
|
if (messages.length > 1) {
|
||||||
setHasResume(true);
|
setHasResume(true);
|
||||||
setHasFacts(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messages.length > 0) {
|
|
||||||
messages[0].role = 'content';
|
messages[0].role = 'content';
|
||||||
messages[0].title = 'Job Description';
|
messages[0].title = 'Job Description';
|
||||||
messages[0].disableCopy = false;
|
messages[0].disableCopy = false;
|
||||||
messages[0].expandable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messages.length > 3) {
|
return messages;
|
||||||
// messages[2] is Show job requirements
|
|
||||||
messages[3].role = 'job-requirements';
|
|
||||||
messages[3].title = 'Job Requirements';
|
|
||||||
messages[3].disableCopy = false;
|
|
||||||
messages[3].expanded = true;
|
|
||||||
messages[3].expandable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Filter out the 2nd and 3rd (0-based) */
|
// let reduced = messages.filter((m, i) => {
|
||||||
const filtered = messages.filter((m, i) => i !== 1 && i !== 2);
|
// const keep = (m.metadata?.origin || m.origin || "no origin") === 'job_description';
|
||||||
|
// if ((m.metadata?.origin || m.origin || "no origin") === 'resume') {
|
||||||
|
// setHasResume(true);
|
||||||
|
// }
|
||||||
|
// // if (!keep) {
|
||||||
|
// // console.log(`filterJobDescriptionMessages: ${i + 1} filtered:`, m);
|
||||||
|
// // } else {
|
||||||
|
// // console.log(`filterJobDescriptionMessages: ${i + 1}:`, m);
|
||||||
|
// // }
|
||||||
|
|
||||||
return filtered;
|
// return keep;
|
||||||
}, [setHasResume, setHasFacts]);
|
// });
|
||||||
|
|
||||||
|
// /* If Resume hasn't occurred yet and there is still more than one message,
|
||||||
|
// * resume has been generated. */
|
||||||
|
// if (!hasResume && reduced.length > 1) {
|
||||||
|
// setHasResume(true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (reduced.length > 0) {
|
||||||
|
// // First message is always 'content'
|
||||||
|
// reduced[0].title = 'Job Description';
|
||||||
|
// reduced[0].role = 'content';
|
||||||
|
// setHasJobDescription(true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /* Filter out any messages which the server injected for state management */
|
||||||
|
// reduced = reduced.filter(m => m.display !== "hide");
|
||||||
|
|
||||||
|
// return reduced;
|
||||||
|
}, [setHasResume/*, setHasJobDescription, hasResume*/]);
|
||||||
|
|
||||||
const filterResumeMessages = useCallback((messages: MessageList): MessageList => {
|
const filterResumeMessages = useCallback((messages: MessageList): MessageList => {
|
||||||
if (messages === undefined || messages.length === 0) {
|
if (messages === undefined || messages.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
console.log("filterResumeMessages disabled")
|
||||||
if (messages.length > 1) {
|
|
||||||
// messages[0] is Show Qualifications
|
|
||||||
messages[1].role = 'qualifications';
|
|
||||||
messages[1].title = 'Candidate qualifications';
|
|
||||||
messages[1].disableCopy = false;
|
|
||||||
messages[1].expanded = false;
|
|
||||||
messages[1].expandable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messages.length > 3) {
|
if (messages.length > 3) {
|
||||||
// messages[2] is Show Resume
|
setHasFacts(true);
|
||||||
messages[3].role = 'resume';
|
|
||||||
messages[3].title = 'Generated Resume';
|
|
||||||
messages[3].disableCopy = false;
|
|
||||||
messages[3].expanded = true;
|
|
||||||
messages[3].expandable = true;
|
|
||||||
}
|
}
|
||||||
|
return messages;
|
||||||
|
|
||||||
/* Filter out the 1st and 3rd messages (0-based) */
|
// let reduced = messages.filter((m, i) => {
|
||||||
const filtered = messages.filter((m, i) => i !== 0 && i !== 2);
|
// const keep = (m.metadata?.origin || m.origin || "no origin") === 'resume';
|
||||||
|
// if ((m.metadata?.origin || m.origin || "no origin") === 'fact_check') {
|
||||||
|
// setHasFacts(true);
|
||||||
|
// }
|
||||||
|
// if (!keep) {
|
||||||
|
// console.log(`filterResumeMessages: ${i + 1} filtered:`, m);
|
||||||
|
// } else {
|
||||||
|
// console.log(`filterResumeMessages: ${i + 1}:`, m);
|
||||||
|
// }
|
||||||
|
// return keep;
|
||||||
|
// });
|
||||||
|
|
||||||
return filtered;
|
// /* If there is more than one message, it is user: "...JOB_DESCRIPTION...", assistant: "...RESUME..."
|
||||||
}, []);
|
// * which means a resume has been generated. */
|
||||||
|
// if (reduced.length > 1) {
|
||||||
|
// /* Remove the assistant message from the UI */
|
||||||
|
// if (reduced[0].role === "user") {
|
||||||
|
// reduced.splice(0, 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /* If Fact Check hasn't occurred yet and there is still more than one message,
|
||||||
|
// * facts have have been generated. */
|
||||||
|
// if (!hasFacts && reduced.length > 1) {
|
||||||
|
// setHasFacts(true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /* Filter out any messages which the server injected for state management */
|
||||||
|
// reduced = reduced.filter(m => m.display !== "hide");
|
||||||
|
|
||||||
|
// /* If there are any messages, there is a resume */
|
||||||
|
// if (reduced.length > 0) {
|
||||||
|
// // First message is always 'content'
|
||||||
|
// reduced[0].title = 'Resume';
|
||||||
|
// reduced[0].role = 'content';
|
||||||
|
// setHasResume(true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return reduced;
|
||||||
|
}, [/*setHasResume, hasFacts,*/ setHasFacts]);
|
||||||
|
|
||||||
const filterFactsMessages = useCallback((messages: MessageList): MessageList => {
|
const filterFactsMessages = useCallback((messages: MessageList): MessageList => {
|
||||||
if (messages === undefined || messages.length === 0) {
|
if (messages === undefined || messages.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
console.log("filterFactsMessages disabled")
|
||||||
|
return messages;
|
||||||
|
|
||||||
if (messages.length > 1) {
|
// messages.forEach((m, i) => console.log(`filterFactsMessages: ${i + 1}:`, m))
|
||||||
// messages[0] is Show verification
|
|
||||||
messages[1].role = 'fact-check';
|
|
||||||
messages[1].title = 'Fact Check';
|
|
||||||
messages[1].disableCopy = false;
|
|
||||||
messages[1].expanded = false;
|
|
||||||
messages[1].expandable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Filter out the 1st (0-based) */
|
// const reduced = messages.filter(m => {
|
||||||
const filtered = messages.filter((m, i) => i !== 0);
|
// return (m.metadata?.origin || m.origin || "no origin") === 'fact_check';
|
||||||
|
// });
|
||||||
|
|
||||||
return filtered;
|
// /* If there is more than one message, it is user: "Fact check this resume...", assistant: "...FACT CHECK..."
|
||||||
}, []);
|
// * which means facts have been generated. */
|
||||||
|
// if (reduced.length > 1) {
|
||||||
|
// /* Remove the user message from the UI */
|
||||||
|
// if (reduced[0].role === "user") {
|
||||||
|
// reduced.splice(0, 1);
|
||||||
|
// }
|
||||||
|
// // First message is always 'content'
|
||||||
|
// reduced[0].title = 'Fact Check';
|
||||||
|
// reduced[0].role = 'content';
|
||||||
|
// setHasFacts(true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return reduced;
|
||||||
|
}, [/*setHasFacts*/]);
|
||||||
|
|
||||||
const jobResponse = useCallback(async (message: MessageData) => {
|
const jobResponse = useCallback(async (message: MessageData) => {
|
||||||
console.log('onJobResponse', message);
|
console.log('onJobResponse', message);
|
||||||
if (message.actions && message.actions.includes("job_description")) {
|
|
||||||
await jobConversationRef.current.fetchHistory();
|
|
||||||
}
|
|
||||||
if (message.actions && message.actions.includes("resume_generated")) {
|
if (message.actions && message.actions.includes("resume_generated")) {
|
||||||
await resumeConversationRef.current.fetchHistory();
|
await resumeConversationRef.current.fetchHistory();
|
||||||
setHasResume(true);
|
setHasResume(true);
|
||||||
@ -195,29 +238,12 @@ const ResumeBuilder: React.FC<ResumeBuilderProps> = ({
|
|||||||
</Box>,
|
</Box>,
|
||||||
];
|
];
|
||||||
|
|
||||||
const jobDescriptionPreamble: MessageList = [{
|
|
||||||
role: 'info',
|
|
||||||
content: `Once you paste a job description and press **Generate Resume**, the system will perform the following actions:
|
|
||||||
|
|
||||||
1. **RAG**: Collects information from the RAG database relavent to the job description
|
|
||||||
2. **Isolated Analysis**: Three sub-stages
|
|
||||||
1. **Job Analysis**: Extracts requirements from job description only
|
|
||||||
2. **Candidate Analysis**: Catalogs qualifications from resume/context only
|
|
||||||
3. **Mapping Analysis**: Identifies legitimate matches between requirements and qualifications
|
|
||||||
3. **Resume Generation**: Uses mapping output to create a tailored resume with evidence-based content
|
|
||||||
4. **Verification**: Performs fact-checking to catch any remaining fabrications
|
|
||||||
1. **Re-generation**: If verification does not pass, a second attempt is made to correct any issues`
|
|
||||||
}];
|
|
||||||
|
|
||||||
|
|
||||||
if (!hasJobDescription) {
|
if (!hasJobDescription) {
|
||||||
return <Conversation
|
return <Conversation
|
||||||
ref={jobConversationRef}
|
ref={jobConversationRef}
|
||||||
{...{
|
{...{
|
||||||
type: "job_description",
|
type: "job_description",
|
||||||
actionLabel: "Generate Resume",
|
actionLabel: "Generate Resume",
|
||||||
preamble: jobDescriptionPreamble,
|
|
||||||
hidePreamble: true,
|
|
||||||
prompt: "Paste a job description, then click Generate...",
|
prompt: "Paste a job description, then click Generate...",
|
||||||
multiline: true,
|
multiline: true,
|
||||||
resetLabel: `job description${hasFacts ? ", resume, and fact check" : hasResume ? " and resume" : ""}`,
|
resetLabel: `job description${hasFacts ? ", resume, and fact check" : hasResume ? " and resume" : ""}`,
|
||||||
@ -331,8 +357,7 @@ const ResumeBuilder: React.FC<ResumeBuilderProps> = ({
|
|||||||
}, [connectionBase, sessionId, setSnack, factsResponse, filterFactsMessages, resetFacts, hasResume, hasFacts]);
|
}, [connectionBase, sessionId, setSnack, factsResponse, filterFactsMessages, resetFacts, hasResume, hasFacts]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="ResumeBuilder"
|
<Box sx={{
|
||||||
sx={{
|
|
||||||
p: 0,
|
p: 0,
|
||||||
m: 0,
|
m: 0,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -13,7 +13,3 @@ pre:not(.MessageContent) {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MuiMarkdown > div {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
@ -4,11 +4,8 @@ import { SxProps, useTheme } from '@mui/material/styles';
|
|||||||
import { Link } from '@mui/material';
|
import { Link } from '@mui/material';
|
||||||
import { ChatQuery, QueryOptions } from './ChatQuery';
|
import { ChatQuery, QueryOptions } from './ChatQuery';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import JsonView from '@uiw/react-json-view';
|
|
||||||
import { vscodeTheme } from '@uiw/react-json-view/vscode';
|
|
||||||
import { Mermaid } from './Mermaid';
|
import { Mermaid } from './Mermaid';
|
||||||
import { Scrollable } from './Scrollable';
|
|
||||||
import { jsonrepair } from 'jsonrepair';
|
|
||||||
|
|
||||||
import './StyledMarkdown.css';
|
import './StyledMarkdown.css';
|
||||||
|
|
||||||
@ -27,42 +24,10 @@ const StyledMarkdown: React.FC<StyledMarkdownProps> = (props: StyledMarkdownProp
|
|||||||
pre: {
|
pre: {
|
||||||
component: (element: any) => {
|
component: (element: any) => {
|
||||||
const { className } = element.children.props;
|
const { className } = element.children.props;
|
||||||
const content = element.children?.props?.children || "";
|
const chart = element.children?.props?.children || "";
|
||||||
if (className === "lang-mermaid") {
|
if (className === "lang-mermaid") {
|
||||||
return <Mermaid className="Mermaid" chart={content} />;
|
console.log(`StyledMarkdown pre: ${className}`);
|
||||||
}
|
return <Mermaid className="Mermaid" chart={chart} />;
|
||||||
if (className === "lang-json") {
|
|
||||||
try {
|
|
||||||
const fixed = jsonrepair(content);
|
|
||||||
return <Scrollable className="JsonViewScrollable">
|
|
||||||
<JsonView
|
|
||||||
className="JsonView"
|
|
||||||
style={{
|
|
||||||
...vscodeTheme,
|
|
||||||
fontSize: "0.8rem",
|
|
||||||
maxHeight: "20rem",
|
|
||||||
padding: "14px 0",
|
|
||||||
overflow: "hidden",
|
|
||||||
width: "100%",
|
|
||||||
minHeight: "max-content",
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
}}
|
|
||||||
displayDataTypes={false}
|
|
||||||
objectSortKeys={false}
|
|
||||||
collapsed={false}
|
|
||||||
value={JSON.parse(fixed)}>
|
|
||||||
<JsonView.String
|
|
||||||
render={({ children, ...reset }) => {
|
|
||||||
if (typeof (children) === "string" && children.match("\n")) {
|
|
||||||
return <pre {...reset} style={{ display: "flex", border: "none", ...reset.style }}>{children}</pre>
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</JsonView>
|
|
||||||
</Scrollable>
|
|
||||||
} catch (e) {
|
|
||||||
console.log("jsonrepair error", e);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return <pre><code className={className}>{element.children}</code></pre>;
|
return <pre><code className={className}>{element.children}</code></pre>;
|
||||||
},
|
},
|
||||||
|
@ -18,6 +18,7 @@ import { Scrollable } from './Scrollable';
|
|||||||
import { StyledMarkdown } from './StyledMarkdown';
|
import { StyledMarkdown } from './StyledMarkdown';
|
||||||
|
|
||||||
import './VectorVisualizer.css';
|
import './VectorVisualizer.css';
|
||||||
|
import { calculatePoint } from 'mermaid/dist/utils';
|
||||||
|
|
||||||
interface Metadata {
|
interface Metadata {
|
||||||
doc_type?: string;
|
doc_type?: string;
|
||||||
|
@ -881,6 +881,9 @@ class WebServer:
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.info(f"{agent_type}.process_message: {message.status} {f'...{message.response[-20:]}' if len(message.response) > 20 else message.response}")
|
logger.info(f"{agent_type}.process_message: {message.status} {f'...{message.response[-20:]}' if len(message.response) > 20 else message.response}")
|
||||||
|
if message.metadata["eval_count"]:
|
||||||
|
agent.metrics.tokens_prompt.labels(agent=agent.agent_type).inc(message.metadata["prompt_eval_count"])
|
||||||
|
agent.metrics.tokens_eval.labels(agent=agent.agent_type).inc(message.metadata["eval_count"])
|
||||||
message.status = "done"
|
message.status = "done"
|
||||||
yield message
|
yield message
|
||||||
return
|
return
|
||||||
|
@ -263,11 +263,11 @@ class Agent(BaseModel, ABC):
|
|||||||
for response in llm.chat(
|
for response in llm.chat(
|
||||||
model=model,
|
model=model,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
|
stream=True,
|
||||||
options={
|
options={
|
||||||
**message.metadata["options"],
|
**message.metadata["options"],
|
||||||
# "temperature": 0.5,
|
# "temperature": 0.5,
|
||||||
},
|
}
|
||||||
stream=True,
|
|
||||||
):
|
):
|
||||||
# logger.info(f"LLM::Tools: {'done' if response.done else 'processing'} - {response.message}")
|
# logger.info(f"LLM::Tools: {'done' if response.done else 'processing'} - {response.message}")
|
||||||
message.status = "streaming"
|
message.status = "streaming"
|
||||||
@ -278,7 +278,6 @@ class Agent(BaseModel, ABC):
|
|||||||
yield message
|
yield message
|
||||||
|
|
||||||
if response.done:
|
if response.done:
|
||||||
self.collect_metrics(response)
|
|
||||||
message.metadata["eval_count"] += response.eval_count
|
message.metadata["eval_count"] += response.eval_count
|
||||||
message.metadata["eval_duration"] += response.eval_duration
|
message.metadata["eval_duration"] += response.eval_duration
|
||||||
message.metadata["prompt_eval_count"] += response.prompt_eval_count
|
message.metadata["prompt_eval_count"] += response.prompt_eval_count
|
||||||
@ -291,10 +290,6 @@ class Agent(BaseModel, ABC):
|
|||||||
message.metadata["timers"]["llm_with_tools"] = f"{(end_time - start_time):.4f}"
|
message.metadata["timers"]["llm_with_tools"] = f"{(end_time - start_time):.4f}"
|
||||||
return
|
return
|
||||||
|
|
||||||
def collect_metrics(self, response):
|
|
||||||
self.metrics.tokens_prompt.labels(agent=self.agent_type).inc(response.prompt_eval_count)
|
|
||||||
self.metrics.tokens_eval.labels(agent=self.agent_type).inc(response.eval_count)
|
|
||||||
|
|
||||||
async def generate_llm_response(self, llm: Any, model: str, message: Message, temperature = 0.7) -> AsyncGenerator[Message, None]:
|
async def generate_llm_response(self, llm: Any, model: str, message: Message, temperature = 0.7) -> AsyncGenerator[Message, None]:
|
||||||
logger.info(f"{self.agent_type} - {inspect.stack()[0].function}")
|
logger.info(f"{self.agent_type} - {inspect.stack()[0].function}")
|
||||||
|
|
||||||
@ -359,7 +354,6 @@ class Agent(BaseModel, ABC):
|
|||||||
},
|
},
|
||||||
stream=False # No need to stream the probe
|
stream=False # No need to stream the probe
|
||||||
)
|
)
|
||||||
self.collect_metrics(response)
|
|
||||||
|
|
||||||
end_time = time.perf_counter()
|
end_time = time.perf_counter()
|
||||||
message.metadata["timers"]["tool_check"] = f"{(end_time - start_time):.4f}"
|
message.metadata["timers"]["tool_check"] = f"{(end_time - start_time):.4f}"
|
||||||
@ -388,7 +382,6 @@ class Agent(BaseModel, ABC):
|
|||||||
},
|
},
|
||||||
stream=False
|
stream=False
|
||||||
)
|
)
|
||||||
self.collect_metrics(response)
|
|
||||||
|
|
||||||
end_time = time.perf_counter()
|
end_time = time.perf_counter()
|
||||||
message.metadata["timers"]["non_streaming"] = f"{(end_time - start_time):.4f}"
|
message.metadata["timers"]["non_streaming"] = f"{(end_time - start_time):.4f}"
|
||||||
@ -448,7 +441,6 @@ class Agent(BaseModel, ABC):
|
|||||||
yield message
|
yield message
|
||||||
|
|
||||||
if response.done:
|
if response.done:
|
||||||
self.collect_metrics(response)
|
|
||||||
message.metadata["eval_count"] += response.eval_count
|
message.metadata["eval_count"] += response.eval_count
|
||||||
message.metadata["eval_duration"] += response.eval_duration
|
message.metadata["eval_duration"] += response.eval_duration
|
||||||
message.metadata["prompt_eval_count"] += response.prompt_eval_count
|
message.metadata["prompt_eval_count"] += response.prompt_eval_count
|
||||||
|
@ -191,25 +191,16 @@ class JobDescription(Agent):
|
|||||||
if message.status == "error":
|
if message.status == "error":
|
||||||
return
|
return
|
||||||
|
|
||||||
# Add the "Job requirements" message
|
|
||||||
if "generate_factual_tailored_resume" in message.metadata and "job_requirements" in message.metadata["generate_factual_tailored_resume"]:
|
|
||||||
new_message = Message(prompt="Show job requirements")
|
|
||||||
job_requirements = message.metadata["generate_factual_tailored_resume"]["job_requirements"]["results"]
|
|
||||||
new_message.response = f"```json\n\n{json.dumps(job_requirements, indent=2)}\n```\n"
|
|
||||||
new_message.status = "done"
|
|
||||||
self.conversation.add(new_message)
|
|
||||||
|
|
||||||
self.system_prompt = system_user_qualifications
|
self.system_prompt = system_user_qualifications
|
||||||
|
|
||||||
resume_agent = self.context.get_agent(agent_type="resume")
|
resume_agent = self.context.get_agent(agent_type="resume")
|
||||||
fact_check_agent = self.context.get_agent(agent_type="fact_check")
|
fact_check_agent = self.context.get_agent(agent_type="fact_check")
|
||||||
if not resume_agent:
|
if not resume_agent:
|
||||||
# Add the "Generated Resume" message
|
|
||||||
if "generate_factual_tailored_resume" in message.metadata and "analyze_candidate_qualifications" in message.metadata["generate_factual_tailored_resume"]:
|
if "generate_factual_tailored_resume" in message.metadata and "analyze_candidate_qualifications" in message.metadata["generate_factual_tailored_resume"]:
|
||||||
resume_agent = self.context.get_or_create_agent(agent_type="resume", resume=message.response)
|
resume_agent = self.context.get_or_create_agent(agent_type="resume", resume=message.response)
|
||||||
resume_message = Message(prompt="Show candidate qualifications")
|
resume_message = Message(prompt="Show candidate qualifications")
|
||||||
qualifications = message.metadata["generate_factual_tailored_resume"]["analyze_candidate_qualifications"]["results"]
|
qualifications = message.metadata["generate_factual_tailored_resume"]["analyze_candidate_qualifications"]["results"]
|
||||||
resume_message.response = f"```json\n\n{json.dumps(qualifications, indent=2)}\n```\n"
|
resume_message.response = f"# Candidate qualifications\n\n```json\n\n{json.dumps(qualifications, indent=2)}\n```\n"
|
||||||
resume_message.status = "done"
|
resume_message.status = "done"
|
||||||
resume_agent.conversation.add(resume_message)
|
resume_agent.conversation.add(resume_message)
|
||||||
|
|
||||||
@ -221,8 +212,6 @@ class JobDescription(Agent):
|
|||||||
|
|
||||||
message.response = "Resume generated."
|
message.response = "Resume generated."
|
||||||
message.actions.append("resume_generated")
|
message.actions.append("resume_generated")
|
||||||
|
|
||||||
# Add the "Fact Check" message
|
|
||||||
if "generate_factual_tailored_resume" in message.metadata and "verify_resume" in message.metadata["generate_factual_tailored_resume"]:
|
if "generate_factual_tailored_resume" in message.metadata and "verify_resume" in message.metadata["generate_factual_tailored_resume"]:
|
||||||
if "second_pass" in message.metadata["generate_factual_tailored_resume"]["verify_resume"]:
|
if "second_pass" in message.metadata["generate_factual_tailored_resume"]["verify_resume"]:
|
||||||
verification = message.metadata["generate_factual_tailored_resume"]["verify_resume"]["second_pass"]["results"]
|
verification = message.metadata["generate_factual_tailored_resume"]["verify_resume"]["second_pass"]["results"]
|
||||||
@ -232,7 +221,7 @@ class JobDescription(Agent):
|
|||||||
fact_check_agent = self.context.get_or_create_agent(agent_type="fact_check", facts=json.dumps(verification, indent=2))
|
fact_check_agent = self.context.get_or_create_agent(agent_type="fact_check", facts=json.dumps(verification, indent=2))
|
||||||
fact_check_message = message.model_copy()
|
fact_check_message = message.model_copy()
|
||||||
fact_check_message.prompt = "Show verification"
|
fact_check_message.prompt = "Show verification"
|
||||||
fact_check_message.response = f"```json\n\n{json.dumps(verification, indent=2)}\n```\n"
|
fact_check_message.response = f"# Resume verfication\n\n```json\n\n{json.dumps(verification, indent=2)}\n```\n"
|
||||||
fact_check_message.status = "done"
|
fact_check_message.status = "done"
|
||||||
fact_check_agent.conversation.add(fact_check_message)
|
fact_check_agent.conversation.add(fact_check_message)
|
||||||
|
|
||||||
@ -555,7 +544,6 @@ class JobDescription(Agent):
|
|||||||
message.chunk = ""
|
message.chunk = ""
|
||||||
|
|
||||||
if response.done:
|
if response.done:
|
||||||
self.collect_metrics(response)
|
|
||||||
message.metadata["eval_count"] += response.eval_count
|
message.metadata["eval_count"] += response.eval_count
|
||||||
message.metadata["eval_duration"] += response.eval_duration
|
message.metadata["eval_duration"] += response.eval_duration
|
||||||
message.metadata["prompt_eval_count"] += response.prompt_eval_count
|
message.metadata["prompt_eval_count"] += response.prompt_eval_count
|
||||||
@ -884,73 +872,40 @@ class JobDescription(Agent):
|
|||||||
yield message
|
yield message
|
||||||
return
|
return
|
||||||
|
|
||||||
system_prompt = """\
|
system_prompt = """
|
||||||
You are a professional resume editor with a focus on factual accuracy. Your task is to correct identified issues in a tailored resume according to the verification report.
|
You are a professional resume editor with a focus on factual accuracy. Your task is to correct
|
||||||
|
the identified issues in a tailored resume according to the verification report.
|
||||||
|
|
||||||
## REFERENCE DATA:
|
## INSTRUCTIONS:
|
||||||
The following sections contain reference information for you to use when making corrections. This information should NOT be included in your output resume:
|
|
||||||
|
|
||||||
1. Original Resume - The resume you will correct
|
1. Make ONLY the changes specified in the verification report
|
||||||
2. Verification Results - Issues that need correction
|
2. Ensure all corrections maintain factual accuracy based on the skills mapping
|
||||||
3. Skills Mapping - How candidate skills align with job requirements
|
3. Do not introduce any new claims or skills not present in the verification data
|
||||||
4. Candidate Qualifications - Verified information about the candidate's background
|
4. Maintain the original format and structure of the resume
|
||||||
5. Original Resume Header - The formatting of the resume header
|
5. DO NOT directly list the verification report or skills mapping
|
||||||
|
6. Provide ONLY the fully corrected resume
|
||||||
|
7. DO NOT provide Verification Results or other additional information beyond the corrected resume
|
||||||
|
|
||||||
## INSTRUCTIONS:
|
## PROCESS:
|
||||||
|
|
||||||
1. Make ONLY the changes specified in the verification report
|
1. For each issue in the verification report:
|
||||||
2. Ensure all corrections maintain factual accuracy based on the skills mapping
|
|
||||||
3. Do not introduce any new claims or skills not present in the verification data
|
|
||||||
4. Maintain the original format and structure of the resume
|
|
||||||
5. Provide ONLY the fully corrected resume as your output
|
|
||||||
6. DO NOT include any of the reference data sections in your output
|
|
||||||
7. DO NOT include any additional comments, explanations, or notes in your output
|
|
||||||
|
|
||||||
## PROCESS:
|
|
||||||
|
|
||||||
1. For each issue in the verification report:
|
|
||||||
- Identify the problematic text in the resume
|
- Identify the problematic text in the resume
|
||||||
- Replace it with the suggested correction
|
- Replace it with the suggested correction
|
||||||
- Ensure the correction is consistent with the rest of the resume
|
- Ensure the correction is consistent with the rest of the resume
|
||||||
|
|
||||||
2. After making all corrections:
|
2. After making all corrections:
|
||||||
- Review the revised resume for consistency
|
- Review the revised resume for consistency
|
||||||
- Ensure no factual inaccuracies have been introduced
|
- Ensure no factual inaccuracies have been introduced
|
||||||
- Check that all formatting remains professional
|
- Check that all formatting remains professional
|
||||||
|
|
||||||
Your output should contain ONLY the corrected resume text with no additional explanations or context.
|
Return the fully corrected resume.
|
||||||
"""
|
"""
|
||||||
prompt = """
|
|
||||||
## REFERENCE DATA
|
|
||||||
|
|
||||||
### Original Resume:
|
prompt = f"Original Resume:\n{generated_resume}\n\n"
|
||||||
"""
|
prompt += f"Verification Results:\n{json.dumps(verification_results, indent=2)}\n\n"
|
||||||
prompt += generated_resume
|
prompt += f"Skills Mapping:\n{json.dumps(skills_mapping, indent=2)}\n\n"
|
||||||
prompt += """
|
prompt += f"Candidate Qualifications:\n{json.dumps(candidate_qualifications, indent=2)}\n\n"
|
||||||
|
prompt += f"Original Resume Header:\n{original_header}"
|
||||||
### Verification Results:
|
|
||||||
"""
|
|
||||||
prompt += json.dumps(verification_results, indent=2)
|
|
||||||
prompt += """
|
|
||||||
|
|
||||||
### Skills Mapping:
|
|
||||||
"""
|
|
||||||
prompt += json.dumps(skills_mapping, indent=2)
|
|
||||||
prompt += """
|
|
||||||
|
|
||||||
### Candidate Qualifications:
|
|
||||||
"""
|
|
||||||
prompt += json.dumps(candidate_qualifications, indent=2)
|
|
||||||
prompt += """
|
|
||||||
|
|
||||||
### Original Resume Header:
|
|
||||||
"""
|
|
||||||
prompt += generated_resume
|
|
||||||
prompt += """
|
|
||||||
|
|
||||||
## TASK
|
|
||||||
Based on the reference data above, please create a corrected version of the resume that addresses all issues identified in the verification report. Return ONLY the corrected resume.
|
|
||||||
"""
|
|
||||||
|
|
||||||
metadata["system_prompt"] = system_prompt
|
metadata["system_prompt"] = system_prompt
|
||||||
metadata["prompt"] = prompt
|
metadata["prompt"] = prompt
|
||||||
|
Loading…
x
Reference in New Issue
Block a user