feat: enhance provenance with infrastructure, actor ID, and commit tracking
- Add infrastructure field to RunStatus model - Store infrastructure (software/hardware) from task result - Format username as ActivityPub actor ID (@user@domain) - Display owner, effects commit, and infrastructure in UI provenance section - Add artdag commit tracking for identity effect - Include infrastructure in raw JSON provenance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
39
server.py
39
server.py
@@ -30,6 +30,7 @@ from tasks import render_effect
|
||||
|
||||
# L2 server for auth verification
|
||||
L2_SERVER = os.environ.get("L2_SERVER", "http://localhost:8200")
|
||||
L2_DOMAIN = os.environ.get("L2_DOMAIN", "artdag.rose-ash.com")
|
||||
|
||||
# Cache directory (use /data/cache in Docker, ~/.artdag/cache locally)
|
||||
CACHE_DIR = Path(os.environ.get("CACHE_DIR", str(Path.home() / ".artdag" / "cache")))
|
||||
@@ -96,7 +97,8 @@ class RunStatus(BaseModel):
|
||||
celery_task_id: Optional[str] = None
|
||||
effects_commit: Optional[str] = None
|
||||
effect_url: Optional[str] = None # URL to effect source code
|
||||
username: Optional[str] = None # Owner of the run
|
||||
username: Optional[str] = None # Owner of the run (ActivityPub actor ID)
|
||||
infrastructure: Optional[dict] = None # Hardware/software used for rendering
|
||||
|
||||
|
||||
# ============ Auth ============
|
||||
@@ -274,6 +276,9 @@ async def create_run(request: RunRequest, username: str = Depends(get_required_u
|
||||
# Generate output name if not provided
|
||||
output_name = request.output_name or f"{request.recipe}-{run_id[:8]}"
|
||||
|
||||
# Format username as ActivityPub actor ID
|
||||
actor_id = f"@{username}@{L2_DOMAIN}"
|
||||
|
||||
# Create run record
|
||||
run = RunStatus(
|
||||
run_id=run_id,
|
||||
@@ -282,7 +287,7 @@ async def create_run(request: RunRequest, username: str = Depends(get_required_u
|
||||
inputs=request.inputs,
|
||||
output_name=output_name,
|
||||
created_at=datetime.now(timezone.utc).isoformat(),
|
||||
username=username
|
||||
username=actor_id
|
||||
)
|
||||
|
||||
# Submit to Celery
|
||||
@@ -324,6 +329,9 @@ async def get_run(run_id: str):
|
||||
run.effects_commit = effects[0].get("repo_commit")
|
||||
run.effect_url = effects[0].get("repo_url")
|
||||
|
||||
# Extract infrastructure info
|
||||
run.infrastructure = result.get("infrastructure")
|
||||
|
||||
# Cache the output
|
||||
output_path = Path(result.get("output", {}).get("local_path", ""))
|
||||
if output_path.exists():
|
||||
@@ -900,6 +908,8 @@ async def ui_detail_page(run_id: str):
|
||||
if effects:
|
||||
run.effects_commit = effects[0].get("repo_commit")
|
||||
run.effect_url = effects[0].get("repo_url")
|
||||
# Extract infrastructure info
|
||||
run.infrastructure = result.get("infrastructure")
|
||||
output_path = Path(result.get("output", {}).get("local_path", ""))
|
||||
if output_path.exists():
|
||||
cache_file(output_path)
|
||||
@@ -979,10 +989,18 @@ async def ui_detail_page(run_id: str):
|
||||
html += f'''
|
||||
<div class="provenance">
|
||||
<h2>Provenance</h2>
|
||||
<div class="prov-item">
|
||||
<div class="prov-label">Owner</div>
|
||||
<div class="prov-value">{run.username or "anonymous"}</div>
|
||||
</div>
|
||||
<div class="prov-item">
|
||||
<div class="prov-label">Effect</div>
|
||||
<div class="prov-value"><a href="{effect_url}" target="_blank">{run.recipe}</a></div>
|
||||
</div>
|
||||
<div class="prov-item">
|
||||
<div class="prov-label">Effects Commit</div>
|
||||
<div class="prov-value">{run.effects_commit or "N/A"}</div>
|
||||
</div>
|
||||
<div class="prov-item">
|
||||
<div class="prov-label">Input(s)</div>
|
||||
<div class="prov-value">
|
||||
@@ -1002,6 +1020,20 @@ async def ui_detail_page(run_id: str):
|
||||
</div>
|
||||
'''
|
||||
|
||||
# Infrastructure section
|
||||
if run.infrastructure:
|
||||
software = run.infrastructure.get("software", {})
|
||||
hardware = run.infrastructure.get("hardware", {})
|
||||
html += f'''
|
||||
<div class="prov-item">
|
||||
<div class="prov-label">Infrastructure</div>
|
||||
<div class="prov-value">
|
||||
Software: {software.get("name", "unknown")} ({software.get("content_hash", "unknown")[:16]}...)<br>
|
||||
Hardware: {hardware.get("name", "unknown")} ({hardware.get("content_hash", "unknown")[:16]}...)
|
||||
</div>
|
||||
</div>
|
||||
'''
|
||||
|
||||
html += f'''
|
||||
<div class="prov-item">
|
||||
<div class="prov-label">Run ID</div>
|
||||
@@ -1042,6 +1074,7 @@ async def ui_detail_page(run_id: str):
|
||||
"created_at": run.created_at,
|
||||
"completed_at": run.completed_at,
|
||||
"username": run.username,
|
||||
"infrastructure": run.infrastructure,
|
||||
"error": run.error
|
||||
}, indent=2)
|
||||
|
||||
@@ -1077,6 +1110,8 @@ async def ui_run_partial(run_id: str):
|
||||
if effects:
|
||||
run.effects_commit = effects[0].get("repo_commit")
|
||||
run.effect_url = effects[0].get("repo_url")
|
||||
# Extract infrastructure info
|
||||
run.infrastructure = result.get("infrastructure")
|
||||
output_path = Path(result.get("output", {}).get("local_path", ""))
|
||||
if output_path.exists():
|
||||
cache_file(output_path)
|
||||
|
||||
Reference in New Issue
Block a user