- Convert all 84 contracts from legacy [DEF:] to #region/#endregion syntax - Fix complexity tiers: 14 modules re-tiered (6 C4 route modules, 7 C4→C5 plugin services) - Remove forbidden tags: @RATIONALE/@REJECTED stripped from C1–C4 contracts - Add required tags: @PRE/@POST/@SIDE_EFFECT on C4, @RELATION on C3, @DATA_CONTRACT/@INVARIANT on C5 - Add belief runtime markers (reason/reflect/explore) to 7 service.py functions - Fix @LAYER: route files → UI, plugins → Domain, superset_executor → Infra - Fix pre-existing test mock_service fixture in test_orchestrator.py - 196/196 translation tests pass, zero regressions
466 lines
15 KiB
Python
466 lines
15 KiB
Python
# #region TranslateSchemas [C:2] [TYPE Module]
|
|
# @BRIEF Pydantic v2 schemas for translation API request/response serialization.
|
|
# @LAYER Domain
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Dict, List, Optional
|
|
from pydantic import BaseModel, Field
|
|
import json
|
|
|
|
|
|
# #region TranslateJobCreate [C:1] [TYPE Class]
|
|
class TranslateJobCreate(BaseModel):
|
|
name: str
|
|
description: Optional[str] = None
|
|
source_dialect: str = Field(..., description="Source database dialect (e.g. postgresql, clickhouse)")
|
|
target_dialect: str = Field(..., description="Target database dialect (e.g. postgresql, clickhouse)")
|
|
database_dialect: Optional[str] = Field(None, description="Detected dialect from Superset connection at save time")
|
|
source_datasource_id: Optional[str] = Field(None, description="Superset datasource ID")
|
|
source_table: Optional[str] = Field(None, description="Source table name")
|
|
target_schema: Optional[str] = Field(None, description="Target table schema")
|
|
target_table: Optional[str] = Field(None, description="Target table name")
|
|
source_key_cols: Optional[List[str]] = Field(default_factory=list, description="Source key column names")
|
|
target_key_cols: Optional[List[str]] = Field(default_factory=list, description="Target key column names")
|
|
translation_column: Optional[str] = Field(None, description="Column to translate")
|
|
context_columns: Optional[List[str]] = Field(default_factory=list, description="Context column names")
|
|
target_language: Optional[str] = Field(None, description="Target language code")
|
|
provider_id: Optional[str] = Field(None, description="LLM provider ID")
|
|
batch_size: int = Field(50, description="Records per batch")
|
|
upsert_strategy: str = Field("MERGE", description="UPSERT strategy: MERGE, INSERT, UPDATE")
|
|
dictionary_ids: Optional[List[str]] = Field(default_factory=list, description="Associated terminology dictionary IDs")
|
|
environment_id: Optional[str] = Field(None, description="Superset environment ID")
|
|
target_database_id: Optional[str] = Field(None, description="Superset database ID for INSERT via SQL Lab")
|
|
# #endregion TranslateJobCreate
|
|
|
|
|
|
# #region TranslateJobUpdate [C:1] [TYPE Class]
|
|
class TranslateJobUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
source_dialect: Optional[str] = None
|
|
target_dialect: Optional[str] = None
|
|
database_dialect: Optional[str] = None
|
|
source_datasource_id: Optional[str] = None
|
|
source_table: Optional[str] = None
|
|
target_schema: Optional[str] = None
|
|
target_table: Optional[str] = None
|
|
source_key_cols: Optional[List[str]] = None
|
|
target_key_cols: Optional[List[str]] = None
|
|
translation_column: Optional[str] = None
|
|
context_columns: Optional[List[str]] = None
|
|
target_language: Optional[str] = None
|
|
provider_id: Optional[str] = None
|
|
batch_size: Optional[int] = None
|
|
upsert_strategy: Optional[str] = None
|
|
status: Optional[str] = None
|
|
dictionary_ids: Optional[List[str]] = None
|
|
environment_id: Optional[str] = None
|
|
target_database_id: Optional[str] = None
|
|
# #endregion TranslateJobUpdate
|
|
|
|
|
|
# #region TranslateJobResponse [C:1] [TYPE Class]
|
|
class TranslateJobResponse(BaseModel):
|
|
id: str
|
|
name: str
|
|
description: Optional[str] = None
|
|
source_dialect: str
|
|
target_dialect: str
|
|
database_dialect: Optional[str] = None
|
|
source_datasource_id: Optional[str] = None
|
|
source_table: Optional[str] = None
|
|
target_schema: Optional[str] = None
|
|
target_table: Optional[str] = None
|
|
source_key_cols: Optional[List[str]] = None
|
|
target_key_cols: Optional[List[str]] = None
|
|
translation_column: Optional[str] = None
|
|
context_columns: Optional[List[str]] = None
|
|
target_language: Optional[str] = None
|
|
provider_id: Optional[str] = None
|
|
batch_size: int = 50
|
|
upsert_strategy: str = "MERGE"
|
|
status: str
|
|
created_by: Optional[str] = None
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
dictionary_ids: Optional[List[str]] = None
|
|
environment_id: Optional[str] = None
|
|
target_database_id: Optional[str] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion TranslateJobResponse
|
|
|
|
|
|
# #region DatasourceColumnResponse [C:1] [TYPE Class]
|
|
class DatasourceColumnResponse(BaseModel):
|
|
name: str
|
|
type: Optional[str] = None
|
|
is_physical: bool = True
|
|
is_dttm: bool = False
|
|
description: Optional[str] = None
|
|
# #endregion DatasourceColumnResponse
|
|
|
|
|
|
# #region DatasourceColumnsResponse [C:1] [TYPE Class]
|
|
class DatasourceColumnsResponse(BaseModel):
|
|
datasource_id: int
|
|
datasource_name: Optional[str] = None
|
|
schema_name: Optional[str] = None
|
|
database_dialect: str
|
|
columns: List[DatasourceColumnResponse] = []
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion DatasourceColumnsResponse
|
|
|
|
|
|
# #region DuplicateJobResponse [C:1] [TYPE Class]
|
|
class DuplicateJobResponse(BaseModel):
|
|
id: str
|
|
name: str
|
|
message: str = "Job duplicated successfully"
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion DuplicateJobResponse
|
|
|
|
|
|
# #region DictionaryCreate [C:1] [TYPE Class]
|
|
class DictionaryCreate(BaseModel):
|
|
name: str
|
|
description: Optional[str] = None
|
|
source_dialect: str
|
|
target_dialect: str
|
|
is_active: bool = True
|
|
# #endregion DictionaryCreate
|
|
|
|
|
|
# #region DictionaryImport [C:1] [TYPE Class]
|
|
class DictionaryImport(BaseModel):
|
|
content: str = Field(..., description="CSV or TSV content as raw string")
|
|
delimiter: Optional[str] = Field(None, description="Detected or forced delimiter: ',' or '\\t'. Auto-detect if omitted.")
|
|
on_conflict: str = Field("overwrite", description="'overwrite' or 'keep_existing' or 'cancel'")
|
|
preview_only: bool = Field(False, description="If true, return preview without applying")
|
|
# #endregion DictionaryImport
|
|
|
|
|
|
# #region DictionaryResponse [C:1] [TYPE Class]
|
|
class DictionaryResponse(BaseModel):
|
|
id: str
|
|
name: str
|
|
description: Optional[str] = None
|
|
source_dialect: str
|
|
target_dialect: str
|
|
is_active: bool
|
|
created_by: Optional[str] = None
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
entry_count: Optional[int] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion DictionaryResponse
|
|
|
|
|
|
# #region DictionaryEntryCreate [C:1] [TYPE Class]
|
|
class DictionaryEntryCreate(BaseModel):
|
|
source_term: str = Field(..., description="Source term to translate")
|
|
target_term: str = Field(..., description="Target/translated term")
|
|
context_notes: Optional[str] = Field(None, description="Optional context notes")
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion DictionaryEntryCreate
|
|
|
|
|
|
# #region DictionaryEntryResponse [C:1] [TYPE Class]
|
|
class DictionaryEntryResponse(BaseModel):
|
|
id: str
|
|
dictionary_id: str
|
|
source_term: str
|
|
source_term_normalized: str
|
|
target_term: str
|
|
context_notes: Optional[str] = None
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion DictionaryEntryResponse
|
|
|
|
|
|
# #region DictionaryImportResult [C:1] [TYPE Class]
|
|
class DictionaryImportResult(BaseModel):
|
|
total: int = 0
|
|
created: int = 0
|
|
updated: int = 0
|
|
skipped: int = 0
|
|
errors: List[Dict[str, Any]] = Field(default_factory=list)
|
|
preview: List[Dict[str, Any]] = Field(default_factory=list, description="Preview rows with conflict flags")
|
|
# #endregion DictionaryImportResult
|
|
|
|
|
|
# #region PreviewRequest [C:1] [TYPE Class]
|
|
class PreviewRequest(BaseModel):
|
|
sample_size: int = Field(10, ge=1, le=100, description="Number of sample rows to preview")
|
|
prompt_template: Optional[str] = Field(None, description="Optional custom prompt template")
|
|
env_id: Optional[str] = Field(None, description="Superset environment ID for preview data fetch")
|
|
# #endregion PreviewRequest
|
|
|
|
|
|
# #region PreviewRowUpdate [C:1] [TYPE Class]
|
|
class PreviewRowUpdate(BaseModel):
|
|
action: str = Field(..., description="'approve', 'reject', or 'edit'")
|
|
translation: Optional[str] = Field(None, description="Edited translation (required for 'edit' action)")
|
|
feedback: Optional[str] = Field(None, description="Optional feedback/comment")
|
|
# #endregion PreviewRowUpdate
|
|
|
|
|
|
# #region PreviewAcceptResponse [C:1] [TYPE Class]
|
|
class PreviewAcceptResponse(BaseModel):
|
|
id: str
|
|
job_id: str
|
|
status: str
|
|
created_by: Optional[str] = None
|
|
created_at: datetime
|
|
expires_at: Optional[datetime] = None
|
|
records: List['PreviewRow'] = []
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion PreviewAcceptResponse
|
|
|
|
|
|
# #region CostEstimate [C:1] [TYPE Class]
|
|
class CostEstimate(BaseModel):
|
|
sample_size: int = 0
|
|
sample_prompt_tokens: int = 0
|
|
sample_output_tokens: int = 0
|
|
sample_total_tokens: int = 0
|
|
sample_cost: float = 0.0
|
|
estimated_total_rows: int = 0
|
|
estimated_tokens: int = 0
|
|
estimated_cost: float = 0.0
|
|
# #endregion CostEstimate
|
|
|
|
|
|
# #region TermCorrectionSubmit [C:1] [TYPE Class]
|
|
class TermCorrectionSubmit(BaseModel):
|
|
source_term: str
|
|
incorrect_target_term: str
|
|
corrected_target_term: str
|
|
dictionary_id: Optional[str] = Field(None, description="Target dictionary ID (language-filtered)")
|
|
origin_run_id: Optional[str] = Field(None, description="Run ID from which this correction originated")
|
|
origin_row_key: Optional[str] = Field(None, description="Row key within the run")
|
|
# #endregion TermCorrectionSubmit
|
|
|
|
|
|
# #region TermCorrectionBulkSubmit [C:1] [TYPE Class]
|
|
class TermCorrectionBulkSubmit(BaseModel):
|
|
corrections: List[TermCorrectionSubmit]
|
|
dictionary_id: str = Field(..., description="Target dictionary ID for all corrections")
|
|
# #endregion TermCorrectionBulkSubmit
|
|
|
|
|
|
# #region CorrectionConflictResult [C:1] [TYPE Class]
|
|
class CorrectionConflictResult(BaseModel):
|
|
source_term: str
|
|
existing_target_term: str
|
|
submitted_target_term: str
|
|
action: str = "keep_existing"
|
|
# #endregion CorrectionConflictResult
|
|
|
|
|
|
# #region CorrectionSubmitResponse [C:1] [TYPE Class]
|
|
class CorrectionSubmitResponse(BaseModel):
|
|
entry_id: Optional[str] = None
|
|
action: str # "created", "updated", "conflict_detected", "skipped"
|
|
source_term: str
|
|
target_term: str
|
|
conflict: Optional[CorrectionConflictResult] = None
|
|
message: Optional[str] = None
|
|
# #endregion CorrectionSubmitResponse
|
|
|
|
|
|
# #region ScheduleConfig [C:1] [TYPE Class]
|
|
class ScheduleConfig(BaseModel):
|
|
cron_expression: str = Field(..., description="Cron expression for scheduling (e.g. '0 2 * * *')")
|
|
timezone: str = Field("UTC", description="Timezone for the cron schedule (e.g. 'UTC', 'Europe/Moscow')")
|
|
is_active: bool = True
|
|
# #endregion ScheduleConfig
|
|
|
|
|
|
# #region ScheduleResponse [C:1] [TYPE Class]
|
|
class ScheduleResponse(BaseModel):
|
|
id: str
|
|
job_id: str
|
|
cron_expression: str
|
|
timezone: str
|
|
is_active: bool
|
|
last_run_at: Optional[datetime] = None
|
|
next_run_at: Optional[datetime] = None
|
|
created_by: Optional[str] = None
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion ScheduleResponse
|
|
|
|
|
|
# #region NextExecutionResponse [C:1] [TYPE Class]
|
|
class NextExecutionResponse(BaseModel):
|
|
job_id: str
|
|
cron_expression: str
|
|
timezone: str
|
|
next_executions: List[str] = []
|
|
# #endregion NextExecutionResponse
|
|
|
|
|
|
# #region TranslationRunResponse [C:1] [TYPE Class]
|
|
class TranslationRunResponse(BaseModel):
|
|
id: str
|
|
job_id: str
|
|
status: str
|
|
trigger_type: Optional[str] = None
|
|
started_at: Optional[datetime] = None
|
|
completed_at: Optional[datetime] = None
|
|
error_message: Optional[str] = None
|
|
total_records: int = 0
|
|
successful_records: int = 0
|
|
failed_records: int = 0
|
|
skipped_records: int = 0
|
|
insert_status: Optional[str] = None
|
|
superset_execution_id: Optional[str] = None
|
|
config_snapshot: Optional[Dict[str, Any]] = None
|
|
key_hash: Optional[str] = None
|
|
config_hash: Optional[str] = None
|
|
dict_snapshot_hash: Optional[str] = None
|
|
created_by: Optional[str] = None
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion TranslationRunResponse
|
|
|
|
|
|
# #region RunDetailResponse [C:1] [TYPE Class]
|
|
class RunDetailResponse(BaseModel):
|
|
run: TranslationRunResponse
|
|
records: List[Dict[str, Any]] = Field(default_factory=list, description="Paginated translation records")
|
|
events: List[Dict[str, Any]] = Field(default_factory=list, description="Run events")
|
|
event_invariants: Optional[Dict[str, Any]] = None
|
|
batch_count: int = 0
|
|
# #endregion RunDetailResponse
|
|
|
|
|
|
# #region RunHistoryFilter [C:1] [TYPE Class]
|
|
class RunHistoryFilter(BaseModel):
|
|
job_id: Optional[str] = None
|
|
status: Optional[str] = None
|
|
trigger_type: Optional[str] = None
|
|
created_by: Optional[str] = None
|
|
date_from: Optional[datetime] = None
|
|
date_to: Optional[datetime] = None
|
|
page: int = 1
|
|
page_size: int = 20
|
|
# #endregion RunHistoryFilter
|
|
|
|
|
|
# #region RunListResponse [C:1] [TYPE Class]
|
|
class RunListResponse(BaseModel):
|
|
items: List[TranslationRunResponse] = []
|
|
total: int = 0
|
|
page: int = 1
|
|
page_size: int = 20
|
|
# #endregion RunListResponse
|
|
|
|
|
|
# #region AggregatedMetricsResponse [C:1] [TYPE Class]
|
|
class AggregatedMetricsResponse(BaseModel):
|
|
job_id: str
|
|
total_runs: int = 0
|
|
successful_runs: int = 0
|
|
failed_runs: int = 0
|
|
cancelled_runs: int = 0
|
|
total_records: int = 0
|
|
successful_records: int = 0
|
|
failed_records: int = 0
|
|
skipped_records: int = 0
|
|
cumulative_tokens: Optional[int] = None
|
|
cumulative_cost: Optional[float] = None
|
|
avg_duration_ms: Optional[float] = None
|
|
last_run_at: Optional[datetime] = None
|
|
next_scheduled_run: Optional[datetime] = None
|
|
# #endregion AggregatedMetricsResponse
|
|
|
|
|
|
# #region PreviewRow [C:1] [TYPE Class]
|
|
class PreviewRow(BaseModel):
|
|
id: str
|
|
source_sql: Optional[str] = None
|
|
target_sql: Optional[str] = None
|
|
source_object_type: Optional[str] = None
|
|
source_object_id: Optional[str] = None
|
|
source_object_name: Optional[str] = None
|
|
status: str = "PENDING"
|
|
feedback: Optional[str] = None
|
|
# #endregion PreviewRow
|
|
|
|
|
|
# #region TranslationPreviewResponse [C:1] [TYPE Class]
|
|
class TranslationPreviewResponse(BaseModel):
|
|
id: str
|
|
job_id: str
|
|
run_id: Optional[str] = None
|
|
status: str
|
|
created_by: Optional[str] = None
|
|
created_at: datetime
|
|
expires_at: Optional[datetime] = None
|
|
records: List[PreviewRow] = []
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion TranslationPreviewResponse
|
|
|
|
|
|
# #region TranslationBatchResponse [C:1] [TYPE Class]
|
|
class TranslationBatchResponse(BaseModel):
|
|
id: str
|
|
run_id: str
|
|
batch_index: int
|
|
status: str
|
|
total_records: int = 0
|
|
successful_records: int = 0
|
|
failed_records: int = 0
|
|
started_at: Optional[datetime] = None
|
|
completed_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion TranslationBatchResponse
|
|
|
|
|
|
# #region MetricsResponse [C:1] [TYPE Class]
|
|
class MetricsResponse(BaseModel):
|
|
job_id: str
|
|
snapshot_date: datetime
|
|
total_jobs: int = 0
|
|
total_runs: int = 0
|
|
total_records: int = 0
|
|
successful_records: int = 0
|
|
failed_records: int = 0
|
|
skipped_records: int = 0
|
|
avg_duration_ms: Optional[int] = None
|
|
p50_duration_ms: Optional[int] = None
|
|
p95_duration_ms: Optional[int] = None
|
|
p99_duration_ms: Optional[int] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# #endregion MetricsResponse
|
|
|
|
# #endregion TranslateSchemas
|