Skip to main content

Floydr Framework

A zero-boilerplate framework for building interactive ChatGPT widgets

PyPI Python License


🚀 Quick Start

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:

  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

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


🔧 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 identifier matches 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

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)

Source distribution for floydr 1.0.5
File Size Uploaded
floydr-1.0.5.tar.gz 34.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for floydr 1.0.5
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

1.0.5 This release

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.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