File size: 3,853 Bytes
61f1214 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | """
Typed Pydantic models for the AI Interview Simulator OpenEnv environment.
Defines Observation, Action, Reward, and State structures.
"""
from __future__ import annotations
from enum import Enum
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
# ββββββββββββββββββββββββββββββββββββββββββββββ
# Enumerations
# ββββββββββββββββββββββββββββββββββββββββββββββ
class InterviewStage(str, Enum):
INTRO = "intro"
BEHAVIORAL = "behavioral"
TECHNICAL = "technical"
SITUATIONAL = "situational"
CLOSING = "closing"
COMPLETE = "complete"
class DifficultyLevel(str, Enum):
EASY = "easy"
MEDIUM = "medium"
HARD = "hard"
# ββββββββββββββββββββββββββββββββββββββββββββββ
# Core OpenEnv Models
# ββββββββββββββββββββββββββββββββββββββββββββββ
class QAPair(BaseModel):
"""A single question + the candidate's answer."""
question: str
answer: str
stage: InterviewStage
question_score: Optional[float] = None # filled after grading
class InterviewObservation(BaseModel):
"""
Observation returned by reset() and step().
Contains everything the agent needs to formulate its next response.
"""
task_id: str
job_title: str
company_context: str
difficulty: DifficultyLevel
current_question: str
question_number: int # 1-indexed
total_questions: int
stage: InterviewStage
previous_qa: List[QAPair] = Field(default_factory=list)
cumulative_score: float = 0.0
time_elapsed: float = 0.0 # seconds since reset
interview_complete: bool = False
class InterviewAction(BaseModel):
"""
Action submitted by the agent β the candidate's spoken response.
"""
response: str = Field(
...,
min_length=1,
description="The candidate's answer to the current interview question.",
)
class InterviewReward(BaseModel):
"""
Detailed reward breakdown for a single step.
All sub-scores are in [0.0, 1.0]; total_score is their weighted sum.
"""
total_score: float # 0.0 β 1.0, primary signal
relevance: float # Does the answer address the question?
structure: float # Organised response (STAR / step-by-step)?
depth: float # Specificity, examples, numbers
professionalism: float # Tone, vocabulary, no red flags
feedback: str # Human-readable grader feedback
class InterviewState(BaseModel):
"""
Full internal state β returned by state().
Useful for debugging, checkpointing, and OpenEnv validators.
"""
task_id: str
difficulty: DifficultyLevel
job_title: str
company_context: str
questions: List[str] # All questions in order
stages: List[InterviewStage] # Stage per question
current_index: int # Which question we're on
qa_history: List[QAPair]
per_question_scores: List[float]
episode_done: bool
total_steps: int
start_time: float
cumulative_reward: float
class StepResult(BaseModel):
"""Return type of env.step()."""
observation: InterviewObservation
reward: float # scalar for RL loop
done: bool
info: Dict[str, Any] = Field(default_factory=dict)
|