Floydr Framework
A zero-boilerplate framework for building interactive ChatGPT widgets
🚀 Quick Start
1. Create Virtual Environment (Recommended)
python -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows
2. Install Floydr & Create Project
pip install floydr
floydr init my-widgets
This generates the complete project structure:
my-widgets/
├── server/
│ ├── __init__.py
│ ├── main.py # Auto-discovery server
│ ├── tools/ # Widget backends
│ │ └── __init__.py
│ └── api/ # (optional) Shared APIs
│ └── __init__.py
├── widgets/ # Widget frontends (empty initially)
├── requirements.txt # Python dependencies
├── package.json # JavaScript dependencies
├── .gitignore
└── README.md
3. Install Dependencies
cd my-widgets
pip install -r requirements.txt
npm install
4. Create Your First Widget
floydr create greeting
This adds to your project:
my-widgets/
├── server/
│ └── tools/
│ └── greeting_tool.py # ← Generated: Widget backend
└── widgets/
└── greeting/
└── index.jsx # ← Generated: Widget frontend
5. Edit Your Widget Code
You only need to edit these 2 files:
server/tools/greeting_tool.py - Backend Logic
from floydr import BaseWidget, Field, ConfigDict
from pydantic import BaseModel
from typing import Dict, Any
class GreetingInput(BaseModel):
model_config = ConfigDict(populate_by_name=True)
name: str = Field(default="World")
class GreetingTool(BaseWidget):
identifier = "greeting"
title = "Greeting Widget"
input_schema = GreetingInput
invoking = "Preparing greeting..."
invoked = "Greeting ready!"
widget_csp = {
"connect_domains": [], # APIs you'll call
"resource_domains": [] # Images/fonts you'll use
}
async def execute(self, input_data: GreetingInput) -> Dict[str, Any]:
# Your logic here
return {
"name": input_data.name,
"message": f"Hello, {input_data.name}!"
}
widgets/greeting/index.jsx - Frontend UI
import React from 'react';
import { useWidgetProps } from 'chatjs-hooks';
export default function Greeting() {
const props = useWidgetProps();
return (
<div style={{
padding: '40px',
textAlign: 'center',
background: '#4A90E2',
color: 'white',
borderRadius: '12px'
}}>
<h1>👋 {props.message}</h1>
<p>Welcome, {props.name}!</p>
</div>
);
}
That's it! These are the only files you need to write.
6. Build and Run
# Build
npm run build
# Start server
python server/main.py
Your widget is now live at http://localhost:8001 🎉
📦 What You Need to Know
Widget Structure
Every widget has exactly 2 files you write:
-
Python Tool (
server/tools/*_tool.py)- Define inputs with Pydantic
- Write your logic in
execute() - Return data as a dictionary
-
React Component (
widgets/*/index.jsx)- Get data with
useWidgetProps() - Render your UI
- Use inline styles
- Get data with
Everything else is automatic:
- ✅ Widget discovery
- ✅ Registration
- ✅ Build process
- ✅ Server setup
- ✅ Mounting logic
Input Schema
from floydr import Field, ConfigDict
from pydantic import BaseModel
class MyInput(BaseModel):
model_config = ConfigDict(populate_by_name=True)
name: str = Field(default="", description="User's name")
age: int = Field(default=0, ge=0, le=150)
email: str = Field(default="", pattern=r'^[\w\.-]+@[\w\.-]+\.\w+$')
CSP (Content Security Policy)
Allow external resources:
widget_csp = {
"connect_domains": ["https://api.example.com"], # For API calls
"resource_domains": ["https://cdn.example.com"] # For images/fonts
}
React Hooks
import { useWidgetProps, useWidgetState, useOpenAiGlobal } from 'chatjs-hooks';
function MyWidget() {
const props = useWidgetProps(); // Data from Python
const [state, setState] = useWidgetState({}); // Persistent state
const theme = useOpenAiGlobal('theme'); // ChatGPT theme
return <div>{props.message}</div>;
}
📚 Documentation
- Quick Start Guide - Detailed setup instructions
- Tutorial - Step-by-step widget examples
- API Reference - Complete API documentation
- Examples - Real-world widget examples
🔧 CLI Commands
# Create new widget (auto-generates both files)
python -m floydr.cli.main create mywidget
# Or if installed globally:
floydr create mywidget
📖 Project Structure After floydr create
When you run python -m floydr.cli.main create greeting, you get:
my-widgets/
├── server/
│ ├── __init__.py
│ ├── main.py # ✅ Already setup (no edits needed)
│ ├── tools/
│ │ ├── __init__.py
│ │ └── greeting_tool.py # ← Edit this: Your widget logic
│ └── api/ # (optional: for shared APIs)
│
├── widgets/
│ └── greeting/
│ └── index.jsx # ← Edit this: Your UI
│
├── assets/ # ⚙️ Auto-generated during build
│ ├── greeting-HASH.html
│ └── greeting-HASH.js
│
├── requirements.txt # Python dependencies
├── package.json # JavaScript dependencies
└── build-all.mts # ⚙️ Auto-copied from chatjs-hooks
You only edit the 2 files marked with ←
🎯 Key Features
- ✅ Zero Boilerplate - Just write your widget code
- ✅ Auto-Discovery - Widgets automatically registered
- ✅ Type-Safe - Pydantic for Python, TypeScript for React
- ✅ CLI Tools - Scaffold widgets instantly
- ✅ React Hooks - Modern React patterns via
chatjs-hooks - ✅ MCP Protocol - Native ChatGPT integration
💡 Examples
Simple Widget
# server/tools/hello_tool.py
class HelloTool(BaseWidget):
identifier = "hello"
title = "Hello"
input_schema = HelloInput
async def execute(self, input_data):
return {"message": "Hello World!"}
// widgets/hello/index.jsx
export default function Hello() {
const props = useWidgetProps();
return <h1>{props.message}</h1>;
}
With API Call
async def execute(self, input_data):
async with httpx.AsyncClient() as client:
response = await client.get("https://api.example.com/data")
data = response.json()
return {"data": data}
With State
function Counter() {
const [state, setState] = useWidgetState({ count: 0 });
return (
<button onClick={() => setState({ count: state.count + 1 })}>
Count: {state.count}
</button>
);
}
🐛 Troubleshooting
Widget not loading?
- Check
identifiermatches folder name - Rebuild:
npm run build - Restart:
python server/main.py
Import errors?
pip install --upgrade floydr
npm install chatjs-hooks@latest
Need help? Check our docs or open an issue
🤝 Contributing
See CONTRIBUTING.md
📄 License
MIT © Floydr Team
🔗 Links
- PyPI: https://pypi.org/project/floydr/
- ChatJS Hooks: https://www.npmjs.com/package/chatjs-hooks
- GitHub: https://github.com/floydr-framework/floydr
- MCP Spec: https://modelcontextprotocol.io/
Metadata
Release files for floydr 1.0.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| floydr-1.0.5.tar.gz | 34.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| floydr-1.0.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.5 kB
Release files / floydr-1.0.5.tar.gz
| Download URL | floydr-1.0.5.tar.gz |
|---|---|
| Size | 34.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
851fa03847687e5c4bc9e7c50d7b3517ff7a88483a7f290aac57a66f3fffd3bb
|
|
BLAKE2b-256 checksum How to use checksums |
00241a58aeb32dbdb19ee493d8c1309a14b66ada280e7f4b9711d362b93330b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.14
|
Release files / floydr-1.0.5-py3-none-any.whl
| Download URL | floydr-1.0.5-py3-none-any.whl |
|---|---|
| Size | 16.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4483ff206dfe7c3e858e8240a12e664e49a3f2cbbe22aebd95ad29cd5a6b788e
|
|
BLAKE2b-256 checksum How to use checksums |
fedc074373dcbf1a6cff99191ea258bb099b7e308fcd3df4f0540c7ab379b260
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.14
|