Skip to content

About

jeremiah.dev: A developer portfolio. This Next.js site brings my career, education, projects, and technical perspective into one responsive experience, using server rendering, Sanity-managed content, strong SEO, and an integrated WebGL AI guide synthesized with my own voice.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

This repository is now a pnpm workspace monorepo.

Layout

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.

Getting Started

Install dependencies:

pnpm dev

Run the current site:

pnpm dev

Open 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 lint

Portfolio content

Sanity 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.

AI app setup

The portfolio assistant lives in apps/ai.

Local setup:

cp apps/ai/.env.example apps/ai/.env.local
pnpm dev:ai

Required 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:

  1. The client submits the current message, recent validated history, and a Turnstile token to app/api/assistant/respond/route.ts.
  2. The route validates the body, applies rate limiting, and verifies the Turnstile token with Cloudflare.
  3. Only after Turnstile succeeds does the route call the isolated OpenAI service.
  4. 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.
  5. The assistant response route signs the exact assistant text with ASSISTANT_SPEECH_SIGNING_SECRET and returns that speechToken alongside the text response.
  6. The client renders the assistant text immediately and requests speech only when the visitor presses the voice control.
  7. The protected app/api/assistant/speech/route.ts verifies 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_KEY is the standard variable name, but the current implementation also accepts ELEVEN_LABS_KEY as a local compatibility fallback.
  • ELEVENLABS_VOICE_ID must be set to your cloned voice ID from ElevenLabs.
  • ELEVENLABS_MODEL_ID defaults to eleven_multilingual_v2.
  • ELEVENLABS_OUTPUT_FORMAT defaults to mp3_44100_128.
  • Voice defaults live in apps/ai/lib/speech/config.ts.
  • To disable TTS locally, leave ELEVENLABS_VOICE_ID or the API key unset.
  • The current tests mock provider requests, so pnpm --filter @jeremiah-dev/ai test does 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.

About

jeremiah.dev: A developer portfolio. This Next.js site brings my career, education, projects, and technical perspective into one responsive experience, using server rendering, Sanity-managed content, strong SEO, and an integrated WebGL AI guide synthesized with my own voice.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages