Files
WeHub Mirror 2bba424313
Integration Tests - PostgreSQL + Elasticsearch + Redis / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Detect Changes (push) Has been cancelled
Java Checkstyle / java-checkstyle (push) Has been cancelled
Maven Collate Tests / maven-collate-ci (push) Has been cancelled
OpenMetadata Service Unit Tests / Detect Changes (push) Has been cancelled
Publish Package to Maven Central Repository / publish-maven-packages (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Build Integration Test Runtime (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Integration Test Lane (global-state) (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Integration Test Lane (multi-node) (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Integration Test Lane (parallel) (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Integration Test Lane (retry-queue) (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / ${{ github.event_name == 'pull_request_target' && github.event.action == 'labeled' && github.event.label.name != 'safe to test' && 'integration-tests-postgres-elasticsearch-redis (ignored label)' || 'integration-tests-postgres-elasticsearch-redis' }} (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Build Integration Test Runtime (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Integration Test Lane (global-state) (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / Build Integration Test Runtime (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / Integration Test Lane (global-state) (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / Integration Test Lane (multi-node) (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / Integration Test Lane (parallel) (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / Integration Test Lane (retry-queue) (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / ${{ github.event_name == 'pull_request_target' && github.event.action == 'labeled' && github.event.label.name != 'safe to test' && 'integration-tests-mysql-elasticsearch (ignored label)' || 'integration-tests-mysql-elasticsearch' }} (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Integration Test Lane (parallel) (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Integration Test Lane (retry-queue) (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / ${{ github.event_name == 'pull_request_target' && github.event.action == 'labeled' && github.event.label.name != 'safe to test' && 'integration-tests-postgres-opensearch (ignored label)' || 'integration-tests-postgres-opensearch' }} (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / k8s_operator-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests-status (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Integration Test Lane (multi-node) (push) Has been cancelled
WeHub snapshot of bae3e29f60050c5970ef31185b76f560fdee41b7
2026-08-07 16:54:16 +08:00

200 lines
6.5 KiB
Python

"""Validate the public sample recipe, including its AI Governance graph."""
import argparse
import json
from pathlib import Path
from typing import Any
from _openmetadata_testutils.ometa import int_admin_ometa
from metadata.generated.schema.entity.ai.aiApplication import AIApplication
from metadata.generated.schema.entity.ai.llmModel import LLMModel
from metadata.generated.schema.entity.ai.mcpServer import McpServer
from metadata.generated.schema.entity.data.table import Table
from metadata.generated.schema.entity.services.llmService import LLMService
from metadata.generated.schema.entity.services.mcpService import McpService
from metadata.ingestion.ometa.utils import model_str
SAMPLE_FOLDER = (
Path(__file__).parent.parent
/ "ingestion"
/ "examples"
/ "sample_data"
/ "ai_governance"
)
ENTITY_TYPES = {
"llmModel": LLMModel,
"mcpServer": McpServer,
"aiApplication": AIApplication,
}
TRACKED_AI_TYPES = {
AIApplication,
LLMModel,
LLMService,
McpServer,
McpService,
}
ENTITY_IDS: dict[str, str] = {}
metadata = int_admin_ometa()
parser = argparse.ArgumentParser()
parser.add_argument(
"--id-snapshot",
type=Path,
help="Write IDs on the first run and require the same IDs on the next run.",
)
args = parser.parse_args()
def read_fixture(filename: str) -> Any:
with (SAMPLE_FOLDER / filename).open(encoding="utf-8") as fixture_file:
return json.load(fixture_file)
def require_entity(entity_type: type[Any], fqn: str) -> Any:
entity = metadata.get_by_name(entity=entity_type, fqn=fqn, fields=["*"])
if entity is None:
raise ValueError(f"{entity_type.__name__} not found: {fqn}")
if entity_type in TRACKED_AI_TYPES:
ENTITY_IDS[f"{entity_type.__name__}:{fqn}"] = model_str(entity.id)
return entity
def verify_id_snapshot(snapshot_path: Path) -> None:
if snapshot_path.exists():
expected_ids = json.loads(snapshot_path.read_text(encoding="utf-8"))
if expected_ids != ENTITY_IDS:
raise ValueError(
f"AI entity IDs changed after rerunning sample data: expected {expected_ids}, found {ENTITY_IDS}"
)
return
snapshot_path.write_text(
json.dumps(ENTITY_IDS, indent=2, sort_keys=True),
encoding="utf-8",
)
def require_single_lineage_edge(
from_type: type[Any],
from_entity: Any,
to_entity: Any,
) -> None:
lineage_graph = metadata.get_lineage_by_id(
entity=from_type,
entity_id=from_entity.id,
up_depth=0,
down_depth=1,
)
to_id = model_str(to_entity.id)
matching_edges = [
edge
for edge in (lineage_graph or {}).get("downstreamEdges", [])
if edge.get("toEntity") == to_id
]
if len(matching_edges) != 1:
raise ValueError(
f"Expected one lineage edge from {model_str(from_entity.id)} to {to_id}, found {len(matching_edges)}"
)
def require_usage_metrics(server: McpServer, expected_metrics: dict[str, Any]) -> None:
if server.usageMetrics is None:
raise ValueError(
f"MCP server lost usage metrics: {model_str(server.fullyQualifiedName)}"
)
actual_metrics = server.usageMetrics.model_dump(by_alias=True, exclude_none=True)
if actual_metrics != expected_metrics:
raise ValueError(
f"MCP server usage metrics changed for {model_str(server.fullyQualifiedName)}: "
f"expected {expected_metrics}, found {actual_metrics}"
)
require_entity(Table, "sample_data.ecommerce_db.shopify.dim_address")
services = read_fixture("services.json")
models = read_fixture("llm_models.json")
servers = read_fixture("mcp_servers.json")
applications = read_fixture("applications.json")
lineage = read_fixture("lineage.json")
for service in services["llmServices"]:
require_entity(LLMService, service["name"])
for service in services["mcpServices"]:
require_entity(McpService, service["name"])
for model in models:
require_entity(LLMModel, f"{model['service']}.{model['name']}")
for server in servers:
require_usage_metrics(
require_entity(McpServer, f"{server['service']}.{server['name']}"),
server["usageMetrics"],
)
for application in applications["registered"] + applications["shadow"]:
require_entity(AIApplication, application["name"])
for edge in lineage:
from_entity = require_entity(
ENTITY_TYPES[edge["fromType"]],
edge["fromFqn"],
)
to_entity = require_entity(
ENTITY_TYPES[edge["toType"]],
edge["toFqn"],
)
if (
metadata.get_lineage_edge(
model_str(from_entity.id),
model_str(to_entity.id),
)
is None
):
raise ValueError(
f"Lineage edge not found: {edge['fromType']}:{edge['fromFqn']} -> {edge['toType']}:{edge['toFqn']}"
)
require_single_lineage_edge(
ENTITY_TYPES[edge["fromType"]],
from_entity,
to_entity,
)
claims_copilot = require_entity(AIApplication, "claims-triage-copilot")
if (
claims_copilot.primaryModel is None
or model_str(claims_copilot.primaryModel.fullyQualifiedName)
!= "ai_governance_llm.gpt_4o_claims_prod"
):
raise ValueError("Claims copilot primary model was not resolved")
expected_mcp_server = "ai_governance_mcp.customer_profile_tools"
actual_mcp_servers = [
reference["fullyQualifiedName"]
for reference in (
claims_copilot.mcpServers.model_dump() if claims_copilot.mcpServers else []
)
]
if actual_mcp_servers != [expected_mcp_server]:
raise ValueError(
f"Claims copilot MCP server changed: expected {expected_mcp_server}, "
f"found {actual_mcp_servers}"
)
shadow_model = require_entity(
LLMModel,
"ai_governance_llm.external_shadow_research_llm",
)
if shadow_model.governanceStatus.value != "Unauthorized":
raise ValueError("External shadow model is not marked unauthorized")
if shadow_model.detection is None or not shadow_model.remediationActions:
raise ValueError("External shadow model lost detection or remediation evidence")
for shadow_application in applications["shadow"]:
entity = require_entity(AIApplication, shadow_application["name"])
if (
entity.developmentStage.value != "Unauthorized"
or entity.governanceMetadata.registrationStatus.value != "Unregistered"
):
raise ValueError(
f"Shadow application is not unauthorized and unregistered: {model_str(entity.name)}"
)
if args.id_snapshot:
verify_id_snapshot(args.id_snapshot)