Compare commits
6 Commits
75d5cfbc62
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28bd9d3475 | ||
|
|
18f04d5f97 | ||
|
|
a2d64d1276 | ||
|
|
f233d39d75 | ||
|
|
a1a03ad536 | ||
|
|
9b537ea0dc |
4
.env.example
Normal file
4
.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
# OpenAI 兼容 API 配置
|
||||
OPENAI_API_KEY='your-api-key-here'
|
||||
OPENAI_BASE_URL='https://api.openai.com/v1'
|
||||
OPENAI_MODEL='gpt-4o'
|
||||
46
.gitignore
vendored
Normal file
46
.gitignore
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
|
||||
# IDEs
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.13
|
||||
38
Dockerfile
Normal file
38
Dockerfile
Normal file
@@ -0,0 +1,38 @@
|
||||
# 使用官方Python运行时作为基础镜像
|
||||
FROM 172.16.102.3:30648/library/python:3.13-slim
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 设置环境变量
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
# 配置uv使用阿里云源
|
||||
ENV UV_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/
|
||||
ENV UV_EXTRA_INDEX_URL=https://pypi.org/simple/
|
||||
|
||||
RUN python -m pip install --upgrade pip setuptools wheel --index-url https://mirrors.aliyun.com/pypi/simple/
|
||||
|
||||
# 安装uv
|
||||
RUN pip install uv --index-url https://mirrors.aliyun.com/pypi/simple/
|
||||
|
||||
# 复制依赖文件
|
||||
COPY pyproject.toml ./
|
||||
|
||||
# 安装依赖
|
||||
RUN uv sync
|
||||
|
||||
# 复制应用代码
|
||||
COPY . .
|
||||
|
||||
# 创建非root用户
|
||||
RUN adduser --disabled-password --gecos '' appuser
|
||||
RUN chown -R appuser:appuser /app
|
||||
USER appuser
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8000
|
||||
|
||||
# 启动命令
|
||||
CMD ["uv", "run","python", "app.py"]
|
||||
61
README.md
61
README.md
@@ -1,3 +1,60 @@
|
||||
# web-page-builder
|
||||
# 网页应用生成器
|
||||
|
||||
一个基于大模型的网页应用生成器,通过自然语言描述自动生成可运行的 React 或 HTML 代码。
|
||||
一个基于大模型的网页应用生成器,通过自然语言描述自动生成可运行的 React 或 HTML 代码。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **自然语言生成代码**:输入需求描述,自动生成完整的网页应用代码
|
||||
- **支持 React / HTML**:默认使用 React + Tailwind CSS,也可按需生成原生 HTML
|
||||
- **内置代码沙箱**:实时预览生成的网页效果
|
||||
- **代码下载**:支持一键下载生成的源代码
|
||||
- **对话历史**:支持多轮对话,持续迭代优化生成的页面
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Python 3.13+
|
||||
- Gradio
|
||||
- ModelScope Studio
|
||||
- OpenAI API / OpenRouter
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 本地运行
|
||||
|
||||
1. 克隆仓库并进入目录
|
||||
2. 配置环境变量(复制 `.env` 并填写):
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
3. 使用 uv 安装依赖并启动:
|
||||
```bash
|
||||
uv run python app.py
|
||||
```
|
||||
4. 打开浏览器访问 `http://localhost:7860`
|
||||
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
.
|
||||
├── app.py # Gradio 应用主入口
|
||||
├── config.py # 配置与系统提示词
|
||||
├── logo.png # 应用 Logo
|
||||
├── pyproject.toml # Python 项目配置
|
||||
├── Dockerfile # 容器镜像构建
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
在 `.env` 文件中配置以下环境变量:
|
||||
|
||||
| 变量名 | 说明 |
|
||||
|--------|------|
|
||||
| `OPENAI_API_KEY` | API 密钥 |
|
||||
| `OPENAI_MODEL` | 使用的模型名称 |
|
||||
| `OPENAI_BASE_URL` | API 基础地址 |
|
||||
|
||||
## 许可证
|
||||
|
||||
Apache-2.0
|
||||
|
||||
469
app.py
Normal file
469
app.py
Normal file
@@ -0,0 +1,469 @@
|
||||
import re
|
||||
|
||||
import gradio as gr
|
||||
import modelscope_studio.components.antd as antd
|
||||
import modelscope_studio.components.base as ms
|
||||
import modelscope_studio.components.pro as pro
|
||||
from openai import OpenAI
|
||||
from config import API_KEY, MODEL, SYSTEM_PROMPT, ENDPOINT, EXAMPLES, DEFAULT_LOCALE, DEFAULT_THEME
|
||||
|
||||
client = OpenAI(api_key=API_KEY, base_url=ENDPOINT)
|
||||
|
||||
react_imports = {
|
||||
"lucide-react": "https://esm.sh/lucide-react@0.525.0",
|
||||
"recharts": "https://esm.sh/recharts@3.1.0",
|
||||
"framer-motion": "https://esm.sh/framer-motion@12.23.6",
|
||||
"matter-js": "https://esm.sh/matter-js@0.20.0",
|
||||
"p5": "https://esm.sh/p5@2.0.3",
|
||||
"konva": "https://esm.sh/konva@9.3.22",
|
||||
"react-konva": "https://esm.sh/react-konva@19.0.7",
|
||||
"three": "https://esm.sh/three@0.178.0",
|
||||
"@react-three/fiber": "https://esm.sh/@react-three/fiber@9.2.0",
|
||||
"@react-three/drei": "https://esm.sh/@react-three/drei@10.5.2",
|
||||
"@tailwindcss/browser": "https://esm.sh/@tailwindcss/browser@4.1.11",
|
||||
"react": "https://esm.sh/react@^19.0.0",
|
||||
"react/": "https://esm.sh/react@^19.0.0/",
|
||||
"react-dom": "https://esm.sh/react-dom@^19.0.0",
|
||||
"react-dom/": "https://esm.sh/react-dom@^19.0.0/"
|
||||
}
|
||||
|
||||
|
||||
class GradioEvents:
|
||||
|
||||
@staticmethod
|
||||
def generate_code(input_value, system_prompt_input_value, state_value):
|
||||
|
||||
def get_generated_files(text):
|
||||
patterns = {
|
||||
'html': r'```html\n(.+?)\n```',
|
||||
'jsx': r'```jsx\n(.+?)\n```',
|
||||
'tsx': r'```tsx\n(.+?)\n```',
|
||||
}
|
||||
result = {}
|
||||
|
||||
for ext, pattern in patterns.items():
|
||||
matches = re.findall(pattern, text, re.DOTALL)
|
||||
if matches:
|
||||
content = '\n'.join(matches).strip()
|
||||
result[f'index.{ext}'] = content
|
||||
|
||||
if len(result) == 0:
|
||||
result["index.html"] = text.strip()
|
||||
return result
|
||||
|
||||
yield {
|
||||
output_loading: gr.update(spinning=True),
|
||||
state_tab: gr.update(active_key="loading"),
|
||||
output: gr.update(value=None)
|
||||
}
|
||||
|
||||
if input_value is None:
|
||||
input_value = ''
|
||||
|
||||
# Keep only conversational turns in history; inject a single system
|
||||
# prompt at request time so providers that require strict ordering work.
|
||||
history = [
|
||||
message for message in state_value["history"]
|
||||
if message.get("role") in ("user", "assistant")
|
||||
]
|
||||
messages = [{
|
||||
'role': "system",
|
||||
"content": SYSTEM_PROMPT
|
||||
# 'content': system_prompt_input_value
|
||||
}] + history
|
||||
|
||||
current_user_message = {'role': "user", 'content': input_value}
|
||||
messages.append(current_user_message)
|
||||
|
||||
generator = client.chat.completions.create(
|
||||
model=MODEL,
|
||||
messages=messages,
|
||||
stream=True,
|
||||
extra_body={"enable_thinking": False},
|
||||
)
|
||||
response = ""
|
||||
for chunk in generator:
|
||||
choice = chunk.choices[0]
|
||||
content = choice.delta.content or ""
|
||||
if content:
|
||||
response += content
|
||||
if choice.finish_reason == 'stop':
|
||||
state_value["history"] = history + [current_user_message, {
|
||||
'role': "assistant",
|
||||
'content': response
|
||||
}]
|
||||
generated_files = get_generated_files(response)
|
||||
react_code = generated_files.get(
|
||||
"index.tsx") or generated_files.get("index.jsx")
|
||||
html_code = generated_files.get("index.html")
|
||||
# Completed
|
||||
yield {
|
||||
output:
|
||||
gr.update(value=response),
|
||||
download_content:
|
||||
gr.update(value=react_code or html_code),
|
||||
state_tab:
|
||||
gr.update(active_key="render"),
|
||||
output_loading:
|
||||
gr.update(spinning=False),
|
||||
sandbox:
|
||||
gr.update(
|
||||
template="react" if react_code else "html",
|
||||
imports=react_imports if react_code else {},
|
||||
value={
|
||||
"./index.tsx": """import Demo from './demo.tsx'
|
||||
import "@tailwindcss/browser"
|
||||
|
||||
export default Demo
|
||||
""",
|
||||
"./demo.tsx": react_code
|
||||
} if react_code else {"./index.html": html_code}),
|
||||
state:
|
||||
gr.update(value=state_value)
|
||||
}
|
||||
|
||||
else:
|
||||
# Generating
|
||||
yield {
|
||||
output: gr.update(value=response),
|
||||
output_loading: gr.update(spinning=False),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def select_example(example: dict):
|
||||
return lambda: gr.update(value=example["description"])
|
||||
|
||||
@staticmethod
|
||||
def close_modal():
|
||||
return gr.update(open=False)
|
||||
|
||||
@staticmethod
|
||||
def open_modal():
|
||||
return gr.update(open=True)
|
||||
|
||||
@staticmethod
|
||||
def disable_btns(btns: list):
|
||||
return lambda: [gr.update(disabled=True) for _ in btns]
|
||||
|
||||
@staticmethod
|
||||
def enable_btns(btns: list):
|
||||
return lambda: [gr.update(disabled=False) for _ in btns]
|
||||
|
||||
@staticmethod
|
||||
def update_system_prompt(system_prompt_input_value, state_value):
|
||||
state_value["system_prompt"] = system_prompt_input_value
|
||||
return gr.update(value=state_value)
|
||||
|
||||
@staticmethod
|
||||
def reset_system_prompt(state_value):
|
||||
return gr.update(value=state_value["system_prompt"])
|
||||
|
||||
@staticmethod
|
||||
def render_history(statue_value):
|
||||
return gr.update(value=statue_value["history"])
|
||||
|
||||
@staticmethod
|
||||
def clear_history(state_value):
|
||||
gr.Success("历史记录已清空。")
|
||||
state_value["history"] = []
|
||||
return gr.update(value=state_value)
|
||||
|
||||
|
||||
css = """
|
||||
#coder-artifacts .output-empty,.output-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 680px;
|
||||
}
|
||||
|
||||
#coder-artifacts #output-container .ms-gr-ant-tabs-content,.ms-gr-ant-tabs-tabpane {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#coder-artifacts .output-html {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
min-height: 680px;
|
||||
max-height: 1200px;
|
||||
}
|
||||
|
||||
#coder-artifacts .output-html > iframe {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#coder-artifacts-code-drawer .output-code {
|
||||
flex:1;
|
||||
}
|
||||
#coder-artifacts-code-drawer .output-code .ms-gr-ant-spin-nested-loading {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
with gr.Blocks(css=css) as demo:
|
||||
# Global State
|
||||
state = gr.State({"system_prompt": "", "history": []})
|
||||
with ms.Application(elem_id="coder-artifacts") as app:
|
||||
with antd.ConfigProvider(theme=DEFAULT_THEME, locale=DEFAULT_LOCALE):
|
||||
|
||||
with ms.AutoLoading():
|
||||
with antd.Row(gutter=[32, 12],
|
||||
elem_style=dict(marginTop=20),
|
||||
align="stretch"):
|
||||
# Left Column
|
||||
with antd.Col(span=24, md=8):
|
||||
with antd.Flex(vertical=True, gap="middle", wrap=True):
|
||||
with antd.Flex(justify="center",
|
||||
align="center",
|
||||
vertical=True,
|
||||
gap="middle"):
|
||||
antd.Image(
|
||||
"logo.png",
|
||||
width=200,
|
||||
preview=False)
|
||||
antd.Typography.Title(
|
||||
"网页应用生成器",
|
||||
level=1,
|
||||
elem_style=dict(fontSize=24))
|
||||
# Input
|
||||
input = antd.Input.Textarea(
|
||||
size="large",
|
||||
allow_clear=True,
|
||||
auto_size=dict(minRows=2, maxRows=6),
|
||||
placeholder=
|
||||
"请描述你想生成的网页应用",
|
||||
elem_id="input-container")
|
||||
# Input Notes
|
||||
with antd.Flex(justify="space-between"):
|
||||
antd.Typography.Text(
|
||||
"说明:模型支持多轮对话,可通过返回 React 或 HTML 代码生成界面。",
|
||||
strong=True,
|
||||
type="warning")
|
||||
|
||||
tour_btn = antd.Button("使用引导",
|
||||
variant="filled",
|
||||
color="default")
|
||||
# Submit Button
|
||||
submit_btn = antd.Button("提交",
|
||||
type="primary",
|
||||
block=True,
|
||||
size="large",
|
||||
elem_id="submit-btn")
|
||||
|
||||
antd.Divider("设置")
|
||||
|
||||
# Settings Area
|
||||
with antd.Space(size="small",
|
||||
wrap=True,
|
||||
elem_id="settings-area"):
|
||||
# system_prompt_btn = antd.Button(
|
||||
# "⚙️ Set System Prompt", type="default")
|
||||
history_btn = antd.Button(
|
||||
"📜 历史记录",
|
||||
type="default",
|
||||
elem_id="history-btn",
|
||||
)
|
||||
cleat_history_btn = antd.Button(
|
||||
"🧹 清空历史", danger=True)
|
||||
|
||||
antd.Divider("示例")
|
||||
|
||||
# Examples
|
||||
with antd.Flex(gap="small", wrap=True):
|
||||
for example in EXAMPLES:
|
||||
with antd.Card(
|
||||
elem_style=dict(
|
||||
flex="1 1 fit-content"),
|
||||
hoverable=True) as example_card:
|
||||
antd.Card.Meta(
|
||||
title=example['title'],
|
||||
description=example['description'])
|
||||
|
||||
example_card.click(
|
||||
fn=GradioEvents.select_example(
|
||||
example),
|
||||
outputs=[input])
|
||||
|
||||
# Right Column
|
||||
with antd.Col(span=24, md=16):
|
||||
with antd.Card(
|
||||
title="输出",
|
||||
elem_style=dict(height="100%",
|
||||
display="flex",
|
||||
flexDirection="column"),
|
||||
styles=dict(body=dict(height=0, flex=1)),
|
||||
elem_id="output-container"):
|
||||
# Output Container Extra
|
||||
with ms.Slot("extra"):
|
||||
with ms.Div(elem_id="output-container-extra"):
|
||||
with antd.Button(
|
||||
"下载代码",
|
||||
type="link",
|
||||
href_target="_blank",
|
||||
disabled=True,
|
||||
) as download_btn:
|
||||
with ms.Slot("icon"):
|
||||
antd.Icon("DownloadOutlined")
|
||||
download_content = gr.Text(visible=False)
|
||||
|
||||
view_code_btn = antd.Button(
|
||||
"🧑💻 查看代码", type="primary")
|
||||
# Output Content
|
||||
with antd.Tabs(
|
||||
elem_style=dict(height="100%"),
|
||||
active_key="empty",
|
||||
render_tab_bar="() => null") as state_tab:
|
||||
with antd.Tabs.Item(key="empty"):
|
||||
antd.Empty(
|
||||
description=
|
||||
"输入你的需求以生成代码",
|
||||
elem_classes="output-empty")
|
||||
with antd.Tabs.Item(key="loading"):
|
||||
with antd.Spin(
|
||||
tip="正在生成代码...",
|
||||
size="large",
|
||||
elem_classes="output-loading"):
|
||||
# placeholder
|
||||
ms.Div()
|
||||
with antd.Tabs.Item(key="render"):
|
||||
sandbox = pro.WebSandbox(
|
||||
height="100%",
|
||||
elem_classes="output-html",
|
||||
template="html",
|
||||
)
|
||||
|
||||
# Modals and Drawers
|
||||
with antd.Modal(open=False,
|
||||
title="系统提示词",
|
||||
width="800px") as system_prompt_modal:
|
||||
system_prompt_input = antd.Input.Textarea(
|
||||
# SYSTEM_PROMPT,
|
||||
value="",
|
||||
size="large",
|
||||
placeholder="请输入系统提示词",
|
||||
allow_clear=True,
|
||||
auto_size=dict(minRows=4, maxRows=14))
|
||||
|
||||
with antd.Drawer(
|
||||
open=False,
|
||||
title="输出代码",
|
||||
placement="right",
|
||||
get_container=
|
||||
"() => document.querySelector('.gradio-container')",
|
||||
elem_id="coder-artifacts-code-drawer",
|
||||
styles=dict(
|
||||
body=dict(display="flex",
|
||||
flexDirection="column-reverse")),
|
||||
width="750px") as output_code_drawer:
|
||||
with ms.Div(elem_classes="output-code"):
|
||||
with antd.Spin(spinning=False) as output_loading:
|
||||
output = ms.Markdown()
|
||||
|
||||
with antd.Drawer(
|
||||
open=False,
|
||||
title="对话历史",
|
||||
placement="left",
|
||||
get_container=
|
||||
"() => document.querySelector('.gradio-container')",
|
||||
width="750px") as history_drawer:
|
||||
history_output = gr.Chatbot(
|
||||
show_label=False,
|
||||
type="messages",
|
||||
height='100%',
|
||||
elem_classes="history_chatbot")
|
||||
# Tour
|
||||
with antd.Tour(open=False) as usage_tour:
|
||||
antd.Tour.Step(
|
||||
title="第 1 步",
|
||||
description=
|
||||
"描述你想创建的网页应用。",
|
||||
get_target=
|
||||
"() => document.querySelector('#input-container')")
|
||||
antd.Tour.Step(
|
||||
title="第 2 步",
|
||||
description="点击提交按钮。",
|
||||
get_target=
|
||||
"() => document.querySelector('#submit-btn')")
|
||||
antd.Tour.Step(
|
||||
title="第 3 步",
|
||||
description="等待生成结果。",
|
||||
get_target=
|
||||
"() => document.querySelector('#output-container')"
|
||||
)
|
||||
antd.Tour.Step(
|
||||
title="第 4 步",
|
||||
description=
|
||||
"可在这里下载生成代码或查看代码内容。",
|
||||
get_target=
|
||||
"() => document.querySelector('#output-container-extra')"
|
||||
)
|
||||
antd.Tour.Step(
|
||||
title="其他设置",
|
||||
description="你可以在这里查看和管理对话历史。",
|
||||
get_target=
|
||||
"() => document.querySelector('#settings-area')")
|
||||
# Event Handler
|
||||
gr.on(fn=GradioEvents.close_modal,
|
||||
triggers=[usage_tour.close, usage_tour.finish],
|
||||
outputs=[usage_tour])
|
||||
tour_btn.click(fn=GradioEvents.open_modal, outputs=[usage_tour])
|
||||
|
||||
# system_prompt_btn.click(fn=GradioEvents.open_modal,
|
||||
# outputs=[system_prompt_modal])
|
||||
|
||||
system_prompt_modal.ok(GradioEvents.update_system_prompt,
|
||||
inputs=[system_prompt_input, state],
|
||||
outputs=[state]).then(fn=GradioEvents.close_modal,
|
||||
outputs=[system_prompt_modal])
|
||||
|
||||
system_prompt_modal.cancel(GradioEvents.close_modal,
|
||||
outputs=[system_prompt_modal]).then(
|
||||
fn=GradioEvents.reset_system_prompt,
|
||||
inputs=[state],
|
||||
outputs=[system_prompt_input])
|
||||
output_code_drawer.close(fn=GradioEvents.close_modal,
|
||||
outputs=[output_code_drawer])
|
||||
cleat_history_btn.click(fn=GradioEvents.clear_history,
|
||||
inputs=[state],
|
||||
outputs=[state])
|
||||
history_btn.click(fn=GradioEvents.open_modal,
|
||||
outputs=[history_drawer
|
||||
]).then(fn=GradioEvents.render_history,
|
||||
inputs=[state],
|
||||
outputs=[history_output])
|
||||
history_drawer.close(fn=GradioEvents.close_modal, outputs=[history_drawer])
|
||||
|
||||
download_btn.click(fn=None,
|
||||
inputs=[download_content],
|
||||
js="""(content) => {
|
||||
const blob = new Blob([content], { type: 'text/plain' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = 'output.txt'
|
||||
a.click()
|
||||
}""")
|
||||
view_code_btn.click(fn=GradioEvents.open_modal,
|
||||
outputs=[output_code_drawer])
|
||||
submit_btn.click(
|
||||
fn=GradioEvents.open_modal,
|
||||
outputs=[output_code_drawer],
|
||||
).then(fn=GradioEvents.disable_btns([submit_btn, download_btn]),
|
||||
outputs=[submit_btn, download_btn]).then(
|
||||
fn=GradioEvents.generate_code,
|
||||
inputs=[input, system_prompt_input, state],
|
||||
outputs=[
|
||||
output, state_tab, sandbox, download_content,
|
||||
output_loading, state
|
||||
]).then(fn=GradioEvents.enable_btns([submit_btn, download_btn]),
|
||||
outputs=[submit_btn, download_btn
|
||||
]).then(fn=GradioEvents.close_modal,
|
||||
outputs=[output_code_drawer])
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.queue(default_concurrency_limit=100,
|
||||
max_size=100).launch(server_name="0.0.0.0", server_port=7860, ssr_mode=False, max_threads=100)
|
||||
127
config.py
Normal file
127
config.py
Normal file
@@ -0,0 +1,127 @@
|
||||
import os
|
||||
|
||||
def _load_dotenv_file() -> None:
|
||||
env_path = os.path.join(os.path.dirname(__file__), ".env")
|
||||
if not os.path.exists(env_path):
|
||||
return
|
||||
|
||||
with open(env_path, "r", encoding="utf-8") as file:
|
||||
for raw_line in file:
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
if (value.startswith("'") and value.endswith("'")) or (
|
||||
value.startswith('"') and value.endswith('"')
|
||||
):
|
||||
value = value[1:-1]
|
||||
|
||||
# Keep existing env vars highest priority.
|
||||
os.environ.setdefault(key, value)
|
||||
|
||||
|
||||
_load_dotenv_file()
|
||||
|
||||
API_KEY = os.getenv('OPENAI_API_KEY')
|
||||
|
||||
MODEL = os.getenv('OPENAI_MODEL')
|
||||
|
||||
# MODEL = "qwen/qwen3-coder-plus"
|
||||
|
||||
ENDPOINT = os.getenv('OPENAI_BASE_URL')
|
||||
|
||||
SYSTEM_PROMPT = """You are an expert on frontend design, you will always respond to web design tasks.
|
||||
Your task is to create a website according to the user's request using either native HTML or React framework.
|
||||
When choosing implementation framework, you should follow these rules:
|
||||
[Implementation Rules]
|
||||
1. You should use React by default.
|
||||
2. When the user requires HTML, choose HTML to implement the request.
|
||||
3. If the user requires a library that is not installed in current react environment, please use HTML and tell the user the reason.
|
||||
4. After choosing the implementation framework, please follow the corresponding instruction.
|
||||
|
||||
|
||||
[Critical Output Rule]
|
||||
NO MATTER WHAT, your response must contain ONLY the runnable code in a single code block. Do NOT include introductions, explanations, summaries, markdown bullet points, or any conversational text outside the code block.
|
||||
|
||||
[HTML Instruction]
|
||||
All code is written in a single code block to form a complete code file for display, without separating HTML and JavaScript code. An artifact refers to a runnable complete code snippet, you prefer to integrate and output such complete runnable code rather than breaking it down into several code blocks. For certain types of code, they can render graphical interfaces in a UI window. After generation, please check the code execution again to ensure there are no errors in the output.
|
||||
Do not use localStorage as it is not supported by current environment.
|
||||
Output only the HTML code, without any additional descriptive text.
|
||||
|
||||
[React Instruction]
|
||||
Your task is to create a website using a SINGLE static React JSX file, which exports a default component. This code will go directly into the App.jsx file and will be used to render the website.
|
||||
|
||||
## Common Design Principles
|
||||
|
||||
Regardless of the technology used, follow these principles for all designs:
|
||||
|
||||
### General Design Guidelines:
|
||||
- Create a stunning, contemporary, and highly functional website based on the user's request
|
||||
- Implement a cohesive design language throughout the entire website/application
|
||||
- Choose a carefully selected, harmonious color palette that enhances the overall aesthetic
|
||||
- Create a clear visual hierarchy with proper typography to improve readability
|
||||
- Incorporate subtle animations and transitions to add polish and improve user experience
|
||||
- Ensure proper spacing and alignment using appropriate layout techniques
|
||||
- Implement responsive design principles to ensure the website looks great on all device sizes
|
||||
- Use modern UI patterns like cards, gradients, and subtle shadows to add depth and visual interest
|
||||
- Incorporate whitespace effectively to create a clean, uncluttered design
|
||||
- For images, use placeholder images from services like https://placehold.co/
|
||||
|
||||
## React Design Guidelines
|
||||
|
||||
### Implementation Requirements:
|
||||
- Ensure the React app is a single page application
|
||||
- DO NOT include any external libraries, frameworks, or dependencies outside of what is already installed
|
||||
- Utilize TailwindCSS for styling, focusing on creating a visually appealing and responsive layout
|
||||
- Avoid using arbitrary values (e.g., `h-[600px]`). Stick to Tailwind's predefined classes for consistency
|
||||
- Use mock data instead of making HTTP requests or API calls to external services
|
||||
- Utilize Tailwind's typography classes to create a clear visual hierarchy and improve readability
|
||||
- Ensure proper spacing and alignment using Tailwind's margin, padding, and flexbox/grid classes
|
||||
- Do not use localStorage as it is not supported by current environment.
|
||||
|
||||
### Installed Libraries:
|
||||
You can use these installed libraries if required.
|
||||
- **lucide-react**: Lightweight SVG icon library with 1000+ icons. Import as `import { IconName } from "lucide-react"`. Perfect for buttons, navigation, status indicators, and decorative elements.
|
||||
- **recharts**: Declarative charting library built on D3. Import components like `import { LineChart, BarChart } from "recharts"`. Use for data visualization, analytics dashboards, and statistical displays.
|
||||
- **framer-motion**: Production-ready motion library for React. Import as `import { motion } from "framer-motion"`. Use for animations, page transitions, hover effects, and interactive micro-interactions.
|
||||
- **p5.js** : JavaScript library for creative coding and generative art. Usage: import p5 from "p5". Create interactive visuals, animations, sound-driven experiences, and artistic simulations.
|
||||
- **three, @react-three/fiber, @react-three/drei**: 3D graphics library with React renderer and helpers. Import as `import { Canvas } from "@react-three/fiber"` and `import { OrbitControls } from "@react-three/drei"`. Use for 3D scenes, visualizations, and immersive experiences.
|
||||
|
||||
Return ONLY the complete App.jsx code inside a single code block. No explanations, no markdown lists, no intro or outro text."""
|
||||
|
||||
EXAMPLES = [
|
||||
{
|
||||
"title":
|
||||
"动画演示",
|
||||
"description":
|
||||
"用 HTML 制作一个页面,展示小球在旋转超立方体中弹跳的动画。",
|
||||
},
|
||||
{
|
||||
"title": "SVG 图形生成",
|
||||
"description":
|
||||
"帮我生成一个包含 5 只宝可梦细节的 SVG 图像。"
|
||||
},
|
||||
{
|
||||
"title":
|
||||
"创意卡片设计",
|
||||
"description":
|
||||
"""单词 "strawberry" 里有几个 "r"?请做一张可爱的小卡片!"""
|
||||
},
|
||||
{
|
||||
"title":
|
||||
"任务管理应用",
|
||||
"description":
|
||||
"我想要一个待办事项列表,支持添加和删除任务,整体配色希望是紫色。"
|
||||
},
|
||||
]
|
||||
|
||||
DEFAULT_LOCALE = 'zh_CN'
|
||||
|
||||
DEFAULT_THEME = {
|
||||
"token": {
|
||||
"colorPrimary": "#6A57FF",
|
||||
}
|
||||
}
|
||||
13
pyproject.toml
Normal file
13
pyproject.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[project]
|
||||
name = "qwen3-coder-webdev"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"dashscope>=1.24.6",
|
||||
"gradio>=5.47.2",
|
||||
"httpx[socks]",
|
||||
"modelscope-studio>=1.5.0",
|
||||
"openai>=1.109.1",
|
||||
]
|
||||
Reference in New Issue
Block a user