jobhunt-platform/apps/api/app/schemas.py
hermes 17759ef564 T1: FastAPI backend core (models, state machine, approval gate) + compose test runner
- apps/api: psycopg v3 repositories, schema.sql + migration runner, state machine
  implementing the data-model transition table, approval gate with sha256 hash
  match + 24h expiry enforced at send time, EchoTransport pluggable sender,
  LLM calls routed through packages.llm-gateway with mock mode, 47 pytest tests green
- apps/api/Dockerfile.test: python 3.13-slim test image (DinD-safe: migrations
  copied as directory)
- docker-compose.yml: add api-test service on compose network (host port
  publishing is broken in this sandbox; container-to-container networking used)
- Fix: replace masked placeholder password in config.py/conftest.py defaults
2026-07-30 18:10:59 +00:00

220 lines
No EOL
4.4 KiB
Python

"""Pydantic schemas for all API contract payloads."""
from __future__ import annotations
from datetime import date
from typing import Any
from pydantic import BaseModel, Field
# --- Profile ---
class ProfileBase(BaseModel):
full_name: str = ""
email: str = ""
phone: str | None = None
location: str | None = None
headline: str | None = None
summary: str | None = None
languages: list[dict[str, Any]] = Field(default_factory=list)
hard_rules: dict[str, Any] = Field(default_factory=dict)
class ProfileUpdate(BaseModel):
full_name: str | None = None
email: str | None = None
phone: str | None = None
location: str | None = None
headline: str | None = None
summary: str | None = None
languages: list[dict[str, Any]] | None = None
hard_rules: dict[str, Any] | None = None
class ProfileOut(BaseModel):
id: str
full_name: str
email: str
phone: str | None = None
location: str | None = None
headline: str | None = None
summary: str | None = None
languages: list[dict[str, Any]] = []
hard_rules: dict[str, Any] = {}
# --- CV Section ---
class CvSectionCreate(BaseModel):
kind: str
title: str
org: str | None = None
location: str | None = None
start_date: date | None = None
end_date: date | None = None
bullets: list[str] = Field(default_factory=list)
tags: list[str] = Field(default_factory=list)
sort_order: int = 0
class CvSectionUpdate(BaseModel):
kind: str | None = None
title: str | None = None
org: str | None = None
location: str | None = None
start_date: date | None = None
end_date: date | None = None
bullets: list[str] | None = None
tags: list[str] | None = None
sort_order: int | None = None
class CvSectionOut(BaseModel):
id: str
profile_id: str
kind: str
title: str
org: str | None = None
location: str | None = None
start_date: date | None = None
end_date: date | None = None
bullets: list[str] = []
tags: list[str] = []
sort_order: int = 0
class AiAssistRequest(BaseModel):
instruction: str
class AiAssistResponse(BaseModel):
suggestions: list[str]
# --- Job Posting ---
class JobPostingCreate(BaseModel):
url: str
class JobPostingOut(BaseModel):
id: str
source: str
external_id: str | None = None
url: str
company: str
title: str
location: str | None = None
description: str = ""
fetched_at: str | None = None
class ScoreResponse(BaseModel):
score: float
rationale: dict[str, Any]
# --- Application ---
class ApplicationOut(BaseModel):
id: str
job_posting_id: str
state: str
score: float | None = None
score_rationale: dict[str, Any] | None = None
notes: str | None = None
state_changed_at: str | None = None
created_at: str | None = None
# joined posting info
company: str | None = None
title: str | None = None
location: str | None = None
class TransitionRequest(BaseModel):
to: str
# --- Artifact ---
class ArtifactCreate(BaseModel):
kind: str
content: str
class ArtifactOut(BaseModel):
id: str
application_id: str
kind: str
filename: str
content_hash: str
storage_path: str
version: int
origin: str
class CoverLetterRequest(BaseModel):
letter_text: str
class CoverLetterResponse(BaseModel):
artifact: ArtifactOut
critique: list[dict[str, Any]]
# --- Approval ---
class ApprovalCreate(BaseModel):
action: str
artifact_id: str
class ApprovalOut(BaseModel):
id: str
application_id: str
artifact_id: str
artifact_hash: str
action: str
confirmed_by_user: bool
confirmed_at: str | None = None
expires_at: str
created_at: str | None = None
# --- Outbox ---
class OutboxSendRequest(BaseModel):
approval_id: str
payload: dict[str, Any]
class OutboxOut(BaseModel):
id: str
approval_id: str
kind: str
payload: dict[str, Any]
status: str
sent_at: str | None = None
error: str | None = None
# --- Telemetry ---
class TaskRunOut(BaseModel):
id: str
task: str
model: str
provider: str
input_tokens: int
output_tokens: int
cost_usd: float | None = None
duration_ms: int
application_id: str | None = None
created_at: str | None = None
# --- Errors ---
class ErrorOut(BaseModel):
error: dict[str, str]