Files
2026-07-13 13:32:05 +08:00

97 lines
3.1 KiB
Python

"""Pydantic models for the Confident AI CLI login/onboarding API.
These mirror the backend contracts:
- request bodies validated by the backend's Zod schemas
(`DeviceCodeRequestSchema`, `UserCodeRequestSchema`,
`CompleteCliOnboardingRequestSchema`), and
- the response payloads returned by /cli/auth/sessions* and /cli/onboarding*.
Field names are snake_case in Python and map to the camelCase wire format
through validation/serialization aliases: responses are parsed with
`Model.model_validate(data)` and request bodies serialize via
`to_payload()`. Fields without a default are required — if the server omits
them, parsing fails with a `pydantic.ValidationError` instead of silently
substituting a value.
"""
from typing import Dict, List, Literal, Optional
from pydantic import BaseModel, ConfigDict, Field
DEFAULT_EXPIRES_IN_SECONDS = 600
DEFAULT_POLL_INTERVAL_SECONDS = 3
class DevicePairing(BaseModel):
"""Parsed POST /cli/auth/sessions response."""
model_config = ConfigDict(populate_by_name=True)
user_code: str = Field(validation_alias="userCode")
device_code: str = Field(validation_alias="deviceCode")
verification_url: str = Field(validation_alias="verificationUriComplete")
expires_in: int = Field(
DEFAULT_EXPIRES_IN_SECONDS, validation_alias="expiresIn"
)
interval: int = DEFAULT_POLL_INTERVAL_SECONDS
class CliAuthorization(BaseModel):
"""Parsed POST /cli/auth/sessions/token response once the browser has
authorized the pairing."""
model_config = ConfigDict(populate_by_name=True)
setup_token: str = Field(validation_alias="setupToken")
email: Optional[str] = None
class CliOnboardingUser(BaseModel):
"""`user` in the GET /cli/onboarding response."""
name: Optional[str] = None
email: Optional[str] = None
class CliOnboardingProject(BaseModel):
"""One entry of `projects` in the GET /cli/onboarding response."""
model_config = ConfigDict(populate_by_name=True)
id: str
name: str
can_create_api_key: bool = Field(validation_alias="canCreateApiKey")
class CliOnboardingContext(BaseModel):
"""Typed GET /cli/onboarding response.
`user` is set for new users; `projects` is populated for existing users.
"""
state: Literal["new_user", "existing_user"]
user: Optional[CliOnboardingUser] = None
projects: List[CliOnboardingProject] = Field(default_factory=list)
class NewUserOnboardingRequest(BaseModel):
"""POST /cli/onboarding/complete body for a first-time user; the backend
creates their organization and first project."""
user_name: str = Field(serialization_alias="userName")
organization_name: str = Field(serialization_alias="organizationName")
project_name: str = Field(serialization_alias="projectName")
def to_payload(self) -> Dict[str, str]:
return self.model_dump(by_alias=True)
class ExistingProjectKeyRequest(BaseModel):
"""POST /cli/onboarding/complete body for an existing user; the backend
issues a key for one of their projects."""
project_id: str = Field(serialization_alias="projectId")
def to_payload(self) -> Dict[str, str]:
return self.model_dump(by_alias=True)