Skip to main content

A zero-boilerplate framework for building interactive ChatGPT widgets

Project description

Floydr Framework

A zero-boilerplate framework for building interactive ChatGPT widgets

PyPI Python License


๐Ÿš€ Quick Start

1. Create Virtual Environment (Recommended)

python -m venv venv
source venv/bin/activate    # macOS/Linux
venv\Scripts\activate       # Windows

2. Install Floydr

pip install floydr

3. Create Project

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

4. Install Dependencies

cd my-widgets

# Python dependencies
pip install -r requirements.txt

# JavaScript dependencies
npm install

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

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

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

๐Ÿ”— Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

floydr-1.0.4.tar.gz (34.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

floydr-1.0.4-py3-none-any.whl (16.7 kB view details)

Uploaded Python 3

File details

Details for the file floydr-1.0.4.tar.gz.

File metadata

  • Download URL: floydr-1.0.4.tar.gz
  • Upload date:
  • Size: 34.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for floydr-1.0.4.tar.gz
Algorithm Hash digest
SHA256 dbaa7554a8c3df0ffd05bfed1c713ef7cf32a8e25b1b246afb44a6b8cc37e35b
MD5 eac3bc98978c0594992c255f5ad5f5e8
BLAKE2b-256 75b11514575bbf3768eb3f3700e8449e23589a40c3f7b01d6aa8e699b9f9c7e8

See more details on using hashes here.

File details

Details for the file floydr-1.0.4-py3-none-any.whl.

File metadata

  • Download URL: floydr-1.0.4-py3-none-any.whl
  • Upload date:
  • Size: 16.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for floydr-1.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 0f574f4617db1a2127553b3b269a7d803a59897a7ac1cbba71264b59a088f9b4
MD5 380df0a9d37d16f8edb9e08fcd53ef07
BLAKE2b-256 c5672fda48dfea8a590cf9d6ac3af40f0753484070031a421a9146dec8ea7a24

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page