-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.example
More file actions
120 lines (112 loc) · 8.31 KB
/
Copy path.env.example
File metadata and controls
120 lines (112 loc) · 8.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# ---- Google Cloud ----
GOOGLE_CLOUD_PROJECT=your-project
GOOGLE_CLOUD_LOCATION=us-central1
GOOGLE_GENAI_USE_VERTEXAI=true
ARTIFACT_BUCKET=gs://your-dry-lab-artifacts
# Immutable provenance store (generation + crc32c pinned objects); see provenance.py.
DRYLAB_PROVENANCE_BUCKET=your-drylab-provenance-bucket
BQ_DATASET=dry_lab
# ---- Models ----
ORCHESTRATOR_MODEL=gemini-2.5-pro
ANALYST_MODEL=gemini-2.5-pro
SCOUT_MODEL=gemini-2.5-pro
SCRIBE_MODEL=gemini-2.5-flash
CRITIC_MODEL=gemini-2.5-pro
# ---- Fivetran (Fivetran-track) ----
FIVETRAN_API_KEY=
FIVETRAN_API_SECRET=
FIVETRAN_CONNECTION_ID=
FIVETRAN_SAMPLES_CONNECTION_ID=
FIVETRAN_TRIALS_CONNECTION_ID=
FIVETRAN_SURVIVAL_CONNECTION_ID=
# After: python scripts/fivetran_setup.py create-dose-response-connector && gsutil cp data/dose_response/*.csv gs://<spine-bucket>/dose_response/
FIVETRAN_DOSERESPONSE_CONNECTION_ID=
# GDSC dose-response source (scripts/build_doseresponse_source.py)
GDSC_RELEASE_DATE=2023-10-27
# TCGA BRCA survival source (scripts/build_survival_source.py)
CBIOPORTAL_STUDY_ID=brca_tcga
SURVIVAL_MARKER_GENE=ERBB2
FIVETRAN_ALLOW_WRITES=true
# AACT monthly snapshot pinned for clinical-trial-landscaping (scripts/build_trials_source.py)
AACT_SNAPSHOT_DATE=20260501
# Optional: a registered Fivetran transformation (dbt-on-Fivetran) the agent may TRIGGER under human approval
# (ingest -> transform -> activate). If unset, the run_transformation tool is NOT exposed (the agent never fakes
# a transform); the read-only list_transformations stays available.
FIVETRAN_TRANSFORMATION_ID=
# ---- Loop OUTPUT EDGE (ingest -> transform -> ACTIVATE; CLAUDE.md #4) ----
# The human-approved ExperimentRequest is emitted in server.emit_experiment, AFTER the approval card is approved
# (never auto-fired). Three honest tiers, first one configured wins; the UI states exactly which fired:
#
# TIER 1 — a real Fivetran Activation (reverse-ETL). Fivetran Activations is powered by Census, so the trigger
# is the Census Management API (Bearer token @ app.getcensus.com), NOT the api.fivetran.com key:secret REST.
# Set it up once in Fivetran/Census: create an Activation whose SOURCE query reads the approved requests from
# our BigQuery index, e.g. SELECT run_id, goal, experiment_request FROM `<proj>.dry_lab.run_status`
# WHERE lab_decision = 'approved' -> map fields to a destination (Google Sheets / Slack / a mock ELN). Then
# copy the sync id from its config page into FIVETRAN_ACTIVATION_ID and a workspace/personal access token into
# FIVETRAN_ACTIVATION_TOKEN. On approval we POST {base}/api/v1/syncs/{id}/trigger (empty body) -> the Activation
# reverse-ETLs the approved row from BigQuery to the destination. (The trigger sends NO payload — the data
# travels via the warehouse, which is the whole point of reverse-ETL.) Off the per-run critical path.
FIVETRAN_ACTIVATION_ID=
FIVETRAN_ACTIVATION_TOKEN=
FIVETRAN_ACTIVATION_BASE_URL=https://app.getcensus.com # EU workspaces: https://app-eu.getcensus.com
FIVETRAN_ACTIVATION_TARGET= # optional human label shown in the UI, e.g. "Google Sheets · Experiment queue"
#
# TIER 2 — a real webhook (no Activation configured): POST the ExperimentRequest JSON to this URL (Slack
# incoming webhook, an Apps Script, a mock ELN, webhook.site, ...). Real emit, end-to-end testable today.
LAB_ENDPOINT_URL=
#
# TIER 3 — neither set: a clearly-labelled mock acceptance so the loop still closes for the demo (mocked:true).
# ---- Sessions (durable conversation/session store) — Phase D ----
# DEFAULT (unset): a local sqlite file (backend/.adk/sessions.db) via ADK's SqliteSessionService — fine for local
# dev and a single Cloud Run instance, but INSTANCE-LOCAL: it does NOT survive instance replacement or scale to >1
# instance. The durable, cross-instance record for PAST runs is the BigQuery runs/run_status index + the GCS
# provenance pins (powers /runs and read-only "Open"); durable LIVE-session continuity is the Cloud SQL upgrade:
#
# PROD UPGRADE (durable cross-instance live sessions): Cloud SQL Postgres over the Cloud Run unix socket, via the
# ASYNC asyncpg driver. ADK's DatabaseSessionService builds a SQLAlchemy ASYNC engine (create_async_engine), so the
# driver MUST be async — asyncpg, NOT pg8000 (pg8000 is sync and is rejected). asyncpg takes the socket DIRECTORY in
# `host=` (it appends /.s.PGSQL.5432 itself); this differs from pg8000's `unix_sock=` full path. Steps:
# 1. Create a Cloud SQL Postgres instance + database + user; store the password in Secret Manager.
# gcloud sql instances create dry-lab-sessions --database-version=POSTGRES_16 --tier=db-f1-micro \
# --edition=ENTERPRISE --region=$GOOGLE_CLOUD_LOCATION --project=$GOOGLE_CLOUD_PROJECT (~$8-11/mo)
# gcloud sql databases create drylab --instance=dry-lab-sessions
# gcloud sql users create drylab --instance=dry-lab-sessions --password=... (store in Secret Manager)
# 2. SESSION_SERVICE_URI=postgresql+asyncpg://USER:PASSWORD@/DBNAME?host=/cloudsql/PROJECT:REGION:INSTANCE
# (empty host before '/', DB name as the path, the socket DIR in host=; URL-encode special chars in PASSWORD)
# 3. PROD (Cloud Run): do NOT put the inline-password URI in env or any tracked file. Store the FULL URI in
# Secret Manager (secret `drylab-session-uri`); deployment/deploy.py injects it as SESSION_SERVICE_URI via
# --set-secrets and grants the runtime SA cloudsql.client + secretmanager.secretAccessor. You only set
# CLOUD_SQL_INSTANCE=PROJECT:REGION:INSTANCE (non-secret) here. asyncpg/sqlalchemy[asyncio] are already deps.
# Create the secret once: printf '%s' "$URI" | gcloud secrets create drylab-session-uri --data-file=-
# LOCAL dev against Cloud SQL (optional): run the Cloud SQL Auth Proxy and set SESSION_SERVICE_URI in your
# gitignored .env to postgresql+asyncpg://USER:PWD@localhost:5432/DBNAME (the proxy serves plaintext locally).
# SESSION_SERVICE_URI=
# CLOUD_SQL_INSTANCE=
# ARTIFACT_SERVICE_URI=gs://your-dry-lab-artifacts
# ---- Light per-user identity (Phase D) ----
# A signed (tamper-evident), HttpOnly cookie mints a stable PSEUDONYMOUS per-browser id so "my runs"/sessions/
# artifacts are per-user, not the shared "user". This is NOT login/real identity (CLAUDE.md lists IAP auth as a
# later horizon; the spoofable X-Goog-Authenticated-User-* header is deliberately not trusted). Set a STABLE secret
# (prod: source from Secret Manager) — if it rotates, all existing cookies fail to verify and every visitor becomes
# a new user. Unset uses a fixed dev-only fallback (local only; never ship that).
# DRYLAB_SESSION_SECRET=
# ---- Cross-run memory (Vertex AI Memory Bank) — Phase D hook, GATED OFF by default ----
# UNSET = NO memory: ADK uses an ephemeral in-process store and the agent claims nothing (preload tool not attached;
# add_session_to_memory not called). To make it REAL, provision a Vertex AI Agent Engine instance and set the id:
# import vertexai; c = vertexai.Client(project=..., location=...); ae = c.agent_engines.create()
# # ae.api_resource.name -> projects/.../locations/.../reasoningEngines/<ID> (no agent code is deployed)
# MEMORY_SERVICE_URI=agentengine://<ID>
# Then PreloadMemoryTool recalls a returning user's cohort/findings into the planning turn, and finalize_run stores
# the session (both off the critical path). Incurs Vertex generation+embedding token cost per run; leave OFF unless wanted.
# MEMORY_SERVICE_URI=
# ---- Sandbox resource caps (PHASE B hardening: bound a bring-your-own user skill to its own kernel) ----
# The @Investigator kernel runs user (and vetted) skill code with NO network and NO creds; these POSIX rlimits
# additionally bound CPU/memory/disk so a skill that slips past the static linter can only abuse its OWN ephemeral
# sandbox, never the host. 0 = skip that cap. Defaults are safe; tune only if needed.
# CODE_MEM_LIMIT_MB=4096 # RLIMIT_AS — keep GENEROUS (numpy/pandas reserve large virtual memory). Linux-enforced.
# CODE_FSIZE_LIMIT_MB=512 # RLIMIT_FSIZE — per-file write cap (disk-fill guard).
# CODE_CPU_LIMIT_S=0 # RLIMIT_CPU seconds; 0 ⇒ derive from CODE_TIMEOUT_S.
# CODE_NPROC_LIMIT=0 # RLIMIT_NPROC fork backstop. 0 = OFF (recommended). PER-UID: only set on a
# # single-tenant instance (Cloud Run --concurrency=1) or it can starve the server itself.
# ---- Coding-agent docs grounding ----
CONTEXT7_API_KEY=