from __future__ import annotations from datetime import datetime from enum import Enum from pydantic import BaseModel, Field, field_validator, model_validator class SalaryType(str, Enum): daily = "daily" hourly = "hourly" monthly = "monthly" task = "task" class SourceType(str, Enum): job_to_worker = "job_to_worker" worker_to_job = "worker_to_job" class Salary(BaseModel): type: SalaryType = SalaryType.daily amount: float = 0 currency: str = "CNY" class SkillScore(BaseModel): name: str score: float = Field(ge=0, le=1) class JobCard(BaseModel): job_id: str title: str category: str description: str skills: list[str] = Field(default_factory=list) city: str region: str location_detail: str start_time: datetime duration_hours: float = Field(gt=0) headcount: int = Field(gt=0) salary: Salary work_mode: str tags: list[str] = Field(default_factory=list) confidence: float = Field(ge=0, le=1) class WorkerCard(BaseModel): worker_id: str name: str description: str skills: list[SkillScore] = Field(default_factory=list) cities: list[str] = Field(default_factory=list) regions: list[str] = Field(default_factory=list) availability: list[str] = Field(default_factory=list) experience_tags: list[str] = Field(default_factory=list) reliability_score: float = Field(ge=0, le=1) profile_completion: float = Field(ge=0, le=1) confidence: float = Field(ge=0, le=1) class MatchBreakdown(BaseModel): skill_score: float = Field(ge=0, le=1) region_score: float = Field(ge=0, le=1) time_score: float = Field(ge=0, le=1) experience_score: float = Field(ge=0, le=1) reliability_score: float = Field(ge=0, le=1) class MatchResult(BaseModel): match_id: str source_type: SourceType source_id: str target_id: str match_score: float = Field(ge=0, le=1) breakdown: MatchBreakdown reasons: list[str] = Field(default_factory=list, min_length=3) class ExtractTextRequest(BaseModel): text: str = Field(min_length=5) class IngestJobRequest(BaseModel): job: JobCard class IngestWorkerRequest(BaseModel): worker: WorkerCard class MatchWorkersRequest(BaseModel): job_id: str | None = None job: JobCard | None = None top_n: int = Field(default=10, ge=1, le=50) @model_validator(mode="after") def validate_source(self) -> "MatchWorkersRequest": if not self.job_id and not self.job: raise ValueError("job_id 或 job 至少需要提供一个") return self class MatchJobsRequest(BaseModel): worker_id: str | None = None worker: WorkerCard | None = None top_n: int = Field(default=10, ge=1, le=50) @model_validator(mode="after") def validate_source(self) -> "MatchJobsRequest": if not self.worker_id and not self.worker: raise ValueError("worker_id 或 worker 至少需要提供一个") return self class ExtractResponse(BaseModel): success: bool data: JobCard | WorkerCard | None = None errors: list[str] = Field(default_factory=list) missing_fields: list[str] = Field(default_factory=list) class BootstrapResponse(BaseModel): jobs: int workers: int skills: int categories: int regions: int class HealthStatus(BaseModel): service: str database: str rag: str timestamp: datetime class ListResponse(BaseModel): items: list[dict] total: int class MatchResponse(BaseModel): items: list[MatchResult] class ExplainResponse(BaseModel): match: MatchResult class PromptOutput(BaseModel): content: dict raw_text: str class QueryFilters(BaseModel): entity_type: str city: str | None = None region: str | None = None categories: list[str] = Field(default_factory=list) tags: list[str] = Field(default_factory=list) skills: list[str] = Field(default_factory=list) @field_validator("entity_type") @classmethod def validate_entity_type(cls, value: str) -> str: if value not in {"job", "worker"}: raise ValueError("entity_type must be job or worker") return value