OmniRoute Project Structure: A Deep Dive into the Next.js, Electron, and Streaming Engine Architecture

OmniRoute organizes its codebase as a monorepo with distinct top-level directories for the Next.js 16 frontend (src/), a dedicated streaming engine (open-sse/), an Electron desktop wrapper (electron/), and comprehensive test suites (tests/).

The repository at diegosouzapw/OmniRoute separates concerns between UI rendering, API routing, and server-side streaming while maintaining tight integration through type-safe TypeScript utilities. This architecture supports everything from chat completion endpoints to complex multi-provider routing strategies.

Top-Level Directory Layout

The project structure follows a clear separation of concerns at the root level:

Directory Purpose Key Contents
src/ Core TypeScript application code Next.js App Router, API routes, domain libraries
open-sse/ Server-side streaming engine Request handlers, provider executors, translators
electron/ Desktop application wrapper Main process, preload scripts, OAuth management
tests/ Verification suites Unit tests (Vitest), integration tests, E2E (Playwright)
docs/ Documentation and specifications OpenAPI specs, architecture diagrams
config/ Runtime configuration i18n settings, quality baselines, payload rules
public/ Static assets Icons, service workers, public files

Root configuration files include package.json, tsconfig.json, and next.config.mjs, which define the build pipeline and TypeScript compilation settings for the entire monorepo.

The Next.js Application Layer

App Router and API Endpoints

The src/app/ directory houses the Next.js 16 App Router implementation. This contains page components, layouts, and error boundaries that render the dashboard and API Explorer.

API routes reside under src/app/api/v1/, following a RESTful structure:

Each route validates input using Zod schemas from src/shared/validation/ before delegating to the streaming engine.

Domain Libraries and Shared Utilities

The src/lib/ directory contains domain-specific modules isolated by concern:

Shared utilities in src/shared/ provide cross-cutting functionality:

Frontend State Management

The src/store/ directory contains Zustand stores for UI state:

  • src/store/themeStore.ts – Theme preferences and dark mode toggling
  • Notification management and email privacy settings

The Streaming Engine (open-sse/)

The open-sse/ directory is the heart of OmniRoute's request processing pipeline. This is where Server-Sent Events (SSE) are orchestrated between upstream LLM providers and downstream clients.

Request Handlers and Executors

Incoming requests flow through specialized handlers:

Executors in open-sse/executors/ handle provider-specific HTTP implementations:

Translation and Routing Services

The open-sse/translator/ directory converts between OpenAI-compatible request formats and provider-specific payloads. Supporting services include:

Desktop and Testing Infrastructure

Electron Wrapper

The electron/ directory wraps the Next.js application as a native desktop client:

The Electron wrapper reuses the same Next.js build output, allowing the full OmniRoute UI to run outside the browser.

Test Organization

The tests/ directory is organized by scope:

  • tests/unit/ – Fast module tests for provider registries and guardrails
  • tests/integration/ – Full request pipeline testing
  • tests/e2e/ – Playwright browser automation tests

Tests run with the native Node test runner (node --import tsx/esm --test) and Vitest for the MCP server components.

How Request Flow Works Through the Project Structure

Understanding the project structure requires tracing how a chat completion request travels through the system:

  1. Client requests /api/v1/chat/completions via the Next.js UI in src/app/
  2. Route validation occurs in src/app/api/v1/chat/completions/route.ts using Zod schemas
  3. Authentication is enforced via src/shared/utils/apiKey.ts
  4. Delegation to src/sse/handlers/chat.ts bridges the HTTP API to the streaming engine
  5. Translation in open-sse/translator/ converts the request format for the target provider
  6. Execution via open-sse/executors/ dispatches to the upstream LLM (OpenAI, Anthropic, etc.)
  7. Streaming responses flow back through open-sse/handlers/chatCore.ts to the client

The combo routing engine (open-sse/services/combo.ts) can intercept this flow to run multiple providers in parallel or sequence, applying cost-optimization or fallback strategies.

Extending the Architecture

Adding a New API Route

Create a new route file under src/app/api/v1/:

// src/app/api/v1/custom/hello/route.ts
import { json } from 'next/headers';
import { ensureApiKey } from '@/shared/utils/apiKey';

export async function GET() {
  await ensureApiKey();               // enforce API-key policy
  return json({ message: 'Hello from OmniRoute!' });
}

Registering a Custom Executor

Implement a new provider by extending the default executor:

// open-sse/executors/myProvider.ts
import { DefaultExecutor } from './default';

export class MyProviderExecutor extends DefaultExecutor {
  protected buildUrl() {
    return 'https://api.myprovider.com/v1/chat/completions';
  }

  protected buildHeaders() {
    return {
      Authorization: `Bearer ${this.credentials.apiKey}`,
      'Content-Type': 'application/json',
    };
  }
}

Then register it in the factory:

// open-sse/executors/index.ts
import { MyProviderExecutor } from './myProvider';

export function getExecutor(providerId: string) {
  if (providerId === 'myProvider') return new MyProviderExecutor();
  // …existing branches…
}

Integrating with Electron IPC

Expose functionality to the renderer via the preload script:

// electron/preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('omni', {
  getVersion: () => ipcRenderer.invoke('get-version'),
});

Access from the frontend:

// src/app/dashboard/page.tsx
useEffect(() => {
  window.omni.getVersion().then(v => setVersion(v));
}, []);

Summary

  • OmniRoute uses a monorepo project structure separating the Next.js frontend (src/), streaming engine (open-sse/), and Electron desktop client (electron/)
  • The src/app/api/v1/ directory contains REST endpoints that validate requests before delegating to open-sse handlers
  • Streaming logic is isolated in open-sse/ with clear boundaries between handlers, translators, and provider-specific executors
  • Type safety is enforced through Zod schemas in src/shared/validation/ and used consistently across API routes
  • The combo routing engine (open-sse/services/combo.ts) enables sophisticated multi-provider strategies without mixing concerns into the API layer
  • Electron reuses the Next.js build output, maintaining a single codebase for web and desktop

Frequently Asked Questions

What is the relationship between src/app/api and the open-sse directory?

The src/app/api/v1/ routes handle HTTP ingress, authentication, and input validation using Zod schemas. Once validated, these routes delegate to thin wrappers in src/sse/handlers/, which bridge to the open-sse/ streaming engine. This separation keeps HTTP concerns (headers, cookies, CORS) distinct from streaming logic (SSE formatting, provider translation, retry logic).

How does the Electron desktop app reuse the Next.js codebase?

The electron/main.js process creates a browser window that loads the Next.js application. The electron/preload.js script exposes a secure IPC bridge to the renderer, allowing the same React components used in the web version to run in a desktop shell. The build pipeline outputs to a directory that Electron serves locally, ensuring feature parity between web and desktop without code duplication.

Where are LLM provider credentials and configurations managed?

Provider configurations are validated against schemas in src/shared/validation/providerSchema.ts. Credentials are handled within executor classes in open-sse/executors/ (such as default.ts or vertex.ts), which retrieve API keys from environment variables or secure storage. The src/lib/db/core.ts SQLite layer persists user-specific provider preferences and rate-limit counters.

How does the combo routing engine select which LLM provider to use?

The combo routing engine, implemented in open-sse/services/combo.ts, applies strategies defined in the request configuration. It can execute providers sequentially for fallback scenarios, parallel for latency optimization, or weighted for cost distribution. The engine intercepts requests after translation but before execution, allowing dynamic provider selection based on availability, token costs, or response quality metrics.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →