Skip to main content

FrameX logo

FrameX

A plugin-first Python framework for building modular FastAPI services with optional Ray Serve support.

Docs CI Coverage Release PyPI License

Overview

FrameX helps split a Python service into independently developed plugins while exposing one consistent HTTP and internal API surface. It is useful when a service needs clear module ownership, plugin loading, optional proxy integration, and a path from local execution to Ray-backed serving.

Core capabilities:

  • plugin registration and discovery
  • FastAPI route generation from plugin methods
  • internal plugin API calls
  • optional Ray Serve execution
  • built-in example and proxy plugins
  • configuration from environment variables, TOML, and pyproject.toml

FrameX architecture

What Problem It Solves

FrameX is most useful when multiple teams need to ship capabilities in parallel, call each other through stable service interfaces, and keep implementation details private so each team can work without understanding or depending on other teams' codebases.

Use it when you need to:

  • build service capabilities as plug-and-play modules
  • let multiple engineers or teams ship in parallel with clearer ownership boundaries
  • split a growing service into independently evolving capability units
  • call other teams' capabilities without depending on their codebases
  • expose local plugins and upstream APIs behind one consistent service surface
  • integrate third-party or internal HTTP services with minimal client-side changes
  • start with simple local execution and scale to Ray when needed
  • keep the system extensible as capabilities, teams, and traffic grow

Why FrameX Instead Of Plain FastAPI

Plain FastAPI is a good choice for a single cohesive application. FrameX is better when the real problem is not route handling, but service decomposition, team boundaries, and cross-service integration.

Compared with plain FastAPI, FrameX gives you:

  • plugin boundaries for clearer ownership between capabilities and teams
  • a better development model for plug-and-play modules and parallel delivery
  • one consistent surface for local capabilities and upstream HTTP services
  • internal callable APIs in addition to normal HTTP routes
  • explicit dependency declarations between capabilities
  • the ability to start locally and move to Ray-backed execution without rewriting plugin code

If you only need a small application with a stable route surface and one codebase, plain FastAPI is usually simpler.

Installation

pip install framex-kit

Install Ray Serve support when needed:

pip install "framex-kit[ray]"

FrameX requires Python 3.11 or newer.

Quick Start

Create a plugin module:

from pydantic import BaseModel

from framex.consts import VERSION
from framex.plugin import BasePlugin, PluginMetadata, on_register, on_request

__plugin_meta__ = PluginMetadata(
    name="foo",
    version=VERSION,
    description="A minimal FrameX plugin",
    author="you",
    url="https://github.com/touale/FrameX-kit",
)


class EchoBody(BaseModel):
    text: str


@on_register()
class FooPlugin(BasePlugin):
    @on_request("/foo", methods=["GET"])
    async def echo(self, message: str) -> str:
        return f"foo: {message}"

    @on_request("/foo_model", methods=["POST"])
    async def echo_model(self, model: EchoBody) -> dict[str, str]:
        return {"text": model.text}

Run the service:

PYTHONPATH=. framex run --load-plugins foo

Call the API:

curl "http://127.0.0.1:8080/api/v1/foo?message=hello"

Open the generated API docs:

  • http://127.0.0.1:8080/docs
  • http://127.0.0.1:8080/redoc
  • http://127.0.0.1:8080/api/v1/openapi.json

You can also run the built-in example plugin:

framex run --load-builtin-plugins echo

Calling Other Plugins

A plugin can call another plugin through FrameX instead of importing the other plugin's implementation directly. Declare the APIs it depends on in required_remote_apis, then call them with self._call_remote_api(...).

from framex.consts import VERSION
from framex.plugin import BasePlugin, PluginMetadata, on_register, on_request

__plugin_meta__ = PluginMetadata(
    name="consumer",
    version=VERSION,
    description="Call another FrameX plugin",
    author="you",
    url="https://github.com/touale/FrameX-kit",
    required_remote_apis=["/api/v1/foo"],
)


