Skip to content

WhiteRow33/OpenRouter-AI-Chat-Platform

Repository files navigation

OpenRouter AI Chat Platform

Local, front-to-back chat experience that talks to OpenRouter as the model gateway. React + TypeScript on the frontend, Express + TypeScript on the backend, and OpenTelemetry traces flowing to a local Jaeger instance.

Contents

  • Project Introduction
  • Technical Architecture
  • Installation & Setup
  • Running Jaeger
  • OpenTelemetry Tracing
  • Usage Guide
  • Project Structure
  • Developer Experience Notes
  • Bonus Features
  • Conclusion

Project Introduction

  • Local chat UI that sends messages through OpenRouter to any supported model.
  • Features: send and receive messages, view and manage chat history, choose models exposed by the backend, and (optionally) include multimodal uploads when wired in.
  • Ships with sensible defaults: in-memory history, a health endpoint, and friendly fallbacks if a model is temporarily unavailable.

Technical Architecture

  • Backend (apps/backend): Node.js + Express + TypeScript. Routes for chat, model discovery, and history. In-memory session store, graceful shutdown, and a health endpoint at /health.
  • Frontend (apps/frontend): React + TypeScript (Create React App). Chat UI with history sidebar, inline rename, delete confirmation, and auto-scrolling composer.
  • Telemetry: OpenTelemetry NodeSDK with auto-instrumentation (Express + HTTP) plus custom spans for chat processing, OpenRouter calls, history operations, and in-memory storage.
  • Tracing backend: Jaeger (local Docker container) receiving OTLP/HTTP on http://localhost:4318/v1/traces with UI on http://localhost:16686.

Installation & Setup

Prerequisites

  • Node.js 18+ and npm 9+.
  • Docker (for Jaeger).
  • An OpenRouter API key.

Clone the project

git clone https://github.com/<your-org>/OpenRouter-AI-Chat-Platform.git
cd OpenRouter-AI-Chat-Platform

Install dependencies

The repo uses npm workspaces with a shared types package.

npm install          # installs backend, frontend, and shared types

If you prefer installing individually:

cd apps/backend && npm install
cd ../frontend && npm install

Configure environment

Create apps/backend/.env:

OPENROUTER_API_KEY=sk-***your-key***
PORT=3001
APP_URL=http://localhost:3000
JAEGER_ENDPOINT=http://localhost:4318/v1/traces

Optional frontend override (only if your backend is not on http://localhost:3001):

# apps/frontend/.env
REACT_APP_API_URL=http://localhost:3001

Start services

  1. Start Jaeger (see the dedicated section below).
  2. Start the backend:
cd apps/backend
npm run dev        
# or production mode:
npm run build
npm start

Health check: curl http://localhost:3001/health.

  1. Start the frontend:
cd apps/frontend
npm start

The UI runs at http://localhost:3000 and proxies requests to the backend URL.

Docker Compose option

Bring up Jaeger and the backend together (frontend still runs with npm start):

OPENROUTER_API_KEY=sk-***your-key*** docker compose up -d jaeger backend

Backend will listen on http://localhost:3001 and send traces to the Jaeger collector in the compose network.

Running Jaeger

  • Start Jaeger only:
docker compose up -d jaeger
  • UI: http://localhost:16686
  • OTLP/HTTP collector (what the backend uses): http://localhost:4318/v1/traces
  • After sending a few chat requests, open Jaeger UI, search for service openrouter-chat-backend, and inspect traces by endpoint (/api/chat, /api/history, /api/models).

OpenTelemetry Tracing

  • What is collected
    • Server spans for every HTTP request (method, route, status, user agent, client IP) via requestTracingMiddleware.
    • Auto-instrumented spans for Express and outbound HTTP (OpenRouter fetch calls).
    • Custom spans for:
      • openrouter.chat and openrouter.getModels (API calls, model id, token usage, response id).
      • Chat flow (chat.sendMessage) including validation, session stitching, and fallback responses.
      • History flows (history.listSessions, history.getSession, history.updateTitle, history.deleteSession, history.clearAll).
      • In-memory store operations (memoryStore.*) so cache behavior is traceable.
    • Resource metadata includes service name/version and environment for clear attribution in Jaeger.

Usage Guide

  • Send a message: Open http://localhost:3000, click “New Chat”, type in the composer, press Enter or the send button. Messages stream through the backend to OpenRouter; responses appear in the thread.
  • Switch models: The backend exposes /api/models. Update the model passed in apps/frontend/src/hooks/useChatStore.tsx (default is amazon/nova-2-lite-v1:free) or wire a dropdown that calls backendClient.getModels() and passes the selected model to sendUiMessages.
  • View history: Sidebar lists sessions with last-updated timestamp. Click to load a session, double-click to rename, use the trash icon (with confirmation) to delete.
  • Optional multimodal upload: If you enable file/image uploads, pass OpenRouter’s multimodal message shape to the backend (for example, content as an array with type: "text" and type: "image_url" items). Extend ApiMessage in packages/shared accordingly, allow the UI composer to attach files, and the backend will forward the enriched messages to routingService.chat unchanged.
  • Reset session: Use the “New Chat” button to start a fresh conversation; history persists in-memory until the backend restarts.

Project Structure

apps/
  backend/
    src/
      index.ts                # Express entrypoint + health + shutdown
      middleware/requestTracingMiddleware.ts
      routes/{chat,models,history}.ts
      services/{routingService,telemetryService,tracingUtils,openrouter-types}.ts
      db/memoryStore.ts
    Dockerfile
  frontend/
    src/
      api/backendClient.ts    # REST client
      components/{ChatPage,ChatWindow,Sidebar,ChatTitle}
      hooks/useChatStore.tsx  # chat state, history, sending
packages/
  shared/                     # reused API/UI types
docker-compose.yml            # Jaeger + backend

Developer Experience Notes

  • TypeScript everywhere: Shared types ensure backend responses and frontend usage stay aligned.
  • Auto-instrumentation first: Express and HTTP spans are automatic; runWithSpan and runWithSpanSync add business-level spans where it matters.
  • Stability and error handling: Backend returns friendly fallbacks if OpenRouter is unreachable and marks spans with exceptions for observability. Health endpoint is lightweight.
  • Graceful shutdown: SIGINT/SIGTERM handlers stop the HTTP server and flush/shutdown the OpenTelemetry SDK to avoid span loss.

Bonus Features

  • UX polish: inline rename, keyboard shortcuts (F2 to rename, Ctrl/Cmd+Shift+O for new chat, Delete to remove), image upload support, confirmation modal, and sidebar collapse.
  • The application automatically generates a conversation title based on the user's first message, providing cleaner session organization without requiring manual naming.
  • Better errors: user-facing fallback message when the model is unavailable; traced exceptions for operators.
  • Extra tracing detail: spans for cache/history operations and model metadata counts for faster debugging.

About

Local AI chat app using OpenRouter with React, Express, and OpenTelemetry tracing.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors