This is the web application for Cofacts.ai, a chat-based AI assistant for collaborative fact-checking. It provides a modern TanStack Start application with an integrated multi-agent system powered by Google's ADK that can research claims, verify sources, and help compose fact-check replies.
- Node.js 18+
- Python 3.12+
- Google Cloud CLI (
gcloud) with application default credentials - pnpm
Note: This repository includes a pnpm-lock.yaml file. Please ensure you have pnpm installed.
- Install Node.js dependencies:
pnpm installNote: This will not install Python dependencies. You must run the next step manually.
- Install Python dependencies for the ADK agent:
pnpm install:agentNote: This will automatically setup a
.venv(virtual environment) inside theadkdirectory.To activate the virtual environment manually, you can run:
source adk/.venv/bin/activate
- Authenticate with Google Cloud for Vertex AI:
gcloud auth application-default login- Set up environment variables:
For the ADK backend agent:
cp adk/cofacts_ai/.env.example adk/cofacts_ai/.envEdit adk/cofacts_ai/.env and fill in the required values.
For the frontend UI (Vite / TanStack Start):
cp .env.example .envEdit .env to configure your browser-safe variables (e.g. Langfuse keys).
- Start the development server:
pnpm devThis will start both the UI and agent servers concurrently:
- http://localhost:3000 for UI
- http://localhost:8000 for ADK web
- http://localhost:8000/docs for ADK API docs
This project uses GitHub Actions for automated deployments to Google Cloud Run.
The agent talks to Gemini through Vertex AI and feeds it Cofacts media as
gs:// URIs, which involves two separate identities and two separate
permissions. Both must be granted on the project set via GC_PROJECT_ID:
-
Calling the model — the Cloud Run runtime service account (
SERVICE_ACCOUNT_EMAIL) needs Vertex AI User (roles/aiplatform.user, which includesaiplatform.endpoints.predict):gcloud projects add-iam-policy-binding "$GC_PROJECT_ID" \ --member="serviceAccount:<SERVICE_ACCOUNT_EMAIL>" \ --role="roles/aiplatform.user"
Missing this surfaces as
403 PERMISSION_DENIEDonaiplatform.endpoints.predictfor the model resource. -
Reading the media — when Gemini fetches a media object from GCS, it uses the Vertex AI service agent (
service-<PROJECT_NUMBER>@gcp-sa-aiplatform.iam.gserviceaccount.com), not the runtime service account. That agent needs read access to the media bucket:gcloud storage buckets add-iam-policy-binding gs://<MEDIA_BUCKET> \ --member="serviceAccount:service-<PROJECT_NUMBER>@gcp-sa-aiplatform.iam.gserviceaccount.com" \ --role="roles/storage.objectViewer"
Missing this surfaces as a
storage.objects.getdenial on the bucket object (note: it's a cross-project grant if the bucket lives in another project).
To build and test both Docker images locally (mirroring what the CI pipeline does):
-
Make sure
adk/cofacts_ai/.envexists and is filled in (see Getting Started step 3). -
Build and start the containers:
docker compose up --buildThis will:
- Build the frontend image from the root
Dockerfile - Build the backend image from
adk/Dockerfile - Start both containers, with the frontend waiting for the backend to be healthy
- Open http://localhost:3000 to test the application.
To stop:
docker compose down- Trigger: Every push or merge to the
masterbranch. - URL: cofacts-ai-236494820908.asia-east1.run.app .
- Traffic: The
masterversion always receives 100% of the traffic.
- Trigger: Every Pull Request (opened or updated).
- Behavior: A dedicated revision is created for each PR with a unique tag.
- URL: You can find the preview URL in the GitHub PR comments or the "Deployments" section of the PR sidebar.
- Isolation: Each PR has its own isolated preview environment, and deployments to
masterwill not interrupt existing PR previews.
The following scripts can also be run using pnpm:
dev- Starts both UI and agent servers in development modedev:ui- Starts only the UI server (Vite)dev:agent- Starts only the ADK agent serverbuild- Builds the application for productionpreview- Previews the production buildtest- Runs unit tests using Vitestlint- Runs ESLint for code lintingformat- Formats code using Prettiercheck- Runs both Prettier and ESLint (format and lint)install:agent- Installs Python dependencies for the agent usinguvcodegen- Regeneratessrc/server/gql/from the rumors-api GraphQL schema. Run after editing anygraphql(\...`)` document so the generated types stay in sync.
Server-side GraphQL queries are typed via @graphql-codegen/client-preset. Define queries inline with the graphql tag from @/server/gql and dispatch them through cofactsExec (see src/lib/cofactsExec.ts):
import { graphql } from '@/server/gql'
import { cofactsExec } from '@/lib/cofactsExec'
const GetSomethingDocument = graphql(`
query GetSomething { ... }
`)
const data = await cofactsExec(GetSomethingDocument)The schema source is https://api.cofacts.tw/graphql (see codegen.ts). The generated src/server/gql/ directory is committed.
- ADK Documentation - Learn more about the ADK and its features
- TanStack Start Documentation - Learn about TanStack Start features and API
- TanStack Query Documentation - Learn about TanStack Query for data fetching
Feel free to submit issues and enhancement requests!
This project is licensed under the MIT License - see the LICENSE file for details.
If you see "I'm having trouble connecting to my tools", make sure:
- The ADK agent is running on port 8000
- You have run
gcloud auth application-default login - Both servers started successfully
If you encounter Python import errors:
cd adk
uv sync