# Floydr Framework
<p align="center">
<strong>A zero-boilerplate framework for building interactive ChatGPT widgets</strong>
</p>
<p align="center">
<a href="https://pypi.org/project/floydr/"><img src="https://img.shields.io/pypi/v/floydr.svg" alt="PyPI"></a>
<a href="https://pypi.org/project/floydr/"><img src="https://img.shields.io/pypi/pyversions/floydr.svg" alt="Python"></a>
<a href="https://github.com/floydr-framework/floydr/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
</p>
---
## 🚀 Quick Start
### 1. Create Virtual Environment (Recommended)
```bash
python -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows
```
### 2. Install Floydr & Create Project
```bash
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
```bash
cd my-widgets
pip install -r requirements.txt
npm install
```
### 4. Create Your First Widget
```bash
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
```python
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
```jsx
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
```bash
# 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**:
1. **Python Tool** (`server/tools/*_tool.py`)
- Define inputs with Pydantic
- Write your logic in `execute()`
- Return data as a dictionary
2. **React Component** (`widgets/*/index.jsx`)
- Get data with `useWidgetProps()`
- Render your UI
- Use inline styles
**Everything else is automatic:**
- ✅ Widget discovery
- ✅ Registration
- ✅ Build process
- ✅ Server setup
- ✅ Mounting logic
### Input Schema
```python
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:
```python
widget_csp = {
"connect_domains": ["https://api.example.com"], # For API calls
"resource_domains": ["https://cdn.example.com"] # For images/fonts
}
```
### React Hooks
```jsx
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](./docs/QUICKSTART.md)** - Detailed setup instructions
- **[Tutorial](./docs/TUTORIAL.md)** - Step-by-step widget examples
- **[API Reference](./docs/API.md)** - Complete API documentation
- **[Examples](../examples/)** - Real-world widget examples
---
## 🔧 CLI Commands
```bash
# 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
```python
# 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!"}
```
```jsx
// widgets/hello/index.jsx
export default function Hello() {
const props = useWidgetProps();
return <h1>{props.message}</h1>;
}
```
### With API Call
```python
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
```jsx
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 `identifier` matches folder name
- Rebuild: `npm run build`
- Restart: `python server/main.py`
**Import errors?**
```bash
pip install --upgrade floydr
npm install chatjs-hooks@latest
```
**Need help?** Check our [docs](./docs/) or [open an issue](https://github.com/floydr-framework/floydr/issues)
---
## 🤝 Contributing
See [CONTRIBUTING.md](../../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/
Raw data
{
"_id": null,
"home_page": "https://github.com/floydr-framework/floydr",
"name": "floydr",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": "chatgpt, widgets, mcp, framework, react",
"author": "Floydr Team",
"author_email": "Floydr Team <hello@floydr.dev>",
"download_url": "https://files.pythonhosted.org/packages/00/24/1a58aeb32dbdb19ee493d8c1309a14b66ada280e7f4b9711d362b93330b5/floydr-1.0.5.tar.gz",
"platform": null,
"description": "# Floydr Framework\n\n<p align=\"center\">\n <strong>A zero-boilerplate framework for building interactive ChatGPT widgets</strong>\n</p>\n\n<p align=\"center\">\n <a href=\"https://pypi.org/project/floydr/\"><img src=\"https://img.shields.io/pypi/v/floydr.svg\" alt=\"PyPI\"></a>\n <a href=\"https://pypi.org/project/floydr/\"><img src=\"https://img.shields.io/pypi/pyversions/floydr.svg\" alt=\"Python\"></a>\n <a href=\"https://github.com/floydr-framework/floydr/blob/main/LICENSE\"><img src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"License\"></a>\n</p>\n\n---\n\n## \ud83d\ude80 Quick Start\n\n### 1. Create Virtual Environment (Recommended)\n\n```bash\npython -m venv venv\nsource venv/bin/activate # macOS/Linux\nvenv\\Scripts\\activate # Windows\n```\n\n### 2. Install Floydr & Create Project\n\n```bash\npip install floydr\nfloydr init my-widgets\n```\n\nThis generates the complete project structure:\n\n```\nmy-widgets/\n\u251c\u2500\u2500 server/\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u251c\u2500\u2500 main.py # Auto-discovery server\n\u2502 \u251c\u2500\u2500 tools/ # Widget backends\n\u2502 \u2502 \u2514\u2500\u2500 __init__.py\n\u2502 \u2514\u2500\u2500 api/ # (optional) Shared APIs\n\u2502 \u2514\u2500\u2500 __init__.py\n\u251c\u2500\u2500 widgets/ # Widget frontends (empty initially)\n\u251c\u2500\u2500 requirements.txt # Python dependencies\n\u251c\u2500\u2500 package.json # JavaScript dependencies\n\u251c\u2500\u2500 .gitignore\n\u2514\u2500\u2500 README.md\n```\n\n### 3. Install Dependencies\n\n```bash\ncd my-widgets\npip install -r requirements.txt\nnpm install\n```\n\n### 4. Create Your First Widget\n\n```bash\nfloydr create greeting\n```\n\nThis adds to your project:\n\n```\nmy-widgets/\n\u251c\u2500\u2500 server/\n\u2502 \u2514\u2500\u2500 tools/\n\u2502 \u2514\u2500\u2500 greeting_tool.py # \u2190 Generated: Widget backend\n\u2514\u2500\u2500 widgets/\n \u2514\u2500\u2500 greeting/\n \u2514\u2500\u2500 index.jsx # \u2190 Generated: Widget frontend\n```\n\n### 5. Edit Your Widget Code\n\n**You only need to edit these 2 files:**\n\n#### `server/tools/greeting_tool.py` - Backend Logic\n\n```python\nfrom floydr import BaseWidget, Field, ConfigDict\nfrom pydantic import BaseModel\nfrom typing import Dict, Any\n\nclass GreetingInput(BaseModel):\n model_config = ConfigDict(populate_by_name=True)\n name: str = Field(default=\"World\")\n\nclass GreetingTool(BaseWidget):\n identifier = \"greeting\"\n title = \"Greeting Widget\"\n input_schema = GreetingInput\n invoking = \"Preparing greeting...\"\n invoked = \"Greeting ready!\"\n \n widget_csp = {\n \"connect_domains\": [], # APIs you'll call\n \"resource_domains\": [] # Images/fonts you'll use\n }\n \n async def execute(self, input_data: GreetingInput) -> Dict[str, Any]:\n # Your logic here\n return {\n \"name\": input_data.name,\n \"message\": f\"Hello, {input_data.name}!\"\n }\n```\n\n#### `widgets/greeting/index.jsx` - Frontend UI\n\n```jsx\nimport React from 'react';\nimport { useWidgetProps } from 'chatjs-hooks';\n\nexport default function Greeting() {\n const props = useWidgetProps();\n \n return (\n <div style={{\n padding: '40px',\n textAlign: 'center',\n background: '#4A90E2',\n color: 'white',\n borderRadius: '12px'\n }}>\n <h1>\ud83d\udc4b {props.message}</h1>\n <p>Welcome, {props.name}!</p>\n </div>\n );\n}\n```\n\n**That's it! These are the only files you need to write.**\n\n### 6. Build and Run\n\n```bash\n# Build\nnpm run build\n\n# Start server\npython server/main.py\n```\n\nYour widget is now live at `http://localhost:8001` \ud83c\udf89\n\n---\n\n## \ud83d\udce6 What You Need to Know\n\n### Widget Structure\n\nEvery widget has **exactly 2 files you write**:\n\n1. **Python Tool** (`server/tools/*_tool.py`)\n - Define inputs with Pydantic\n - Write your logic in `execute()`\n - Return data as a dictionary\n\n2. **React Component** (`widgets/*/index.jsx`)\n - Get data with `useWidgetProps()`\n - Render your UI\n - Use inline styles\n\n**Everything else is automatic:**\n- \u2705 Widget discovery\n- \u2705 Registration\n- \u2705 Build process\n- \u2705 Server setup\n- \u2705 Mounting logic\n\n### Input Schema\n\n```python\nfrom floydr import Field, ConfigDict\nfrom pydantic import BaseModel\n\nclass MyInput(BaseModel):\n model_config = ConfigDict(populate_by_name=True)\n \n name: str = Field(default=\"\", description=\"User's name\")\n age: int = Field(default=0, ge=0, le=150)\n email: str = Field(default=\"\", pattern=r'^[\\w\\.-]+@[\\w\\.-]+\\.\\w+$')\n```\n\n### CSP (Content Security Policy)\n\nAllow external resources:\n\n```python\nwidget_csp = {\n \"connect_domains\": [\"https://api.example.com\"], # For API calls\n \"resource_domains\": [\"https://cdn.example.com\"] # For images/fonts\n}\n```\n\n### React Hooks\n\n```jsx\nimport { useWidgetProps, useWidgetState, useOpenAiGlobal } from 'chatjs-hooks';\n\nfunction MyWidget() {\n const props = useWidgetProps(); // Data from Python\n const [state, setState] = useWidgetState({}); // Persistent state\n const theme = useOpenAiGlobal('theme'); // ChatGPT theme\n \n return <div>{props.message}</div>;\n}\n```\n\n---\n\n## \ud83d\udcda Documentation\n\n- **[Quick Start Guide](./docs/QUICKSTART.md)** - Detailed setup instructions\n- **[Tutorial](./docs/TUTORIAL.md)** - Step-by-step widget examples\n- **[API Reference](./docs/API.md)** - Complete API documentation\n- **[Examples](../examples/)** - Real-world widget examples\n\n---\n\n## \ud83d\udd27 CLI Commands\n\n```bash\n# Create new widget (auto-generates both files)\npython -m floydr.cli.main create mywidget\n\n# Or if installed globally:\nfloydr create mywidget\n```\n\n---\n\n## \ud83d\udcd6 Project Structure After `floydr create`\n\nWhen you run `python -m floydr.cli.main create greeting`, you get:\n\n```\nmy-widgets/\n\u251c\u2500\u2500 server/\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u251c\u2500\u2500 main.py # \u2705 Already setup (no edits needed)\n\u2502 \u251c\u2500\u2500 tools/\n\u2502 \u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u2502 \u2514\u2500\u2500 greeting_tool.py # \u2190 Edit this: Your widget logic\n\u2502 \u2514\u2500\u2500 api/ # (optional: for shared APIs)\n\u2502\n\u251c\u2500\u2500 widgets/\n\u2502 \u2514\u2500\u2500 greeting/\n\u2502 \u2514\u2500\u2500 index.jsx # \u2190 Edit this: Your UI\n\u2502\n\u251c\u2500\u2500 assets/ # \u2699\ufe0f Auto-generated during build\n\u2502 \u251c\u2500\u2500 greeting-HASH.html\n\u2502 \u2514\u2500\u2500 greeting-HASH.js\n\u2502\n\u251c\u2500\u2500 requirements.txt # Python dependencies\n\u251c\u2500\u2500 package.json # JavaScript dependencies\n\u2514\u2500\u2500 build-all.mts # \u2699\ufe0f Auto-copied from chatjs-hooks\n```\n\n**You only edit the 2 files marked with \u2190**\n\n---\n\n## \ud83c\udfaf Key Features\n\n- \u2705 **Zero Boilerplate** - Just write your widget code\n- \u2705 **Auto-Discovery** - Widgets automatically registered\n- \u2705 **Type-Safe** - Pydantic for Python, TypeScript for React\n- \u2705 **CLI Tools** - Scaffold widgets instantly\n- \u2705 **React Hooks** - Modern React patterns via `chatjs-hooks`\n- \u2705 **MCP Protocol** - Native ChatGPT integration\n\n---\n\n## \ud83d\udca1 Examples\n\n### Simple Widget\n\n```python\n# server/tools/hello_tool.py\nclass HelloTool(BaseWidget):\n identifier = \"hello\"\n title = \"Hello\"\n input_schema = HelloInput\n \n async def execute(self, input_data):\n return {\"message\": \"Hello World!\"}\n```\n\n```jsx\n// widgets/hello/index.jsx\nexport default function Hello() {\n const props = useWidgetProps();\n return <h1>{props.message}</h1>;\n}\n```\n\n### With API Call\n\n```python\nasync def execute(self, input_data):\n async with httpx.AsyncClient() as client:\n response = await client.get(\"https://api.example.com/data\")\n data = response.json()\n return {\"data\": data}\n```\n\n### With State\n\n```jsx\nfunction Counter() {\n const [state, setState] = useWidgetState({ count: 0 });\n return (\n <button onClick={() => setState({ count: state.count + 1 })}>\n Count: {state.count}\n </button>\n );\n}\n```\n\n---\n\n## \ud83d\udc1b Troubleshooting\n\n**Widget not loading?**\n- Check `identifier` matches folder name\n- Rebuild: `npm run build`\n- Restart: `python server/main.py`\n\n**Import errors?**\n```bash\npip install --upgrade floydr\nnpm install chatjs-hooks@latest\n```\n\n**Need help?** Check our [docs](./docs/) or [open an issue](https://github.com/floydr-framework/floydr/issues)\n\n---\n\n## \ud83e\udd1d Contributing\n\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md)\n\n## \ud83d\udcc4 License\n\nMIT \u00a9 Floydr Team\n\n## \ud83d\udd17 Links\n\n- **PyPI**: https://pypi.org/project/floydr/\n- **ChatJS Hooks**: https://www.npmjs.com/package/chatjs-hooks\n- **GitHub**: https://github.com/floydr-framework/floydr\n- **MCP Spec**: https://modelcontextprotocol.io/\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A zero-boilerplate framework for building interactive ChatGPT widgets",
"version": "1.0.5",
"project_urls": {
"Bug Reports": "https://github.com/floydr-framework/floydr/issues",
"Documentation": "https://floydr.dev/docs",
"Homepage": "https://floydr.dev",
"Repository": "https://github.com/floydr-framework/floydr"
},
"split_keywords": [
"chatgpt",
" widgets",
" mcp",
" framework",
" react"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "fedc074373dcbf1a6cff99191ea258bb099b7e308fcd3df4f0540c7ab379b260",
"md5": "5f7120054ca9c6bdd4c7013ca2152367",
"sha256": "4483ff206dfe7c3e858e8240a12e664e49a3f2cbbe22aebd95ad29cd5a6b788e"
},
"downloads": -1,
"filename": "floydr-1.0.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5f7120054ca9c6bdd4c7013ca2152367",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 16737,
"upload_time": "2025-10-15T08:53:16",
"upload_time_iso_8601": "2025-10-15T08:53:16.893788Z",
"url": "https://files.pythonhosted.org/packages/fe/dc/074373dcbf1a6cff99191ea258bb099b7e308fcd3df4f0540c7ab379b260/floydr-1.0.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "00241a58aeb32dbdb19ee493d8c1309a14b66ada280e7f4b9711d362b93330b5",
"md5": "934fb702fbb995d734e785949eec06e2",
"sha256": "851fa03847687e5c4bc9e7c50d7b3517ff7a88483a7f290aac57a66f3fffd3bb"
},
"downloads": -1,
"filename": "floydr-1.0.5.tar.gz",
"has_sig": false,
"md5_digest": "934fb702fbb995d734e785949eec06e2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 34746,
"upload_time": "2025-10-15T08:53:18",
"upload_time_iso_8601": "2025-10-15T08:53:18.086025Z",
"url": "https://files.pythonhosted.org/packages/00/24/1a58aeb32dbdb19ee493d8c1309a14b66ada280e7f4b9711d362b93330b5/floydr-1.0.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-15 08:53:18",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "floydr-framework",
"github_project": "floydr",
"github_not_found": true,
"lcname": "floydr"
}