aps-agent/server/shipyard_synthetic/schemas.py

263 lines
8.8 KiB
Python
Raw Permalink Normal View History

from __future__ import annotations
from typing import Any
from .registry import TABLE_SPECS
_JSON_SCHEMA = "https://json-schema.org/draft/2020-12/schema"
def _record_schema(title: str, required: tuple[str, ...]) -> dict[str, Any]:
return {
"$schema": _JSON_SCHEMA,
"title": title,
"type": "object",
"required": list(required),
"properties": {
field: {
"type": ["string", "number", "integer", "boolean", "array", "object", "null"]
}
for field in required
},
"additionalProperties": True,
}
def _domain_schema(title: str, table_names: tuple[str, ...]) -> dict[str, Any]:
return {
"$schema": _JSON_SCHEMA,
"title": title,
"type": "object",
"required": list(table_names),
"properties": {
name: {
"type": "array",
"items": {"$ref": f"tables/{name}.schema.json"},
}
for name in table_names
},
"additionalProperties": False,
}
def _manifest_schema() -> dict[str, Any]:
return {
"$schema": _JSON_SCHEMA,
"title": "Beihai synthetic shipyard APS dataset manifest",
"type": "object",
"required": [
"datasetType",
"organizationScenario",
"generatedAt",
"generatorVersion",
"schemaVersion",
"randomSeed",
"businessDigest",
"fileCount",
"files",
],
"properties": {
"datasetType": {"const": "SYNTHETIC"},
"organizationScenario": {"const": "BEIHAI_SHIPYARD_APS"},
"generatedAt": {"type": "string", "format": "date-time"},
"generatorVersion": {"type": "string"},
"schemaVersion": {"type": "string"},
"randomSeed": {"type": "integer"},
"businessDigest": {"type": "string", "minLength": 64, "maxLength": 64},
"fileCount": {"type": "integer", "minimum": 1},
"files": {
"type": "array",
"items": {
"type": "object",
"required": ["path", "bytes", "sha256"],
"properties": {
"path": {"type": "string", "minLength": 1},
"kind": {"type": "string"},
"rowCount": {"type": ["integer", "null"], "minimum": 0},
"bytes": {"type": "integer", "minimum": 0},
"sha256": {
"type": "string",
"minLength": 64,
"maxLength": 64,
},
},
"additionalProperties": False,
},
},
},
"additionalProperties": True,
}
def _skill_schema() -> dict[str, Any]:
required = (
"skillId",
"name",
"description",
"inputSchema",
"outputSchema",
"requiredData",
"algorithmCandidates",
"hardConstraints",
"softConstraints",
"fallbackAlgorithm",
"timeoutSeconds",
"validationRules",
"evidenceFields",
"version",
)
schema = _record_schema("Shipyard APS skill", required)
schema["properties"].update(
{
"skillId": {"type": "string", "pattern": "^ship-[a-z0-9-]+$"},
"timeoutSeconds": {"type": "integer", "minimum": 1},
"requiredData": {"type": "array", "items": {"type": "string"}},
"algorithmCandidates": {"type": "array", "items": {"type": "string"}},
"hardConstraints": {"type": "array", "items": {"type": "string"}},
"softConstraints": {"type": "array", "items": {"type": "string"}},
"validationRules": {"type": "array", "items": {"type": "string"}},
"evidenceFields": {"type": "array", "items": {"type": "string"}},
}
)
return schema
def _knowledge_schema() -> dict[str, Any]:
required = (
"knowledgeId",
"title",
"category",
"content",
"applicableShipTypes",
"applicableWorkshops",
"tags",
"sourceType",
"version",
"effectiveDate",
"confidence",
"relatedResourceIds",
"relatedMaterialGroups",
"relatedOperationCodes",
)
schema = _record_schema("Synthetic shipbuilding knowledge asset", required)
schema["properties"].update(
{
"sourceType": {"const": "SYNTHETIC_KNOWLEDGE"},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
"effectiveDate": {"type": "string", "format": "date"},
"applicableShipTypes": {"type": "array", "items": {"type": "string"}},
"applicableWorkshops": {"type": "array", "items": {"type": "string"}},
"tags": {"type": "array", "items": {"type": "string"}},
"relatedResourceIds": {"type": "array", "items": {"type": "string"}},
"relatedMaterialGroups": {"type": "array", "items": {"type": "string"}},
"relatedOperationCodes": {"type": "array", "items": {"type": "string"}},
}
)
return schema
def build_schema_documents() -> dict[str, dict[str, Any]]:
"""Return deterministic JSON Schema documents keyed below the schemas directory."""
groups: tuple[tuple[str, str, tuple[str, ...]], ...] = (
(
"project.schema.json",
"Project and contract domain",
("contracts", "ship-projects", "milestones"),
),
("wbs.schema.json", "Shipyard WBS domain", ("wbs", "blocks", "zones", "work-packages")),
(
"engineering.schema.json",
"Engineering and routing domain",
(
"drawings",
"ebom",
"pbom",
"mbom",
"routings",
"routing-operations",
"engineering-releases",
),
),
(
"material.schema.json",
"Material and inventory domain",
(
"materials",
"inventory",
"inventory-allocations",
"substitutes",
"planned-receipts",
"material-requirements",
"kit-readiness",
),
),
(
"planning.schema.json",
"Planning domain",
(
"production-orders",
"work-orders",
"operations",
"purchase-suggestions",
"outsource-suggestions",
"capacity-demands",
"schedule-versions",
"schedule-slots",
"resource-loads",
"conflicts",
"kpis",
),
),
(
"execution.schema.json",
"Execution domain",
("mes-orders", "operation-reports", "material-issues"),
),
(
"quality.schema.json",
"Quality domain",
("quality-inspections", "nonconformities", "rework-orders"),
),
)
documents: dict[str, dict[str, Any]] = {
"manifest.schema.json": _manifest_schema(),
"world.schema.json": {
"$schema": _JSON_SCHEMA,
"title": "Synthetic shipyard APS world",
"type": "object",
"required": ["datasetMetadata", "shipyardTables", "planningSourceHash"],
"properties": {
"datasetMetadata": {"type": "object"},
"shipyardTables": {"type": "object"},
"planningSourceHash": {"type": "string"},
},
"additionalProperties": True,
},
"scenario.schema.json": _record_schema(
"Shipyard planning scenario",
("scenarioId", "status", "datasetType"),
),
"skill.schema.json": _skill_schema(),
"knowledge-asset.schema.json": _knowledge_schema(),
"validation-report.schema.json": {
"$schema": _JSON_SCHEMA,
"title": "Synthetic dataset validation report",
"type": "object",
"required": ["valid", "blockingIssues"],
"properties": {
"valid": {"type": "boolean"},
"blockingIssues": {"type": "array", "items": {"type": "string"}},
"metrics": {"type": "object"},
},
"additionalProperties": True,
},
}
for path, title, tables in groups:
documents[path] = _domain_schema(title, tables)
for name, spec in TABLE_SPECS.items():
documents[f"tables/{name}.schema.json"] = _record_schema(
f"{name} row",
spec.required,
)
return {key: documents[key] for key in sorted(documents)}