rezabarkhordary commited on
Commit
b835e52
·
verified ·
1 Parent(s): ffdef3c

Update sovereign_core.py

Browse files
Files changed (1) hide show
  1. sovereign_core.py +292 -115
sovereign_core.py CHANGED
@@ -1,14 +1,25 @@
1
-
2
  import os
3
  import json
4
  import time
5
  import uuid
6
  import hashlib
7
- from typing import Optional, List, Dict, Any
 
 
 
 
8
 
9
  ENGINE_NAME = "AI_Sovereign_Sentinel_Core_v1"
10
  AUTHORITY_NAME = "DataClear Sovereign Authority"
11
- SOVEREIGN_VERSION = "1.1-final"
 
 
 
 
 
 
 
 
12
 
13
 
14
  class SovereignFingerprint:
@@ -32,13 +43,18 @@ class SovereignFingerprint:
32
  "version": "1.0",
33
  }
34
 
35
- def export(self):
36
  fp = self._build()
37
- with open(self.output, "w") as f:
38
  json.dump(fp, f, indent=4)
39
  return self.output, fp["fingerprint"]
40
 
41
 
 
 
 
 
 
42
  class SovereignValidator:
43
  """
44
  Simple registry to bind engine name → fingerprint string.
@@ -48,7 +64,7 @@ class SovereignValidator:
48
  def __init__(self, path: str = "sovereign_registry.json"):
49
  self.path = path
50
  if os.path.exists(self.path):
51
- with open(self.path, "r") as f:
52
  self.registry = json.load(f)
53
  else:
54
  self.registry = {}
@@ -58,7 +74,7 @@ class SovereignValidator:
58
  "fingerprint": fingerprint,
59
  "registered_at": int(time.time()),
60
  }
61
- with open(self.path, "w") as f:
62
  json.dump(self.registry, f, indent=4)
63
  return True
64
 
@@ -69,6 +85,11 @@ class SovereignValidator:
69
  return entry.get("fingerprint") == fingerprint
70
 
71
 
 
 
 
 
 
72
  def attestation_signature(att: dict) -> str:
73
  """
74
  Unified, stable signature generation for attestation objects.
@@ -83,7 +104,7 @@ def attestation_signature(att: dict) -> str:
83
  return hashlib.sha256(base.encode()).hexdigest()
84
 
85
 
86
- def generate_attestation(fp):
87
  """
88
  Create a signed attestation for a given fingerprint (dict or string).
89
  """
@@ -92,7 +113,7 @@ def generate_attestation(fp):
92
  else:
93
  fp_value = fp
94
 
