Skip to content

Latest commit

 

History

History
112 lines (89 loc) · 5.53 KB

File metadata and controls

112 lines (89 loc) · 5.53 KB

Ravendr

Voice-first research demo. Click the mic, say a topic, get a cited briefing back.
Every step is a Render Workflow task.

Deploy to Render

Node 22+ TypeScript strict MIT

Stack

Platform Job
Render Workflows Orchestrator. voiceSession task owns the session; plan_queries → search_branch × N → synthesize are independently-retried subtasks.
AssemblyAI Voice Agent STT + VAD + LLM + TTS in one WebSocket. Lives inside the voiceSession task.
Mastra Agent primitive. Plans queries and writes the briefing using Anthropic Sonnet 4 (see ANTHROPIC_MODEL in src/config.ts).
You.com Research One call per planned angle, fanned out in parallel.

Architecture

Browser ←audio WS→ Web service (broker) ←reverse WS→ voiceSession task ←→ AssemblyAI
                        │                                   │
                   Postgres NOTIFY  ←── phase events ──     │
                        │                                   │
                        ▼ SSE                               ▼ on tool.call "research":
                   Browser activity feed           research subtask
                                                     ├─ plan_queries
                                                     ├─ search_branch × N
                                                     └─ synthesize
  • Click mic → POST /api/start → Render dispatches voiceSession.
  • Task opens AssemblyAI and a reverse WS back to the broker; audio tunnels through.
  • You speak a topic. AssemblyAI fires research(topic). The tool dispatches the research subtask.
  • Each subtask emits a phase event via Postgres NOTIFY → SSE → activity feed.
  • briefing.ready fires; tool.result returns the full briefing; AssemblyAI reads it aloud.

Run locally

cp .env.example .env
createdb ravendr
npm install
npm run migrate
npm run dev           # web service on :3000
npm run dev:tasks     # workflow runner in a second terminal

Required env on both services:

  • DATABASE_URL, ANTHROPIC_API_KEY, YOU_API_KEY, ASSEMBLYAI_API_KEY

Web only: RENDER_API_KEY, WORKFLOW_SLUG (default ravendr-workflow).

Deploy

  1. Fork. Hit Deploy to Render — Blueprint creates ravendr-web + ravendr-db.
  2. In the dashboard, create a Workflow service ravendr-workflow, same repo, start command node dist/render/tasks/index.js.
  3. Put secrets in an env group ravendr-shared so both services share them.
  4. Migrations run on every web deploy (preDeployCommand: npm run migrate).

Repo layout

One folder per vendor — each owns its protocol or SDK; the Render task files are thin orchestration glue that compose them.

src/
  server.ts  routes.ts  config.ts      web service composition root
  assemblyai/
    voice-agent.ts                      AssemblyAI WebSocket protocol client
  mastra/
    agents.ts                           Planner, synthesizer, verifier factories
  youcom/
    research.ts                         You.com Research API adapter
  render/
    db.ts                               typed Postgres queries
    event-bus.ts                        LISTEN/NOTIFY event bus
    session-broker.ts                   pairs /ws/client with /ws/task
    workflow-dispatcher.ts              @renderinc/sdk wrapper
    tasks/
      index.ts                          auto-registers every task with Render
      research.ts                       ROOT orchestrator (pure Render Workflows)
      assemblyai/
        voice-session.ts                holds AssemblyAI WS + reverse WS
      mastra/
        classify-ask.ts                 ask-shape classifier
        plan-queries.ts                 shape-aware planner
        synthesize.ts                   shape-aware synthesizer
        verify.ts                       shape-aware verifier + 1-retry
      youcom/
        search-branch.ts                one You.com call (× N parallel)
  shared/                               ports + events + envelope + errors + logger

static/                                 vanilla ES modules (index.html + main.js + mic.js)

Known limitation

AssemblyAI's Voice Agent doesn't let the server force the agent to speak a specific string. After tool.result the agent's LLM usually reads the briefing aloud, but not always. When it goes silent, the briefing still renders on screen — voice is the soft path, UI is the hard one. For guaranteed single-voice narration of every phase, swap AssemblyAI for OpenAI Realtime (conversation.item.create).

License

MIT