Skip to main content

openui-forge-python

OpenUI generative UI with Python FastAPI backend. OpenAI and Anthropic SDK variants.

معلومات المصدر

المستودع
OthmanAdi/openui-forge
آخر نشاط في المصدر
٢٠ يونيو ٢٠٢٦ في ٠٢:٢٤
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٤
التفرعات
١

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
openui-forge-python
description
OpenUI generative UI with Python FastAPI backend. OpenAI and Anthropic SDK variants.
version
1.2.0
author
OthmanAdi
# OpenUI Forge — Python Build generative UI apps with a React frontend + Python FastAPI backend. Streams OpenAI-compatible NDJSON. ## Activation Triggers - "openui python", "openui fastapi", "openui flask" - "generative ui python", "python streaming ui backend" ## Prerequisites - Node.js >= 22 (24 LTS recommended) + React >= 18.3.1 (19+ recommended) (frontend) - Python >= 3.10 (backend) - `OPENAI_API_KEY` or `ANTHROPIC_API_KEY` set ## Quick Start 1. Create the React frontend and install OpenUI deps: ```bash npm install @openuidev/react-ui @openuidev/react-headless @openuidev/react-lang lucide-react zod ``` 2. Generate the system prompt from your component library: ```bash npx @openuidev/cli generate ./src/lib/library.ts --out backend/system-prompt.txt ``` 3. Set up the Python backend (see Full Code below) 4. Run both: frontend on `:3000`, backend on `:8000` ## Full Code ### Backend: `backend/requirements.txt` ``` fastapi>=0.115.0 uvicorn>=0.24.0 openai>=2.0 anthropic>=0.111.0 python-dotenv>=1.0.0 ``` > The Python >= 3.10 floor comes from fastapi/uvicorn/python-dotenv; openai and anthropic themselves need only Python 3.9. ### Backend (OpenAI): `backend/main.py` ```python import os from pathlib import Path from dotenv import load_dotenv from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import StreamingResponse from openai import AsyncOpenAI load_dotenv() app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:3000"], allow_methods=["POST"], allow_headers=["*"], ) # AsyncOpenAI keeps the request from blocking the event loop during streaming. client = AsyncOpenAI() SYSTEM_PROMPT = Path("system-prompt.txt").read_text() @app.post("/api/chat") async def chat(request: Request): body = await request.json() messages = [{"role": "system", "content": SYSTEM_PROMPT}] + body["messages"] async def generate(): response = await client.chat.completions.create( model=os.getenv("OPENAI_MODEL", "gpt-5.5"), stream=True, messages=messages, ) async for chunk in response: data = chunk.model_dump_json() yield f"data: {data}\n\n" yield "data: [DONE]\n\n" return StreamingResponse(generate(), media_type="text/event-stream") ``` ### Backend (Anthropic variant): `backend/main_anthropic.py` ```python import os, json, time from pathlib import Path from dotenv import load_dotenv from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import StreamingResponse from anthropic import AsyncAnthropic load_dotenv() app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:3000"], allow_methods=["POST"], allow_headers=["*"], ) # AsyncAnthropic mirrors AsyncOpenAI so the stream does not block the loop. client = AsyncAnthropic() SYSTEM_PROMPT = Path("system-prompt.txt").read_text() @app.post("/api/chat") async def chat(request: Request): body = await request.json() stream_id = f"chatcmpl-{int(time.time())}" async def generate(): async with client.messages.stream( model=os.getenv("ANTHROPIC_MODEL", "claude-sonnet-4-6"), max_tokens=4096, system=SYSTEM_PROMPT, messages=body["messages"], ) as stream: async for text in stream.text_stream: chunk = {"id": stream_id, "object": "chat.completion.chunk", "choices": [{"index": 0, "delta": {"content": text}, "finish_reason": None}]} yield f"data: {json.dumps(chunk)}\n\n" done = {"id": stream_id, "object": "chat.completion.chunk", "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]} yield f"data: {json.dumps(done)}\n\n" yield "data: [DONE]\n\n" return StreamingResponse(generate(), media_type="text/event-stream") ``` ### Frontend: `app/chat/page.tsx` (or `src/Chat.tsx` for Vite) ```tsx "use client"; import { FullScreen } from "@openuidev/react-ui"; import { openuiChatLibrary } from "@openuidev/react-ui/genui-lib"; import { openAIAdapter, openAIMessageFormat, } from "@openuidev/react-headless"; export default function ChatPage() { return ( <FullScreen componentLibrary={openuiChatLibrary} streamProtocol={openAIAdapter()} messageFormat={openAIMessageFormat} apiUrl="http://localhost:8000/api/chat" /> ); } ``` > The Python backend emits SSE (`data: {json}\n\n`). Pair it with `openAIAdapter()` on the frontend. `openAIReadableStreamAdapter()` is for NDJSON (no `data:` prefix) and will silently produce no output here. ## System Prompt Generation Generate once, copy to backend directory: ```bash npx @openuidev/cli generate ./src/lib/library.ts --out backend/system-prompt.txt ``` Regenerate after every component change. ## Validation Checklist - [ ] `system-prompt.txt` exists in the backend directory - [ ] CORS allows the frontend origin - [ ] Backend streams `data: {json}\n\n` lines with OpenAI chunk format - [ ] Final chunk has `finish_reason: "stop"` followed by `data: [DONE]` - [ ] Frontend `apiUrl` points to the correct backend URL - [ ] Frontend uses `streamProtocol={openAIAdapter()}` and `openAIMessageFormat` - [ ] `componentLibrary={openuiChatLibrary}` prop passed to `FullScreen` - [ ] CSS import in root layout (`@openuidev/react-ui/components.css`) - [ ] Run backend: `uvicorn main:app --reload --port 8000` ## Error Patterns | Error | Cause | Fix | |-------|-------|-----| | CORS blocked | Frontend origin not allowed | Add origin to `allow_origins` list | | Connection refused | Backend not running | Start with `uvicorn main:app --port 8000` | | FileNotFoundError | system-prompt.txt missing | Run the CLI generate command | | Stream not rendering | Backend not sending SSE format | Ensure `data: ` prefix and `\n\n` after each chunk | | 422 Unprocessable Entity | Request body missing `messages` | Check frontend sends `{ messages: [...] }` |
عرض على GitHub