floydr


Namefloydr JSON
Version 1.0.5 PyPI version JSON
download
home_pagehttps://github.com/floydr-framework/floydr
SummaryA zero-boilerplate framework for building interactive ChatGPT widgets
upload_time2025-10-15 08:53:18
maintainerNone
docs_urlNone
authorFloydr Team
requires_python>=3.11
licenseMIT
keywords chatgpt widgets mcp framework react
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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"
}
        
Elapsed time: 0.55311s