Clarion is an AI-powered litigation tool. It ingests case evidence such as PDFs, audio, images, and video, then:
- Indexes facts - Builds citation-backed evidence references for specific claims.
- Finds contradictions - Flags conflicts between sources like witness statements and official records.
- Generates reports - Produces courtroom-ready reports that can include AI-generated images and reconstructions.
You create a case, upload evidence, analyze it, and generate a report through the API or the deployed web app. Clarion also includes a voice workflow for asking case questions and editing reports by speech.
cd backend
pip install -r requirements.txt
cp .env.example .env
PYTHONPATH=. uvicorn app.main:app --reload- API:
http://127.0.0.1:8000 - Docs:
http://127.0.0.1:8000/docs
Clarion currently runs as a small multi-service system on Google Cloud Run.
Core runtime pieces:
clarion-experience- Next.js frontend for case intake, report viewing, and editing.clarion-api- Public FastAPI service. Handles uploads, case APIs, report/job APIs, exports, and voice endpoints.clarion-intelligence-worker- Private FastAPI worker service. Runs long-lived report generation and case analysis work.
Supporting infrastructure:
- Cloud Tasks - Queues report and analysis work for the worker service.
- Firestore - Persists case state, report jobs, workflow progress, and analysis metadata.
- GCS - Stores uploads, generated reports, media assets, and manifests.
- Signed URL delivery - The API converts private GCS artifact URIs into short-lived browser-safe URLs.
- Optional reconstruction job path - Reconstruction still has a separate Cloud Run Job path available when needed.
The backend codebase is deployed in different service modes:
CLARION_SERVICE_MODE=apiforclarion-apiCLARION_SERVICE_MODE=workerforclarion-intelligence-worker
In API mode, the app exposes public routes like /cases, /generate, /upload, and /voice. In worker mode, it exposes only authenticated internal routes under /internal/*.
- Backend: Python, FastAPI, Pydantic
- Frontend: Next.js
- AI: Google Gemini, Imagen, and Veo
- Storage: Firestore and GCS
- Execution: Cloud Tasks dispatches a warm Cloud Run worker service for report and analysis; reconstruction remains separate
The active backend deployment is now two Cloud Run services, not one service plus report/analysis jobs:
clarion-api- public backend serviceclarion-intelligence-worker- private backend worker service
Live services currently deployed in us-central1:
clarion-apiclarion-intelligence-workerclarion-experience
Cloud Run Jobs may still exist in the project:
clarion-report-workerclarion-analysis-workerclarion-reconstruction-worker
Only the reconstruction job is still aligned with the active design. Report and analysis are now intended to run through the warm worker service.
The repo already includes separate env files for the two backend services:
- backend/cloudrun.env.yaml - API service config
- backend/cloudrun.worker.env.yaml - worker service config
Important settings:
CLARION_SERVICE_MODE=apion the public APICLARION_SERVICE_MODE=workeron the worker serviceINTELLIGENCE_WORKER_BASE_URLon the API must point to the deployed worker service URLINTELLIGENCE_WORKER_AUDIENCEshould usually match that same worker URL
Clarion currently uses these queues:
clarion-report-jobsclarion-analysis-jobsclarion-reconstruction-jobs
The main async path is:
- Report tasks call
POST /internal/report-jobs/{job_id}onclarion-intelligence-worker - Analysis tasks call
POST /internal/case-analysis/{case_id}onclarion-intelligence-worker
Cloud Tasks should send OIDC tokens using the task-runner service account, and the worker service should grant that principal roles/run.invoker.
Deploy the worker service first:
cd backend
gcloud run deploy clarion-intelligence-worker \
--source=. \
--region=us-central1 \
--service-account=clarion-runtime@YOUR_PROJECT.iam.gserviceaccount.com \
--no-allow-unauthenticated \
--concurrency=1 \
--min-instances=1 \
--timeout=1800 \
--env-vars-file=cloudrun.worker.env.yamlThen put the worker URL into INTELLIGENCE_WORKER_BASE_URL and INTELLIGENCE_WORKER_AUDIENCE inside backend/cloudrun.env.yaml, and deploy the API:
gcloud run deploy clarion-api \
--source=. \
--region=us-central1 \
--service-account=clarion-runtime@YOUR_PROJECT.iam.gserviceaccount.com \
--allow-unauthenticated \
--min-instances=1 \
--env-vars-file=cloudrun.env.yamlThe API and worker use the same source tree; only the env file and service mode differ.
flowchart TD
U["User / Experience UI"] --> API["clarion-api<br/>POST /cases/{caseId}/report-jobs"]
API --> STORE["ReportJobStore<br/>create queued job + save request"]
API --> TASKS["Cloud Tasks<br/>enqueue report task"]
TASKS --> WORKER["clarion-intelligence-worker<br/>POST /internal/report-jobs/{job_id}"]
WORKER --> ORCH["ReportGenerationOrchestrator.run_job(...)"]
ORCH --> STORE
ORCH --> ADKRT["AdkReportingPipeline.run(...)"]
subgraph ADK["ADK + Gemini reporting workflow"]
PLANNER["TimelinePlannerAgent<br/>Gemini text model"]
GREVIEW["GroundingReviewerAgent<br/>Gemini text model"]
GREFINE["TimelineRefinerAgent<br/>Gemini text model"]
CTX["ContextEnrichmentAgent<br/>Gemini helper + Google Search"]
MEDIA["MediaPlannerAgent<br/>Gemini helper"]
COMPOSER["FinalComposerAgent<br/>Gemini text model"]
CREVIEW["CompositionReviewerAgent<br/>Gemini helper"]
CREFINE["CompositionRefinerAgent<br/>Gemini text model"]
RESULT["PipelineResult<br/>blocks + image_requests + reconstruction_requests"]
end
ADKRT --> PLANNER
PLANNER --> GREVIEW
GREVIEW -- "issues found" --> GREFINE
GREFINE --> GREVIEW
GREVIEW -- "approved" --> CTX
GREVIEW -- "approved" --> MEDIA
CTX --> COMPOSER
MEDIA --> COMPOSER
COMPOSER --> CREVIEW
CREVIEW -- "issues found" --> CREFINE
CREFINE --> CREVIEW
CREVIEW -- "approved" --> RESULT
ADKRT -- "ADK failure" --> FALLBACK["HeuristicReportingPipeline"]
FALLBACK --> RESULT
RESULT --> REPORT["create_initial_report(...)<br/>text blocks + media placeholders"]
subgraph MEDIAEXEC["Media execution"]
IMG["GeminiImageGenerator<br/>Imagen"]
RECON["ReconstructionMediaService<br/>Veo"]
IMGASSET["image asset + manifest"]
VIDASSET["video asset + manifest"]
end
RESULT --> IMG
RESULT --> RECON
IMG --> IMGASSET
RECON --> VIDASSET
IMGASSET --> ATTACH["attach_media_asset(...)"]
VIDASSET --> ATTACH
ATTACH --> FINAL["finalize_report(...)<br/>persist report.json + manifest"]
FINAL --> STORE
STORE --> STATUS["GET /generate/jobs/{job_id}<br/>SSE + polling"]
STATUS --> U
Cloud Run serves report and reconstruction artifacts from a private GCS bucket by generating V4 signed URLs at request time.
- Set
SIGNED_URL_SERVICE_ACCOUNT_EMAILto the service account that should sign artifact URLs. - Enable
iamcredentials.googleapis.comin the same project as the signer. - Grant the API runtime service account
roles/iam.serviceAccountTokenCreatoronSIGNED_URL_SERVICE_ACCOUNT_EMAIL. - Keep the bucket private. Clarion expects signed URLs instead of public
storage.googleapis.comlinks.
Post-deploy validation:
- Submit a reconstruction or report job until it reaches
completed. - Call the polling or report endpoint and confirm the returned artifact URL is HTTPS and includes
X-Goog-Algorithm,X-Goog-Credential, andX-Goog-Signature. - Fetch that URL from your browser or
curloutside GCP and confirm the object loads without making the bucket public.
- The checked-in Cloud Run env files currently contain a real Google API key value. That should be rotated and ideally moved into Secret Manager.
- For local configuration, start from backend/.env.example.
- For deeper schema details, see
backend/app/models/schema.pyandbackend/app/models/report_schema.py.