This repository is now a pnpm workspace monorepo.
apps/
ai/ WebGL portfolio chat assistant
portfolio/ Current jeremiah.dev Next.js app
packages/ Shared packages and shared data for workspace apps
The existing site lives in apps/portfolio, and the repository root now owns the workspace-level scripts.
Install dependencies:
pnpm devRun the current site:
pnpm devOpen http://localhost:3000 to view the app.
Useful workspace commands:
pnpm dev:ai
pnpm dev:portfolio
pnpm build:ai
pnpm build
pnpm lint:ai
pnpm build:portfolio
pnpm lintSanity owns the portfolio's site text, academics, career roles, and skills. Edit them in Studio; there are no local content defaults or reseeding scripts.
Configure apps/portfolio/.env.local before running the site:
NEXT_PUBLIC_SANITY_PROJECT_ID=qnk05guu
NEXT_PUBLIC_SANITY_DATASET=production
SANITY_API_READ_TOKEN=<server-side read token>The career and academic records require authenticated reads. Local development can also use the existing LOCAL_SANITY_API_EDIT_TOKEN; tokens stay server-side. Missing configuration or failed content requests surface errors instead of substituting local copy. The local site reads production content, so Studio edits affect the live dataset.
The portfolio assistant lives in apps/ai.
Local setup:
cp apps/ai/.env.example apps/ai/.env.local
pnpm dev:aiRequired environment variables for apps/ai:
NEXT_PUBLIC_TURNSTILE_SITE_KEY=
CLOUDFLARE_TURNSTILE_SECRET_KEY=
OPENAI_API_KEY=
OPENAI_MODEL=
ASSISTANT_SPEECH_SIGNING_SECRET=
ELEVENLABS_API_KEY=
ELEVENLABS_VOICE_ID=
ELEVENLABS_MODEL_ID=
ELEVENLABS_OUTPUT_FORMAT=Key assistant files:
- System prompt:
apps/ai/lib/prompt/system-prompt.ts - Approved Jeremiah profile context loader:
apps/ai/lib/context/profile.ts - Approved Jeremiah profile context data:
packages/profile-data - Approved Jeremiah context tool:
apps/ai/lib/context/context-tools.ts - Speech pipeline helpers:
apps/ai/lib/speech - Assistant response API route:
apps/ai/app/api/assistant/respond/route.ts - Conversation request processing:
apps/ai/lib/conversation - OpenAI service:
apps/ai/lib/openai/service.ts - Turnstile verification utility:
apps/ai/lib/turnstile/verify.ts - Speech normalization:
apps/ai/lib/speech/normalization.ts - Speech authorization:
apps/ai/lib/speech/authorization.ts - Text-to-speech service:
apps/ai/lib/assistant/text-to-speech.ts - TTS API route:
apps/ai/app/api/assistant/speech/route.ts
Request flow:
- The client submits the current message, recent validated history, and a Turnstile token to
app/api/assistant/respond/route.ts. - The route validates the body, applies rate limiting, and verifies the Turnstile token with Cloudflare.
- Only after Turnstile succeeds does the route call the isolated OpenAI service.
- The OpenAI service builds the assistant instructions from the dedicated system prompt and approved Jeremiah profile context, then calls the Responses API with
OPENAI_MODEL. - The assistant response route signs the exact assistant text with
ASSISTANT_SPEECH_SIGNING_SECRETand returns thatspeechTokenalongside the text response. - The client renders the assistant text immediately and requests speech only when the visitor presses the voice control.
- The protected
app/api/assistant/speech/route.tsverifies the signed token, normalizes the assistant text for speech, applies TTS-specific rate limiting, and then streams MP3 audio from ElevenLabs.
TTS notes:
- Arbitrary visitor text is not synthesized. The speech route accepts only assistant text paired with a valid signed token generated by the assistant response route.
ELEVENLABS_API_KEYis the standard variable name, but the current implementation also acceptsELEVEN_LABS_KEYas a local compatibility fallback.ELEVENLABS_VOICE_IDmust be set to your cloned voice ID from ElevenLabs.ELEVENLABS_MODEL_IDdefaults toeleven_multilingual_v2.ELEVENLABS_OUTPUT_FORMATdefaults tomp3_44100_128.- Voice defaults live in
apps/ai/lib/speech/config.ts. - To disable TTS locally, leave
ELEVENLABS_VOICE_IDor the API key unset. - The current tests mock provider requests, so
pnpm --filter @jeremiah-dev/ai testdoes not spend ElevenLabs credits.
You can start editing the portfolio site by modifying apps/portfolio/app/page.tsx, or the AI assistant by modifying apps/ai/app/page.tsx.
This project uses next/font to automatically optimize and load Inter, a custom Google Font.