Plexus LLM Gateway

repository·main·Indexed 42 days ago

https://github.com/mcowger/plexus

An LLM gateway providing a unified API surface for multiple providers including OpenAI, Anthropic, and Gemini. It features protocol translation, policy-aware routing based on cost or performance, resilience tools like provider failover, and a management API and CLI for observability and configuration.

Tokens
35.9K
Snippets
80
Records
155
Agent score
52%

What's inside Plexus

  1. What is Plexus and how does it work?

    main

    Plexus is an LLM gateway that sits in front of multiple LLM providers (OpenAI, Anthropic, Gemini, and OpenAI-compatible providers). It provides a single, consistent API surface for clients, allowing you to decouple your client code from specific provider implementations.

    Key Capabilities:

    • Protocol Translation: Handles requests in OpenAI Chat Completions, Anthropic Messages, and Gemini native formats, including streaming and tool use.
    • Policy-Aware Routing: Map model aliases to one or more targets using selection strategies like random, in_order, cost, performance, latency, usage, quota, or e2e_performance.
    • Resilience: Supports exponential cooldowns, provider failover, stalled stream detection, and vision fallthrough for non-vision targets.
    • Observability: Provides a dashboard to inspect request logs, tokens, cost, latency, throughput, and provider health.
    • Security: Supports AES-256-GCM encryption at rest for sensitive data (API keys, OAuth tokens, etc.) when an ENCRYPTION_KEY is provided.
  2. Configure Provider Routing and Restrictions

    main

    When making image requests, you can influence how Plexus selects and routes to providers using the following routing intent parameters:

    • provider.only: Restricts eligible requests to a specific set of configured Plexus provider names. An empty set results in an error.
    • provider.ignore: Excludes specific configured Plexus provider names from consideration.
    • provider.order: Ranks eligible providers in a preferred order. Providers not listed in the order remain valid fallback candidates.
    • provider.allow_fallbacks:
      • true (default): Allows the system to try fallback providers if the primary target fails.
      • false: Limits the request strictly to the primary eligible target.
    • provider.sort: A routing preference (e.g., by price or latency) based on Plexus ranking data. Note that this is a routing preference, not an image generation parameter.
    • provider.options: Scoped by the configured provider name. These options are passed to the specific target transformer and are not shared across providers.
  3. Understand the Backend Test Architecture

    main

    Backend tests run on Vitest using three parallel projects defined in packages/backend/vitest.config.ts. The project used depends on the file being tested:

    ProjectPurposeDB Setup
    unitGeneral unit tests (DB mocked)None
    sqliteDB-layer tests against SQLiteTemp SQLite DB + migrations
    postgresDB-layer tests against Postgres (pglite)Temp pglite DB + migrations

    Files listed in DB_TEST_FILES (within vitest.db-tests.ts) are routed to the sqlite or postgres projects. All other files run in the unit project.

  4. Configure Multi-Protocol Providers

    main

    Some providers support multiple API formats. You can configure them using a map of protocol $\rightarrow$ URL.

    Supported protocols:

    • chat: OpenAI-compatible chat completions.
    • messages: Anthropic Claude Messages API.
    • embeddings: OpenAI-compatible embeddings.
    • openai-images: OpenAI-compatible image generation and edits (/images/generations, /images/edits).
    • openrouter-images: Dedicated OpenRouter image endpoint (/images).
    • codex-images: ChatGPT-subscription Codex Images backend (only valid on openai-codex OAuth provider).
    • transcriptions: Speech-to-text (Whisper).
    • speech: Text-to-speech.

    When combined with priority: api_match on a model alias, Plexus prefers providers that natively support the incoming API format.

  5. The Canonical Image Response Model

    main

    Plexus normalizes all image provider responses into a single unified model.

    Response Structure:

    • created: Unix timestamp (seconds).
    • data[]: An array of image objects. Each image contains:
      • bytes or base64: The image data.
      • url: A usable URL (if provided by the upstream).
      • media_type: The actual media type when known.
      • revised_prompt: An optional field containing the revised prompt.
    • usage: (Optional) Token usage details.

    Usage Normalization: Token fields from OpenAI and Gemini usageMetadata are normalized to Plexus internal names. For OpenRouter-shaped responses, these are projected to:

    • prompt_tokens
    • completion_tokens
    • total_tokens (including reported cost if available).

    Image Token Details: If a target reports image tokens, they are mapped to:

    • input_image_tokens (via input_tokens_details.image_tokens)
    • output_image_tokens (via output_tokens_details.image_tokens)
  6. Configure MCP servers

    main

    Plexus proxies Model Context Protocol (MCP) servers using HTTP streaming transport.

    Server Settings

    • Server Name: Identifier used in URLs (Required).
    • Upstream URL: Full MCP server endpoint (Required).
    • Enabled: Whether the server is active for routing.
    • Headers: Static headers to be forwarded to the upstream.

    Endpoints

    • POST /mcp/:name: For JSON-RPC messages.
    • GET /mcp/:name: For SSE streaming.
    • DELETE /mcp/:name: To end a session.

    Authentication & OAuth

    All MCP endpoints require a Plexus API key. Client auth headers are NOT forwarded; only configured static headers are added. Plexus exposes standard OAuth 2.0 endpoints for MCP clients, including discovery via /.well-known/oauth-authorization-server.

  7. How image generation and editing converge in Plexus

    main

    In Plexus, /v1/images/generations and /v1/images/edits are unified into a single dispatch loop. The system uses a UnifiedImageGenerationRequest to handle both.

    Whether a request is treated as a generation or an edit is determined by the presence of input_references. For multipart edits, the uploaded image is mapped to input_references[0].

    Key behaviors:

    • Masks: Masks remain an explicit field at every target. While the OpenAI multipart path appends the mask, other transformers like Gemini, OpenRouter, and Codex will reject a mask with an explicit error rather than silently dropping it.
    • Response Formats: For /v1/images/edits, if response_format is omitted, the system defaults to the provider's own default (e.g., url for dall-e-2) instead of forcing b64_json.
    • MIME Types: If an upload's declared MIME type is not image/*, Plexus sniffs the magic bytes to determine the type rather than rejecting it.
  8. How OpenAPI Sync Tool detects and normalizes routes

    main

    The tool uses static analysis to detect Fastify route registrations.

    Route Detection

    It looks for patterns like:

    • fastify.get('/path', handler)
    • mgmt.post('/path', handler)
    • app.put('/path', handler)

    Note: Dynamic paths (e.g., fastify.get(path, handler)) or variable methods (e.g., fastify[method](path, handler)) will not be detected.

    Path Normalization

    To compare code to documentation, the tool normalizes path parameters:

    Code PatternOpenAPI PatternNormalized
    :id{id}{id}
    :slug{slug}{slug}
    *{wildcard}{wildcard}

    Tag Assignment

    When generating new files, tags are automatically assigned based on the path prefix:

    • /v0/management/config/* $\rightarrow$ Management — Config
    • /v0/management/quotas/* $\rightarrow$ Management — Quotas (Provider)
    • /v0/management/oauth/* $\rightarrow$ Management — OAuth
    • /v1/* $\rightarrow$ Inference
    • /mcp/* $\rightarrow$ MCP
  9. Configure Grouped Analytics bounds and behavior

    main

    When requesting grouped analytics via the breakdowns parameter, the API enforces the following constraints to ensure performance:

    • Dimension Limit: Maximum of 3 dimensions per request.
    • Result Limit: Defaults to the top 10 groups; maximum allowed is 50.
    • Ordering: Results are sorted deterministically by request_count, then by name.
    • Rollups: If results are truncated, the API returns an exact Other rollup along with totalDimensions and truncated metadata.
    • Response Size: Basic responses are kept under 16 KiB, and grouped responses are kept under approximately 128 KiB.
  10. Manage Singletons and Test Isolation

    main

    Several services in Plexus are singletons (e.g., OAuthAuthManager, CooldownManager, DebugManager). To prevent state leakage between tests, you must manually reset them in your beforeEach block.

    Use the provided resetForTesting() method or equivalent helpers like ConfigService.resetInstance(). Always use afterEach for any additional cleanup required by the service.

  11. Use Plexus Management MCP tools

    main

    The /mcp/plexus endpoint provides a suite of tools for managing Plexus. Tools are compact: instead of many unique tool names, they use an operation argument to determine the action.

    Input Shape

    All tool calls follow this JSON structure:

    {
      "operation": "list | get | ...",
      "id": "optional-resource-id",
      "category": "optional-settings-category",
      "query": {},
      "body": {},
      "destructive": "acknowledged"
    }

    Destructive Operations

    For high-impact actions (e.g., delete, restore, restart, rotate), you must include "destructive": "acknowledged" in the input. If this is omitted, the tool call will fail with a confirmation_required error.

    Secret Redaction

    To protect security, all responses automatically redact sensitive fields (API keys, secrets, tokens, cookies, sessions, passwords) by replacing them with [REDACTED].

    {
      "operation": "list | get | ...",
      "id": "optional-resource-id",
      "category": "optional-settings-category",
      "query": {},
      "body": {},
      "destructive": "acknowledged"
    }
  12. JavaScript Limitations and Best Practices for Checkers

    main

    When writing custom quota checkers, adhere to the following constraints:

    • Language: Only JavaScript is supported; TypeScript is not supported.
    • Module System: Paste a function body only. Do not use import or npm package imports.
    • Security: Do not hard-code provider secrets. Use the provider API key (via ctx.fetch() or ctx.requestHeaders()) or provider options.
    • Permissions: Checker code runs with the permissions of the Plexus process. Only trusted administrators should create/edit them.
    • Execution: Checkers run in a worker with a 10-second timeout.

    Common Failure Modes:

    • Syntax error: The code cannot be compiled.
    • Runtime error: The body throws, returns a non-array, or returns an invalid meter.
    • Timeout: Execution exceeds 10 seconds.
    • HTTP error: The provider response was not ok (check response.ok manually).