Anthropic backend working

Add regenerate skill assessment
This commit is contained in:
James Ketr 2025-07-11 13:24:47 -07:00
parent fd6ec5015c
commit 179bef1564
9 changed files with 143 additions and 20 deletions

View File

@ -15,6 +15,7 @@ services:
- REDIS_URL=redis://redis:6379
- REDIS_DB=0
- SSL_ENABLED=true
# To use Anthropic, uncomment the following lines and comment out the OpenAI lines
# - DEFAULT_LLM_PROVIDER=anthropic
# - MODEL_NAME=claude-3-5-haiku-latest
- DEFAULT_LLM_PROVIDER=openai

View File

@ -15,10 +15,13 @@ import {
Button,
Paper,
SxProps,
Tooltip,
IconButton,
} from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import ErrorIcon from '@mui/icons-material/Error';
import ModelTrainingIcon from '@mui/icons-material/ModelTraining';
import PendingIcon from '@mui/icons-material/Pending';
import WarningIcon from '@mui/icons-material/Warning';
import { Candidate, SkillAssessment, SkillStatus } from 'types/types';
@ -261,7 +264,8 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
const skillMatchHandlers = useMemo(() => {
return {
onStatus: (status: Types.ChatMessageStatus): void => {
setMatchStatus(status.content.toLowerCase());
console.log('Skill Match Status:', status.content);
setMatchStatus(status.content);
},
};
}, [setMatchStatus]);
@ -305,6 +309,7 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
const request = await apiClient.candidateMatchForRequirement(
candidate.id || '',
requirements[i].requirement,
false,
skillMatchHandlers
);
const result = await request.promise; /* Wait for the streaming result to complete */
@ -410,6 +415,64 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
return <ErrorIcon color="error" />;
};
const handleAssesmentRegenerate = async (index: number, skill: string): Promise<void> => {
setSkillMatches(prev => {
const updated = [...prev];
updated[index] = {
...updated[index],
status: 'pending',
assessment: '',
evidenceDetails: [],
evidenceStrength: 'none',
matchScore: 0,
};
return updated;
});
setMatchStatus('Regenerating assessment...');
const request = apiClient.candidateMatchForRequirement(
candidate.id || '',
skill,
true,
skillMatchHandlers
);
const result = await request.promise; /* Wait for the streaming result to complete */
const skillMatch = result.skillAssessment;
setMatchStatus('');
setSkillMatches(prev => {
const updated = [...prev];
updated[index] = {
...skillMatch,
status: 'complete',
matchScore: calculateScore(skillMatch),
domain: updated[index].domain,
};
return updated;
});
// Update overall score
setSkillMatches(current => {
const completedMatches = current.filter(match => match.status === 'complete');
if (completedMatches.length > 0) {
const newOverallScore =
completedMatches.reduce((sum, match) => sum + match.matchScore, 0) /
completedMatches.length;
setOverallScore(Math.round(newOverallScore));
}
return current;
});
setAnalysis({
score: overallScore,
skills: skillMatches,
});
onAnalysisComplete &&
onAnalysisComplete({
score: overallScore,
skills: skillMatches,
});
setStartAnalysis(false);
setAnalyzing(false);
setFirstRun(false);
};
const beginAnalysis = (): void => {
initializeRequirements(job);
setStartAnalysis(true);
@ -595,6 +658,7 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
bgcolor: getMatchColor(match.matchScore),
color: 'white',
minWidth: 90,
pointerEvents: 'none',
}}
/>
) : match.status === 'waiting' ? (
@ -605,6 +669,7 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
bgcolor: 'rgb(189, 173, 85)',
color: 'white',
minWidth: 90,
pointerEvents: 'none',
}}
/>
) : match.status === 'pending' ? (
@ -615,6 +680,7 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
bgcolor: theme.palette.grey[400],
color: 'white',
minWidth: 90,
pointerEvents: 'none',
}}
/>
) : (
@ -625,6 +691,7 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
bgcolor: theme.palette.error.main,
color: 'white',
minWidth: 90,
pointerEvents: 'none',
}}
/>
)}
@ -709,6 +776,19 @@ const JobMatchAnalysis: React.FC<JobAnalysisProps> = (props: JobAnalysisProps) =
Skill description
</Typography>
<Typography paragraph>{match.description}</Typography>
{match.status === 'complete' && (
<Tooltip title="Regenerate Assessment">
<IconButton
size="small"
onClick={(e): void => {
e.stopPropagation();
handleAssesmentRegenerate(index, match.skill);
}}
>
<ModelTrainingIcon />
</IconButton>
</Tooltip>
)}
{/* { match.ragResults && match.ragResults.length !== 0 && <>
<Typography variant="h6" gutterBottom>
RAG Information

View File

@ -211,11 +211,14 @@ const MessageMeta = (props: MessageMetaProps): JSX.Element => {
/* MessageData */
ragResults = [],
tools = null,
} = props.metadata || {};
const {
evalCount = 0,
evalDuration = 0,
promptEvalCount = 0,
promptEvalDuration = 0,
} = props.metadata || {};
} = props.metadata.usage || {};
const message: ChatMessage | ChatMessageError | ChatMessageStatus = props.messageProps.message;
// let llm_submission = '<|system|>\n';

View File

@ -418,6 +418,7 @@ const JobsView: React.FC<JobsViewProps> = ({
</Box>
</TableCell>
<TableCell>Description</TableCell>
<TableCell>Pay Range</TableCell>
{!isMobile && (
<>
<TableCell>Owner</TableCell>
@ -499,6 +500,10 @@ const JobsView: React.FC<JobsViewProps> = ({
{truncateDescription(job.summary || job.description || '', 100)}
</Typography>
</TableCell>
<TableCell>
{job.payMinimum ? job.payMinimum : 'N/A'}-
{job.payMaximum ? job.payMaximum : 'N/A'}
</TableCell>
{!isMobile && (
<>
<TableCell>

View File

@ -26,10 +26,12 @@ const emptyMetadata: ChatMessageMetaData = {
frequencyPenalty: 0,
presencePenalty: 0,
stopSequences: [],
evalCount: 0,
evalDuration: 0,
promptEvalCount: 0,
promptEvalDuration: 0,
usage: {
evalCount: 0,
evalDuration: 0,
promptEvalCount: 0,
promptEvalDuration: 0,
},
};
const defaultMessage: ChatMessage = {

View File

@ -32,10 +32,12 @@ const emptyMetadata: ChatMessageMetaData = {
frequencyPenalty: 0,
presencePenalty: 0,
stopSequences: [],
evalCount: 0,
evalDuration: 0,
promptEvalCount: 0,
promptEvalDuration: 0,
usage: {
evalCount: 0,
evalDuration: 0,
promptEvalCount: 0,
promptEvalDuration: 0,
},
};
const defaultMessage: ChatMessage = {

View File

@ -38,10 +38,12 @@ const emptyMetadata: ChatMessageMetaData = {
frequencyPenalty: 0,
presencePenalty: 0,
stopSequences: [],
evalCount: 0,
evalDuration: 0,
promptEvalCount: 0,
promptEvalDuration: 0,
usage: {
evalCount: 0,
evalDuration: 0,
promptEvalCount: 0,
promptEvalDuration: 0,
},
};
const defaultMessage: ChatMessage = {

View File

@ -1148,9 +1148,10 @@ class ApiClient {
candidateMatchForRequirement(
candidate_id: string,
requirement: string,
regenerate: boolean = false,
streamingOptions?: StreamingOptions<Types.ChatMessageSkillAssessment>
): StreamingResponse<Types.ChatMessageSkillAssessment> {
const body = JSON.stringify(requirement);
const body = JSON.stringify({ skill: requirement, regenerate });
streamingOptions = {
...streamingOptions,
headers: this.defaultHeaders,

View File

@ -1,6 +1,6 @@
// Generated TypeScript types from Pydantic models
// Source: src/backend/models.py
// Generated on: 2025-07-09T20:57:47.233946
// Generated on: 2025-07-11T20:02:47.037054
// DO NOT EDIT MANUALLY - This file is auto-generated
// ============================
@ -323,10 +323,7 @@ export interface ChatMessageMetaData {
stopSequences: Array<string>;
ragResults?: Array<ChromaDBGetResponse>;
llmHistory?: Array<any>;
evalCount: number;
evalDuration: number;
promptEvalCount: number;
promptEvalDuration: number;
usage?: UsageStats;
options?: ChatOptions;
tools?: Tool;
timers?: Record<string, number>;
@ -416,6 +413,14 @@ export interface ChatQuery {
agentOptions?: Record<string, any>;
}
export interface ChatResponse {
content: string;
model: string;
finishReason?: string;
usage?: UsageStats;
usage_dict?: Record<string, number>;
}
export interface ChatSession {
id?: string;
userId?: string;
@ -714,6 +719,8 @@ export interface Job {
title?: string;
summary?: string;
company?: string;
payMinimum?: string;
payMaximum?: string;
description: string;
requirements?: JobRequirements;
createdAt?: Date;
@ -1051,6 +1058,11 @@ export interface SkillAssessment {
matchScore: number;
}
export interface SkillMatchRequest {
skill: string;
regenerate: boolean;
}
export interface SocialLink {
platform: "linkedin" | "twitter" | "github" | "dribbble" | "behance" | "website" | "other";
url: string;
@ -1060,6 +1072,7 @@ export interface SystemInfo {
installedRAM: string;
graphicsCards: Array<GPUInfo>;
CPU: string;
llmBackend: string;
llmModel: string;
embeddingModel: string;
maxContextLength: number;
@ -1083,6 +1096,20 @@ export interface Tunables {
enableContext: boolean;
}
export interface UsageStats {
promptTokens?: number;
completionTokens?: number;
totalTokens?: number;
promptEvalCount?: number;
promptEvalDuration?: number;
evalCount?: number;
evalDuration?: number;
totalDuration?: number;
tokensPerSecond?: number;
promptTokensPerSecond?: number;
extraStats?: Record<string, any>;
}
export interface UserActivity {
id?: string;
userId?: string;