Spaces:
Sleeping
Sleeping
| """Pydantic schemas shared across services. | |
| Phase A / P0 scaffold. Field sets follow the build plan; fill in | |
| validators and constraints in subsequent steps. | |
| """ | |
| from __future__ import annotations | |
| from typing import Any, Literal, Optional | |
| from pydantic import BaseModel, Field | |
| Confidence = Literal["High", "Medium", "Low"] | |
| ExtractionConfidence = Literal["high", "medium", "low"] | |
| SourceType = Literal[ | |
| "structured_profile_json", | |
| "skillarbitrage_dossier_roadmap", | |
| "linkedin_optimization", | |
| "offer_blueprint", | |
| "upwork_profile", | |
| "linkedin_profile", | |
| "pricing_or_service_package", | |
| "portfolio_or_case_study", | |
| "testimonial_or_review", | |
| "certification_or_course", | |
| "discovery_call_transcript", | |
| "resume_or_cv", | |
| "past_proposal", | |
| "client_research", | |
| "niche_research", | |
| "personal_branding", | |
| "strategy_document", | |
| "notes_or_misc_profile_context", | |
| "generic_profile_document", | |
| "dossier_template_json", | |
| "unknown_supported_file", | |
| ] | |
| SOURCE_PRIORITY: dict[str, int] = { | |
| "structured_profile_json": 1, | |
| "skillarbitrage_dossier_roadmap": 2, | |
| "linkedin_optimization": 3, | |
| "offer_blueprint": 4, | |
| "upwork_profile": 5, | |
| "linkedin_profile": 6, | |
| "pricing_or_service_package": 7, | |
| "portfolio_or_case_study": 8, | |
| "testimonial_or_review": 9, | |
| "certification_or_course": 10, | |
| "discovery_call_transcript": 11, | |
| "resume_or_cv": 12, | |
| "past_proposal": 13, | |
| "client_research": 14, | |
| "niche_research": 15, | |
| "personal_branding": 16, | |
| "strategy_document": 17, | |
| "notes_or_misc_profile_context": 18, | |
| "generic_profile_document": 19, | |
| "dossier_template_json": 20, | |
| "unknown_supported_file": 21, | |
| } | |
| SOURCE_TYPE_LABELS: dict[str, str] = { | |
| "structured_profile_json": "Structured profile JSON", | |
| "skillarbitrage_dossier_roadmap": "SkillArbitrage dossier / roadmap", | |
| "linkedin_optimization": "LinkedIn optimization document", | |
| "offer_blueprint": "Offer blueprint", | |
| "upwork_profile": "Upwork profile", | |
| "linkedin_profile": "LinkedIn profile", | |
| "pricing_or_service_package": "Pricing / service package", | |
| "portfolio_or_case_study": "Portfolio / case study", | |
| "testimonial_or_review": "Testimonial / review", | |
| "certification_or_course": "Certification / course", | |
| "discovery_call_transcript": "Discovery call transcript", | |
| "resume_or_cv": "Resume / CV", | |
| "past_proposal": "Past proposal", | |
| "client_research": "Client / target-client research", | |
| "niche_research": "Niche / market research", | |
| "personal_branding": "Personal branding", | |
| "strategy_document": "Strategy document", | |
| "notes_or_misc_profile_context": "Notes / misc profile context", | |
| "generic_profile_document": "Generic profile document", | |
| "dossier_template_json": "Dossier template JSON", | |
| "unknown_supported_file": "Unknown supported file", | |
| } | |
| ClaimType = Literal[ | |
| "identity", | |
| "positioning", | |
| "selected_offer", | |
| "target_client", | |
| "service", | |
| "deliverable", | |
| "skill", | |
| "tool", | |
| "industry", | |
| "project", | |
| "experience", | |
| "work_history", | |
| "metric", | |
| "testimonial", | |
| "certification", | |
| "education", | |
| "language", | |
| "pricing", | |
| "availability", | |
| "proposal_preference", | |
| "weakness_or_constraint", | |
| "portfolio", | |
| "achievement", | |
| "location", | |
| "timezone", | |
| "guarantee", | |
| "other_relevant_evidence", | |
| ] | |
| ExtractionStatus = Literal["ok", "partial", "failed", "metadata_only", "empty"] | |
| ConflictStatus = Literal["none", "conflicting", "superseded", "supporting"] | |
| UsedFor = Literal["recommendation", "proposal", "missing_info", "ignored"] | |
| class ExtractedField(BaseModel): | |
| name: str | |
| value: Optional[str] = None | |
| confidence: Confidence = "Low" | |
| visible: bool = True | |
| FieldSource = Literal[ | |
| "not visible", | |
| "ocr extracted", | |
| "user corrected", | |
| "manually entered", | |
| ] | |
| class ScreenshotField(BaseModel): | |
| value: str = "Not visible" | |
| confidence: ExtractionConfidence = "low" | |
| source: str = "not visible" | |
| class ConfirmedField(BaseModel): | |
| name: str | |
| value: str = "Not visible" | |
| confidence: ExtractionConfidence = "low" | |
| source: FieldSource = "not visible" | |
| class ScreenshotExtraction(BaseModel): | |
| job_title: ScreenshotField = Field(default_factory=ScreenshotField) | |
| job_description: ScreenshotField = Field(default_factory=ScreenshotField) | |
| client_need: ScreenshotField = Field(default_factory=ScreenshotField) | |
| required_deliverables: ScreenshotField = Field(default_factory=ScreenshotField) | |
| required_skills: ScreenshotField = Field(default_factory=ScreenshotField) | |
| budget_or_rate: ScreenshotField = Field(default_factory=ScreenshotField) | |
| project_type: ScreenshotField = Field(default_factory=ScreenshotField) | |
| experience_level: ScreenshotField = Field(default_factory=ScreenshotField) | |
| project_duration: ScreenshotField = Field(default_factory=ScreenshotField) | |
| posted_date: ScreenshotField = Field(default_factory=ScreenshotField) | |
| proposal_count: ScreenshotField = Field(default_factory=ScreenshotField) | |
| payment_verification: ScreenshotField = Field(default_factory=ScreenshotField) | |
| client_rating: ScreenshotField = Field(default_factory=ScreenshotField) | |
| client_total_spend: ScreenshotField = Field(default_factory=ScreenshotField) | |
| hire_rate: ScreenshotField = Field(default_factory=ScreenshotField) | |
| client_location: ScreenshotField = Field(default_factory=ScreenshotField) | |
| connects_required: ScreenshotField = Field(default_factory=ScreenshotField) | |
| # Added for the job evaluation signals (Instruction Set 1). | |
| contract_type: ScreenshotField = Field(default_factory=ScreenshotField) | |
| client_jobs_posted: ScreenshotField = Field(default_factory=ScreenshotField) | |
| client_hires: ScreenshotField = Field(default_factory=ScreenshotField) | |
| client_last_active: ScreenshotField = Field(default_factory=ScreenshotField) | |
| hidden_keyword: ScreenshotField = Field(default_factory=ScreenshotField) | |
| screening_questions: ScreenshotField = Field(default_factory=ScreenshotField) | |
| class JobOpportunity(BaseModel): | |
| title: Optional[str] = None | |
| client_need: Optional[str] = None | |
| budget: Optional[str] = None | |
| proposal_count: Optional[int] = None | |
| required_skills: list[str] = Field(default_factory=list) | |
| client_quality: Optional[str] = None | |
| client_questions: list[str] = Field(default_factory=list) | |
| fields: list[ExtractedField] = Field(default_factory=list) | |
| class DossierFile(BaseModel): | |
| path: str | |
| kind: str | |
| modified_at: Optional[str] = None | |
| readable: bool = True | |
| class EvidenceItem(BaseModel): | |
| claim: str | |
| source_file: str | |
| location: str | |
| kind: str | |
| class ChunkRecord(BaseModel): | |
| chunk_id: str | |
| file_name: str | |
| file_path: str | |
| file_type: str | |
| source_type: SourceType | |
| source_priority: int | |
| page_number: Optional[int] = None | |
| section_name: Optional[str] = None | |
| extracted_text: str = "" | |
| json_data: Optional[Any] = None | |
| extraction_status: ExtractionStatus = "ok" | |
| extraction_warning: Optional[str] = None | |
| class ProofPoint(BaseModel): | |
| evidence_id: str | |
| source_file: str | |
| source_type: SourceType | |
| source_priority: int | |
| source_location: str | |
| claim_type: ClaimType | |
| claim_text: str | |
| normalized_value: Optional[str] = None | |
| skills: list[str] = Field(default_factory=list) | |
| industries: list[str] = Field(default_factory=list) | |
| tools: list[str] = Field(default_factory=list) | |
| metrics: list[str] = Field(default_factory=list) | |
| confidence: ExtractionConfidence = "medium" | |
| conflict_status: ConflictStatus = "none" | |
| used_for: UsedFor = "proposal" | |
| def claim(self) -> str: | |
| """Spec-aligned alias for ``claim_text``.""" | |
| return self.claim_text | |
| class CanonicalProfileField(BaseModel): | |
| value: Any = None | |
| evidence_ids: list[str] = Field(default_factory=list) | |
| source_confidence: ExtractionConfidence = "low" | |
| conflict_note: Optional[str] = None | |
| class CanonicalFreelancerProfile(BaseModel): | |
| name: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| title_or_positioning: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| location: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| timezone: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| languages: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| selected_offer: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| guarantee: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| target_client: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| industries: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| services: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| deliverables: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| skills: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| tools: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| work_history: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| education: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| certifications: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| portfolio_or_proof: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| achievements: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| pricing: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| preferred_project_types: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| proposal_preferences: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| strengths: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| weaknesses_to_account_for: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| missing_information: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| source_summary: CanonicalProfileField = Field(default_factory=CanonicalProfileField) | |
| MatchLevel = Literal["direct", "adjacent", "weak", "missing"] | |
| ProofRating = Literal["strong", "medium", "weak", "unknown"] | |
| class OpportunityProfile(BaseModel): | |
| """Compact, normalized view of one uploaded Upwork opportunity. | |
| Built from the confirmed job fields before matching/scoring so the | |
| LLM and the deterministic rules both reason about the same compact | |
| structure rather than the raw screenshot fields. | |
| """ | |
| opportunity_title: Optional[str] = None | |
| client_problem: Optional[str] = None | |
| required_skills: list[str] = Field(default_factory=list) | |
| required_tools: list[str] = Field(default_factory=list) | |
| required_deliverables: Optional[str] = None | |
| industry_or_domain: Optional[str] = None | |
| expected_experience_level: Optional[str] = None | |
| budget_or_rate: Optional[str] = None | |
| proposal_count: Optional[str] = None | |
| client_quality_indicators: dict[str, str] = Field(default_factory=dict) | |
| visible_risks: list[str] = Field(default_factory=list) | |
| missing_fields: list[str] = Field(default_factory=list) | |
| class RequiredSkillMatch(BaseModel): | |
| """One opportunity requirement compared against the evidence index.""" | |
| requirement: str = "" | |
| match_level: MatchLevel = "missing" | |
| matching_evidence_ids: list[str] = Field(default_factory=list) | |
| reason: str = "" | |
| class PortfolioProofAnalysis(BaseModel): | |
| """LLM signal describing what real proof supports the opportunity. | |
| ``score_signal`` is a 0-100 *signal only* — the final numeric | |
| Portfolio Proof score is computed deterministically in | |
| :mod:`app.services.scoring`, never taken directly from the LLM. | |
| """ | |
| rating: ProofRating = "unknown" | |
| score_signal: int = 0 | |
| direct_proof: list[str] = Field(default_factory=list) | |
| adjacent_proof: list[str] = Field(default_factory=list) | |
| missing_proof: list[str] = Field(default_factory=list) | |
| matched_portfolio_items: list[str] = Field(default_factory=list) | |
| matched_projects: list[str] = Field(default_factory=list) | |
| matched_testimonials: list[str] = Field(default_factory=list) | |
| matched_work_history: list[str] = Field(default_factory=list) | |
| matched_skills: list[str] = Field(default_factory=list) | |
| matched_tools: list[str] = Field(default_factory=list) | |
| evidence_ids_used: list[str] = Field(default_factory=list) | |
| short_reason: str = "" | |
| confidence: ExtractionConfidence = "low" | |
| class ScoreComponent(BaseModel): | |
| """One weighted score component plus its explanation. | |
| ``value``/``max_value`` are the points awarded out of the component | |
| weight; ``short_reason`` is user-facing; ``evidence_ids_used`` are | |
| shown only behind the debug panel; ``source`` records how the value | |
| was derived. | |
| """ | |
| value: int = 0 | |
| max_value: int = 0 | |
| short_reason: str = "" | |
| evidence_ids_used: list[str] = Field(default_factory=list) | |
| confidence: ExtractionConfidence = "low" | |
| source: str = "llm_match_result + deterministic_scoring" | |
| class SubScores(BaseModel): | |
| profile_fit: int = 0 | |
| portfolio_proof: int = 0 | |
| client_quality: int = 0 | |
| competition: int = 0 | |
| budget_value: int = 0 | |
| class ScoreResult(BaseModel): | |
| total: int | |
| sub_scores: SubScores | |
| confidence: Confidence | |
| job_fingerprint: str = "" | |
| components: dict[str, ScoreComponent] = Field(default_factory=dict) | |
| BeginnerResult = Literal["Apply Confidently", "Proceed With Caution", "Do Not Proceed"] | |
| PaymentStatus = Literal["verified", "not_verified", "not_visible"] | |
| ProposalBucket = Literal["low", "high", "too_high", "not_visible"] | |
| PostedBucket = Literal["fresh", "recent", "stale", "not_visible"] | |
| RatingBucket = Literal["ok", "low", "not_visible"] | |
| ExperienceBucket = Literal["entry", "intermediate", "expert", "other", "not_visible"] | |
| class BeginnerWarning(BaseModel): | |
| key: str | |
| reason: str | |
| class BeginnerJobEvaluation(BaseModel): | |
| """Result of the beginner-safety checklist for one opportunity. | |
| Produced deterministically by | |
| :func:`app.services.beginner_evaluator.evaluate` (the service returns a | |
| plain dict; this model documents and, where useful, validates the | |
| shape). ``result`` plus up to two ``reasons`` are the only fields shown | |
| on the clean UI — the per-field buckets are debug-only. | |
| """ | |
| result: BeginnerResult = "Proceed With Caution" | |
| instant_no: bool = False | |
| reasons: list[str] = Field(default_factory=list, max_length=2) | |
| warnings: list[BeginnerWarning] = Field(default_factory=list) | |
| instant_no_reasons: list[str] = Field(default_factory=list) | |
| missing_fields: list[str] = Field(default_factory=list) | |
| missing_info_note: Optional[str] = None | |
| reduce_confidence: bool = False | |
| triggered_rule: str = "" | |
| score_signals: dict[str, bool] = Field(default_factory=dict) | |
| fields: dict[str, Any] = Field(default_factory=dict) | |
| class Recommendation(BaseModel): | |
| verdict: Literal["Strongly Proceed", "Proceed", "Proceed with Caution", "Do Not Proceed"] | |
| short_verdict: str = "" | |
| why: str = "" | |
| match_strengths: list[str] = Field(default_factory=list, max_length=2) | |
| concerns: list[str] = Field(default_factory=list, max_length=2) | |
| connects_recommendation: Optional[str] = None | |
| best_proposal_angle: Optional[str] = None | |
| # Backwards-compat aliases used by older callers / tests. | |
| one_line: str = "" | |
| reasoning: str = "" | |
| strengths: list[str] = Field(default_factory=list) | |
| connect_guidance: Optional[str] = None | |
| proposal_angle: Optional[str] = None | |