@on_register()
class ConsumerPlugin(BasePlugin):
    @on_request("/consumer", methods=["GET"])
    async def call_foo(self, message: str) -> str:
        return await self._call_remote_api("/api/v1/foo", message=message)

Run both plugins:

PYTHONPATH=. framex run \
  --load-plugins foo \
  --load-plugins consumer

Call the consumer API:

curl "http://127.0.0.1:8080/api/v1/consumer?message=hello"

required_remote_apis keeps plugin dependencies explicit. It can reference HTTP APIs such as /api/v1/foo, or internal function APIs such as echo.EchoPlugin.confess when a plugin exposes a function-only API.

CLI

framex run --host 0.0.0.0 --port 8080 --load-builtin-plugins echo

Common options:

  • --host
  • --port
  • --load-plugins
  • --load-builtin-plugins
  • --use-ray / --no-use-ray
  • --enable-proxy / --no-enable-proxy
  • --dashboard-host
  • --dashboard-port
  • --num-cpus

--load-plugins and --load-builtin-plugins are repeatable options:

framex run \
  --load-builtin-plugins echo \
  --load-plugins foo \
  --load-plugins your_project.plugins.bar

Use Proxy Plugin

Use the built-in proxy plugin when you already have an HTTP service and want FrameX to expose it as part of the same API surface without writing wrapper plugin methods.

For example, if an upstream service runs at http://127.0.0.1:9000 and exposes GET /api/v1/chat, FrameX can expose the same route at http://127.0.0.1:8080/api/v1/chat and forward matching requests to the upstream service.

Minimal configuration:

load_builtin_plugins = ["proxy"]

[server]
enable_proxy = true

[plugins.proxy]
timeout = 600

[plugins.proxy.proxy_urls."http://127.0.0.1:9000"]
enable = ["/*"]
disable = []

Start from the CLI:

framex run --load-builtin-plugins proxy --enable-proxy

Common uses:

  • bridge an existing FastAPI/OpenAPI service into FrameX
  • expose remote team services under one gateway
  • migrate APIs into plugins gradually instead of rewriting them all at once
  • forward query parameters, JSON bodies, multipart forms, file uploads, and configured streaming endpoints

Configuration

FrameX reads settings from environment variables, .env, .env.prod, config.toml, and [tool.framex] in pyproject.toml.

Minimal config.toml:

load_builtin_plugins = ["echo"]
load_plugins = ["your_project.plugins.foo"]

[server]
host = "127.0.0.1"
port = 8080
use_ray = false
enable_proxy = false

[plugins.foo]
debug = true

Nested environment variables are supported:

export SERVER__PORT=9000
export SERVER__ENABLE_PROXY=true

Documentation

See the online documentation for plugin APIs, proxy mode, Ray mode, authentication, and advanced configuration.

License

This project is licensed under the MIT License. See LICENSE.

Release files for framex-kit 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for framex-kit 0.4.1
File Size Uploaded
framex_kit-0.4.1.tar.gz 307.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for framex-kit 0.4.1
File Interpreter ABI Platform
framex_kit-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 376.7 kB

Release files / framex_kit-0.4.1.tar.gz

Download URL framex_kit-0.4.1.tar.gz
Size 307.8 kB
Tags Source
SHA-256 checksum
How to use checksums
7beb8b76dc987871af5e3ef9590178a56382d54cc4841b064b81a82bfe2992f6
BLAKE2b-256 checksum
How to use checksums
1f3bb01a212b7a3f662b74e5869f8fa13bd962c599aebfa513916d44abd89004
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Release files / framex_kit-0.4.1-py3-none-any.whl

Download URL framex_kit-0.4.1-py3-none-any.whl
Size 68.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
51cf0a5ab325e140a1e4c6a5eeacb088297439ec9c5dd02c75bacdc6a82b24d1
BLAKE2b-256 checksum
How to use checksums
c75528e2359e8d13367ecca95aad9088d8e0238864d784693f07c5a1d6f8bf20
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.9

2 release files

0.3.8

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.15

2 release files

0.2.13

2 release files

0.2.9

2 release files

0.2.8

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page