95
- att = {
96
  "attestation_id": str(uuid.uuid4()),
97
  "issued_at": int(time.time()),
98
  "fingerprint": fp_value,
@@ -109,6 +130,11 @@ def verify_attestation(att: dict) -> bool:
109
  return expected == att.get("signature")
110
 
111
 
 
 
 
 
 
112
  def build_lineage_record(
113
  engine: str,
114
  fingerprint: str,
@@ -116,6 +142,8 @@ def build_lineage_record(
116
  model_version: str = "1.0",
117
  data_tags: Optional[List[str]] = None,
118
  notes: Optional[str] = None,
 
 
119
  ) -> Dict[str, Any]:
120
  """
121
  Create a minimal, cryptographically-bound lineage record for a model.
@@ -123,12 +151,16 @@ def build_lineage_record(
123
  - engine
124
  - fingerprint
125
  - version
 
126
  - optional parent model
127
  - optional dataset / domain tags
128
  into a single signed lineage_id.
129
  """
130
  created_at = int(time.time())
131
- payload = f"{engine}|{fingerprint}|{model_version}|{parent_model or ''}|{created_at}"
 
 
 
132
  lineage_hash = hashlib.sha256(payload.encode()).hexdigest()
133
 
134
  return {
@@ -139,11 +171,18 @@ def build_lineage_record(
139
  "model_version": model_version,
140
  "data_tags": data_tags or [],
141
  "notes": notes,
 
 
142
  "created_at": created_at,
143
  "lineage_hash": lineage_hash,
144
  }
145
 
146
 
 
 
 
 
 
147
  def compute_risk_profile(
148
  data_tags: Optional[List[str]] = None,
149
  deployment_env: Optional[str] = None,
@@ -151,7 +190,6 @@ def compute_risk_profile(
151
  """
152
  Very simple, rule-based risk scoring so that certificate
153
  carries a usable risk_level and risk_score.
154
- اینجا الگوریتم خیلی ساده است؛ بعداً می‌شود پیچیده‌ترش کرد.
155
  """
156
  tags = [t.lower() for t in (data_tags or [])]
157
  env = (deployment_env or "").lower().strip()
@@ -200,6 +238,140 @@ def compute_risk_profile(
200
  }
201
 
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  def generate_integrity_certificate(
204
  fp,
205
  att,
@@ -209,6 +381,8 @@ def generate_integrity_certificate(
209
  data_tags: Optional[List[str]] = None,
210
  notes: Optional[str] = None,
211
  deployment_env: Optional[str] = None,
 
 
212
  ) -> Dict[str, Any]:
213
  """
214
  Bundle fingerprint + attestation + lineage + risk profile
@@ -226,12 +400,13 @@ def generate_integrity_certificate(
226
  model_version=model_version,
227
  data_tags=data_tags,
228
  notes=notes,
 
 
229
  )
230
 
231
  risk = compute_risk_profile(data_tags=data_tags, deployment_env=deployment_env)
232
 
233
  issued_at = att["issued_at"]
234
- now_ts = int(time.time())
235
 
236
  cert: Dict[str, Any] = {
237
  "engine": ENGINE_NAME,
@@ -245,130 +420,73 @@ def generate_integrity_certificate(
245
  "threat_flags": risk["threat_flags"],
246
  "environment": deployment_env or "unspecified",
247
  "audit_id": str(uuid.uuid4()),
248
- "expires_at": issued_at + 30 * 24 * 3600, # 30 روز اعتبار
249
- "last_verified_at": now_ts,
250
- "certificate_version": "1.1",
251
- "authority": AUTHORITY_NAME,
252
  }
253
 
254
- with open(output, "w") as f:
255
  json.dump(cert, f, indent=4)
256
 
257
  return cert
258
 
259
 
260
- def verify_lineage(cert: dict) -> bool:
261
- """
262
- Basic sanity + integrity check for lineage section inside a certificate.
263
- """
264
- lineage = cert.get("lineage")
265
- if not lineage:
266
- return False
267
-
268
- if lineage.get("engine") != ENGINE_NAME:
269
- return False
270
-
271
- # fingerprint inside lineage must match certificate fingerprint
272
- if lineage.get("fingerprint") != cert.get("fingerprint"):
273
- return False
274
-
275
- engine = lineage.get("engine", "")
276
- fingerprint = lineage.get("fingerprint", "")
277
- model_version = lineage.get("model_version", "")
278
- parent_model = lineage.get("parent_model") or ""
279
- created_at = lineage.get("created_at")
280
-
281
- if created_at is None:
282
- return False
283
-
284
- payload = f"{engine}|{fingerprint}|{model_version}|{parent_model}|{created_at}"
285
- expected_hash = hashlib.sha256(payload.encode()).hexdigest()
286
- return expected_hash == lineage.get("lineage_hash")
287
-
288
-
289
- def verify_certificate(cert: dict) -> bool:
290
- """
291
- High-level check over the full certificate.
292
- """
293
- if cert.get("engine") != ENGINE_NAME:
294
- return False
295
-
296
- if cert.get("authority") != AUTHORITY_NAME:
297
- return False
298
-
299
- att = cert.get("attestation", {})
300
- if not verify_attestation(att):
301
- return False
302
-
303
- if not verify_lineage(cert):
304
- return False
305
 
306
- return True
307
 
 
 
 
 
308
 
309
- # ----------------------------------------
310
- # Centralised Sovereign Audit Log
311
- # ----------------------------------------
312
 
313
- AUDIT_LOG_FILE = "sovereign_audit_log.jsonl"
314
-
315
-
316
- class SovereignAuditLogger:
 
 
 
 
 
 
 
 
 
317
  """
318
- Append-only audit logger for Sovereign.
319
- Stores one JSON object per line in sovereign_audit_log.jsonl
320
- so it can be exported for G-Cloud / NCSC style reviews.
321
  """
 
322
 
323
- def __init__(self, path: str = AUDIT_LOG_FILE):
324
- self.path = path
325
-
326
- def log_event(
327
- self,
328
- event_type: str,
329
- engine: str,
330
- fingerprint: str,
331
- parent_model: str | None,
332
- model_version: str,
333
- data_tags: list[str] | None,
334
- risk_level: str | None,
335
- risk_score: int | None,
336
- deployment_env: str | None,
337
- extra: dict | None = None,
338
- ) -> dict:
339
- """
340
- event_type: e.g. "integrity_check", "attestation_verify", "risk_evaluation"
341
- """
342
- entry = {
343
- "event_id": str(uuid.uuid4()),
344
- "event_type": event_type,
345
- "engine": engine,
346
- "fingerprint": fingerprint,
347
- "parent_model": parent_model,
348
- "model_version": model_version,
349
- "data_tags": data_tags or [],
350
- "risk_level": risk_level,
351
- "risk_score": risk_score,
352
- "deployment_env": deployment_env,
353
- "timestamp": int(time.time()),
354
- "extra": extra or {},
355
- }
356
 
357
- # append as JSON line
358
- with open(self.path, "a", encoding="utf-8") as f:
359
- f.write(json.dumps(entry) + "\n")
360
 
361
- return entry
362
 
363
 
364
- def export_audit_log_json(path: str = AUDIT_LOG_FILE) -> list[dict]:
365
  """
366
  Load the audit log as a list of JSON objects.
367
  """
368
  if not os.path.exists(path):
369
  return []
370
 
371
- entries: list[dict] = []
372
  with open(path, "r", encoding="utf-8") as f:
373
  for line in f:
374
  line = line.strip()
@@ -392,16 +510,15 @@ def export_audit_log_csv(path: str = AUDIT_LOG_FILE) -> str:
392
  if not entries:
393
  return ""
394
 
395
- # use all keys from first entry as header
396
  headers = list(entries[0].keys())
397
- lines: list[str] = []
398
 
399
  # header row
400
  lines.append(",".join(headers))
401
 
402
  # data rows
403
  for row in entries:
404
- row_values = []
405
  for h in headers:
406
  value = row.get(h, "")
407
  if isinstance(value, list):
@@ -413,3 +530,63 @@ def export_audit_log_csv(path: str = AUDIT_LOG_FILE) -> str:
413
 
414
  return "\n".join(lines)
415
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import json
3
  import time
4
  import uuid
5
  import hashlib
6
+ from typing import Optional, List, Dict, Any, Tuple
7
+
8
+ # ---------------------------------------------------------------------
9
+ # Core identifiers
10
+ # ---------------------------------------------------------------------
11
 
12
  ENGINE_NAME = "AI_Sovereign_Sentinel_Core_v1"
13
  AUTHORITY_NAME = "DataClear Sovereign Authority"
14
+ SOVEREIGN_VERSION = "1.2-gov-ready"
15
+
16
+ # Centralised audit log path (JSONL)
17
+ AUDIT_LOG_FILE = "sovereign_audit_log.jsonl"
18
+
19
+
20
+ # ---------------------------------------------------------------------
21
+ # Fingerprinting
22
+ # ---------------------------------------------------------------------
23
 
24
 
25
  class SovereignFingerprint:
 
43
  "version": "1.0",
44
  }
45
 
46
+ def export(self) -> Tuple[str, str]:
47
  fp = self._build()
48
+ with open(self.output, "w", encoding="utf-8") as f:
49
  json.dump(fp, f, indent=4)
50
  return self.output, fp["fingerprint"]
51
 
52
 
53
+ # ---------------------------------------------------------------------
54
+ # Engine registry / validator
55
+ # ---------------------------------------------------------------------
56
+
57
+
58
  class SovereignValidator:
59
  """
60
  Simple registry to bind engine name → fingerprint string.
 
64
  def __init__(self, path: str = "sovereign_registry.json"):
65
  self.path = path
66
  if os.path.exists(self.path):
67
+ with open(self.path, "r", encoding="utf-8") as f:
68
  self.registry = json.load(f)
69
  else:
70
  self.registry = {}
 
74
  "fingerprint": fingerprint,
75
  "registered_at": int(time.time()),
76
  }
77
+ with open(self.path, "w", encoding="utf-8") as f:
78
  json.dump(self.registry, f, indent=4)
79
  return True
80
 
 
85
  return entry.get("fingerprint") == fingerprint
86
 
87
 
88
+ # ---------------------------------------------------------------------
89
+ # Attestation
90
+ # ---------------------------------------------------------------------
91
+
92
+
93
  def attestation_signature(att: dict) -> str:
94
  """
95
  Unified, stable signature generation for attestation objects.
 
104
  return hashlib.sha256(base.encode()).hexdigest()
105
 
106
 
107
+ def generate_attestation(fp) -> Dict[str, Any]:
108
  """
109
  Create a signed attestation for a given fingerprint (dict or string).
110
  """
 
113
  else:
114
  fp_value = fp
115
 
116
+ att: Dict[str, Any] = {
117
  "attestation_id": str(uuid.uuid4()),
118
  "issued_at": int(time.time()),
119
  "fingerprint": fp_value,
 
130
  return expected == att.get("signature")
131
 
132
 
133
+ # ---------------------------------------------------------------------
134
+ # Lineage + Deployment binding (multi-model metadata ready)
135
+ # ---------------------------------------------------------------------
136
+
137
+
138
  def build_lineage_record(
139
  engine: str,
140
  fingerprint: str,
 
142
  model_version: str = "1.0",
143
  data_tags: Optional[List[str]] = None,
144
  notes: Optional[str] = None,
145
+ provider: Optional[str] = None, # OpenAI / Anthropic / DeepMind / Llama / Mistral / other
146
+ model_family: Optional[str] = None, # narrow / frontier / embedding / classifier
147
  ) -> Dict[str, Any]:
148
  """
149
  Create a minimal, cryptographically-bound lineage record for a model.
 
151
  - engine
152
  - fingerprint
153
  - version
154
+ - provider (multi-model support)
155
  - optional parent model
156
  - optional dataset / domain tags
157
  into a single signed lineage_id.
158
  """
159
  created_at = int(time.time())
160
+ payload = (
161
+ f"{engine}|{fingerprint}|{model_version}|{parent_model or ''}|"
162
+ f"{provider or ''}|{model_family or ''}|{created_at}"
163
+ )
164
  lineage_hash = hashlib.sha256(payload.encode()).hexdigest()
165
 
166
  return {
 
171
  "model_version": model_version,
172
  "data_tags": data_tags or [],
173
  "notes": notes,
174
+ "provider": provider,
175
+ "model_family": model_family,
176
  "created_at": created_at,
177
  "lineage_hash": lineage_hash,
178
  }
179
 
180
 
181
+ # ---------------------------------------------------------------------
182
+ # Simple risk profile (env + data tags)
183
+ # ---------------------------------------------------------------------
184
+
185
+
186
  def compute_risk_profile(
187
  data_tags: Optional[List[str]] = None,
188
  deployment_env: Optional[str] = None,
 
190
  """
191
  Very simple, rule-based risk scoring so that certificate
192
  carries a usable risk_level and risk_score.
 
193
  """
194
  tags = [t.lower() for t in (data_tags or [])]
195
  env = (deployment_env or "").lower().strip()
 
238
  }
239
 
240
 
241
+ # ---------------------------------------------------------------------
242
+ # Compute-efficiency scoring (approximate)
243
+ # ---------------------------------------------------------------------
244
+
245
+
246
+ def estimate_compute_cost(
247
+ model_name: str,
248
+ tokens: int,
249
+ provider: Optional[str] = None,
250
+ ) -> Dict[str, Any]:
251
+ """
252
+ Tiny heuristic for 'compute efficiency' the government is obsessed with.
253
+ We approximate per-token cost by model family.
254
+ """
255
+
256
+ name = (model_name or "").lower()
257
+ provider_norm = (provider or "").lower()
258
+
259
+ if any(k in name for k in ["gpt-4", "claude-3-opus", "gemini-1.5-pro"]) or "frontier" in name:
260
+ base_cost = 3.0
261
+ family = "frontier"
262
+ elif any(k in name for k in ["gpt-4o-mini", "gpt-3.5", "sonnet", "llama-3", "mistral"]):
263
+ base_cost = 1.0
264
+ family = "general"
265
+ else:
266
+ base_cost = 0.2
267
+ family = "narrow"
268
+
269
+ estimated_units = base_cost * tokens
270
+
271
+ if family == "narrow":
272
+ band = "HIGH_EFFICIENCY"
273
+ elif family == "general":
274
+ band = "BALANCED"
275
+ else:
276
+ band = "HIGH_COMPUTE"
277
+
278
+ return {
279
+ "provider": provider_norm or None,
280
+ "model_name": model_name,
281
+ "family": family,
282
+ "estimated_compute_units": estimated_units,
283
+ "efficiency_band": band,
284
+ "tokens": tokens,
285
+ }
286
+
287
+
288
+ # ---------------------------------------------------------------------
289
+ # Policy Engine (standard & custom)
290
+ # ---------------------------------------------------------------------
291
+
292
+
293
+ class PolicyEngine:
294
+ """
295
+ Simple, pluggable policy engine.
296
+ Provides:
297
+ - standard safety templates (LLM safety / restriction)
298
+ - rule-based enforcement
299
+ - support for custom organisation policies
300
+ """
301
+
302
+ def __init__(self):
303
+ self.policies: Dict[str, Dict[str, Any]] = {}
304
+ self._load_default_policies()
305
+
306
+ def _load_default_policies(self):
307
+ self.policies["SAFE_OUTPUT_BASELINE"] = {
308
+ "id": "SAFE_OUTPUT_BASELINE",
309
+ "description": "Disallow obvious harmful, illegal or highly sensitive content.",
310
+ "rules": {
311
+ "blocked_keywords": [
312
+ "terrorist",
313
+ "child sexual",
314
+ "explosive instructions",
315
+ "bioweapon",
316
+ "financial fraud",
317
+ ]
318
+ },
319
+ }
320
+ self.policies["NO_PII_LEAK"] = {
321
+ "id": "NO_PII_LEAK",
322
+ "description": "Reduce risk of leaking obvious PII markers in logs.",
323
+ "rules": {
324
+ "blocked_markers": ["national insurance number", "passport number", "nhs number"]
325
+ },
326
+ }
327
+
328
+ def add_policy(self, policy: Dict[str, Any]) -> None:
329
+ pid = policy.get("id")
330
+ if not pid:
331
+ raise ValueError("Policy must have an 'id'")
332
+ self.policies[pid] = policy
333
+
334
+ def list_policies(self) -> List[Dict[str, Any]]:
335
+ return list(self.policies.values())
336
+
337
+ def evaluate_output(
338
+ self,
339
+ output_text: str,
340
+ active_policy_ids: Optional[List[str]] = None,
341
+ ) -> Dict[str, Any]:
342
+ text = (output_text or "").lower()
343
+ active_policy_ids = active_policy_ids or list(self.policies.keys())
344
+
345
+ violations: List[Dict[str, Any]] = []
346
+
347
+ for pid in active_policy_ids:
348
+ policy = self.policies.get(pid)
349
+ if not policy:
350
+ continue
351
+
352
+ rules = policy.get("rules", {})
353
+
354
+ for kw in rules.get("blocked_keywords", []):
355
+ if kw in text:
356
+ violations.append(
357
+ {"policy_id": pid, "type": "blocked_keyword", "value": kw}
358
+ )
359
+
360
+ for marker in rules.get("blocked_markers", []):
361
+ if marker in text:
362
+ violations.append(
363
+ {"policy_id": pid, "type": "blocked_marker", "value": marker}
364
+ )
365
+
366
+ is_allowed = len(violations) == 0
367
+ return {"is_allowed": is_allowed, "violations": violations}
368
+
369
+
370
+ # ---------------------------------------------------------------------
371
+ # Integrity certificate
372
+ # ---------------------------------------------------------------------
373
+
374
+
375
  def generate_integrity_certificate(
376
  fp,
377
  att,
 
381
  data_tags: Optional[List[str]] = None,
382
  notes: Optional[str] = None,
383
  deployment_env: Optional[str] = None,
384
+ provider: Optional[str] = None,
385
+ model_family: Optional[str] = None,
386
  ) -> Dict[str, Any]:
387
  """
388
  Bundle fingerprint + attestation + lineage + risk profile
 
400
  model_version=model_version,
401
  data_tags=data_tags,
402
  notes=notes,
403
+ provider=provider,
404
+ model_family=model_family,
405
  )
406
 
407
  risk = compute_risk_profile(data_tags=data_tags, deployment_env=deployment_env)
408
 
409
  issued_at = att["issued_at"]
 
410
 
411
  cert: Dict[str, Any] = {
412
  "engine": ENGINE_NAME,
 
420
  "threat_flags": risk["threat_flags"],
421
  "environment": deployment_env or "unspecified",
422
  "audit_id": str(uuid.uuid4()),
 
 
 
 
423
  }
424
 
425
+ with open(output, "w", encoding="utf-8") as f:
426
  json.dump(cert, f, indent=4)
427
 
428
  return cert
429
 
430
 
431
+ # ---------------------------------------------------------------------
432
+ # Centralised Audit Log Layer (JSONL + CSV/JSON export)
433
+ # ---------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
 
 
435
 
436
+ def _ensure_audit_log_dir(path: str) -> None:
437
+ directory = os.path.dirname(path)
438
+ if directory and not os.path.exists(directory):
439
+ os.makedirs(directory, exist_ok=True)
440
 
 
 
 
441
 
442
+ def write_audit_event(
443
+ event_type: str,
444
+ engine: str,
445
+ fingerprint: str,
446
+ parent_model: Optional[str],
447
+ model_version: str,
448
+ data_tags: Optional[List[str]],
449
+ risk_level: Optional[str],
450
+ risk_score: Optional[int],
451
+ deployment_env: Optional[str],
452
+ extra: Optional[Dict[str, Any]] = None,
453
+ path: str = AUDIT_LOG_FILE,
454
+ ) -> Dict[str, Any]:
455
  """
456
+ Append a single audit event as JSON line into the centralised audit journal.
457
+ Designed to be reusable / exportable for G-Cloud / NCSC type evidence.
 
458
  """
459
+ _ensure_audit_log_dir(path)
460
 
461
+ entry = {
462
+ "event_id": str(uuid.uuid4()),
463
+ "event_type": event_type,
464
+ "engine": engine,
465
+ "fingerprint": fingerprint,
466
+ "parent_model": parent_model,
467
+ "model_version": model_version,
468
+ "data_tags": data_tags or [],
469
+ "risk_level": risk_level,
470
+ "risk_score": risk_score,
471
+ "deployment_env": deployment_env,
472
+ "timestamp": int(time.time()),
473
+ "extra": extra or {},
474
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
 
476
+ with open(path, "a", encoding="utf-8") as f:
477
+ f.write(json.dumps(entry) + "\n")
 
478
 
479
+ return entry
480
 
481
 
482
+ def export_audit_log_json(path: str = AUDIT_LOG_FILE) -> List[Dict[str, Any]]:
483
  """
484
  Load the audit log as a list of JSON objects.
485
  """
486
  if not os.path.exists(path):
487
  return []
488
 
489
+ entries: List[Dict[str, Any]] = []
490
  with open(path, "r", encoding="utf-8") as f:
491
  for line in f:
492
  line = line.strip()
 
510
  if not entries:
511
  return ""
512
 
 
513
  headers = list(entries[0].keys())
514
+ lines: List[str] = []
515
 
516
  # header row
517
  lines.append(",".join(headers))
518
 
519
  # data rows
520
  for row in entries:
521
+ row_values: List[str] = []
522
  for h in headers:
523
  value = row.get(h, "")
524
  if isinstance(value, list):
 
530
 
531
  return "\n".join(lines)
532
 
533
+
534
+ # ---------------------------------------------------------------------
535
+ # Higher-level helper: log model execution with policy & efficiency info
536
+ # ---------------------------------------------------------------------
537
+
538
+
539
+ def log_model_execution(
540
+ model_name: str,
541
+ provider: Optional[str],
542
+ deployment_env: str,
543
+ data_tags: Optional[List[str]],
544
+ output_text: str,
545
+ tokens: int,
546
+ parent_model: Optional[str] = None,
547
+ model_version: str = "1.0",
548
+ active_policy_ids: Optional[List[str]] = None,
549
+ ) -> Dict[str, Any]:
550
+ """
551
+ Convenience helper that:
552
+ - computes risk profile
553
+ - evaluates policy engine
554
+ - estimates compute efficiency
555
+ - writes a single unified audit event
556
+ """
557
+ # risk
558
+ risk = compute_risk_profile(data_tags=data_tags, deployment_env=deployment_env)
559
+
560
+ # policy
561
+ pe = PolicyEngine()
562
+ policy_result = pe.evaluate_output(output_text, active_policy_ids=active_policy_ids)
563
+
564
+ # efficiency
565
+ efficiency = estimate_compute_cost(
566
+ model_name=model_name,
567
+ tokens=tokens,
568
+ provider=provider,
569
+ )
570
+
571
+ extra = {
572
+ "model_name": model_name,
573
+ "provider": provider,
574
+ "policy_result": policy_result,
575
+ "efficiency": efficiency,
576
+ }
577
+
578
+ entry = write_audit_event(
579
+ event_type="model_execution",
580
+ engine=ENGINE_NAME,
581
+ fingerprint=efficiency["family"], # simple placeholder binding
582
+ parent_model=parent_model,
583
+ model_version=model_version,
584
+ data_tags=data_tags,
585
+ risk_level=risk["level"],
586
+ risk_score=risk["score"],
587
+ deployment_env=deployment_env,
588
+ extra=extra,
589
+ )
590
+
591
+ return entry
592
+