Spaces:
Running
Running
little demo
Browse files- .gitignore +1 -0
- GEMINI.md +107 -92
- README.md +1 -2
- __pycache__/config.cpython-313.pyc +0 -0
- __pycache__/local.cpython-313.pyc +0 -0
- __pycache__/models.cpython-313.pyc +0 -0
- __pycache__/tab_chat.cpython-313.pyc +0 -0
- __pycache__/tab_code.cpython-313.pyc +0 -0
- __pycache__/tab_search.cpython-313.pyc +0 -0
- __pycache__/tab_workflow.cpython-313.pyc +0 -0
- __pycache__/utils.cpython-313.pyc +0 -0
- app.py +54 -36
- config.py +83 -84
- docs/backlog/2025-10-11-18-43-auto-fix-for-code-generator.md +1 -0
- docs/backlog/2025-10-11-19-41-add-local-model-id-mapping.md +1 -0
- docs/backlog/2025-10-11-20-32-chat-interrupt-output.md +46 -0
- docs/requirements/2025-10-11-14-35-fix-chat-model-display-name.md +1 -1
- docs/requirements/2025-10-11-14-37-update-model-descriptions.md +1 -1
- docs/requirements/2025-10-11-14-39-update-chat-example-prompts.md +1 -1
- docs/requirements/2025-10-11-15-08-refactor-chat-examples-to-scenarios.md +1 -1
- docs/requirements/2025-10-11-15-47-add-model-identity-to-chat-output.md +1 -1
- docs/requirements/2025-10-11-16-47-implement-static-page-generation.md +1 -1
- docs/requirements/2025-10-11-16-56-add-code-generation-presets.md +1 -1
- docs/requirements/2025-10-11-16-59-add-fullscreen-preview.md +1 -1
- docs/requirements/2025-10-11-17-12-refactor-code-preview-to-tabs.md +1 -1
- docs/requirements/2025-10-11-17-38-add-floating-island-example.md +1 -1
- docs/requirements/2025-10-11-18-18-add-model-selection-switch.md +1 -1
- docs/requirements/2025-10-11-18-18-display-think-tags-in-source-only.md +1 -1
- docs/requirements/2025-10-11-21-05-add-real-time-generation-status.md +27 -0
- docs/requirements/2025-10-11-21-09-implement-dynamic-gradio-app-generation.md +50 -0
- docs/requirements/2025-10-12-08-30-implement-workflow-tab.md +80 -0
- docs/requirements/2025-10-12-09-05-optimize-workflow-prompts-and-test.md +47 -0
- docs/requirements/2025-10-12-09-20-add-mermaid-workflow-visualization.md +30 -0
- docs/requirements/2025-10-12-22-38-prevent-premature-code-preview-refresh.md +26 -0
- docs/requirements/2025-10-13-10-21-fix-workflow-chat-context.md +33 -0
- docs/requirements/2025-10-13-18-32-update-chat-model-list.md +34 -0
- docs/requirements/2025-10-13-23-39-update-chat-models.md +30 -0
- docs/requirements/2025-10-14-00-40-add-credits.md +13 -0
- docs/uncategorized/development_todo.md +0 -31
- models.py +28 -36
- tab_chat.py +55 -56
- tab_code.py +187 -89
- tab_search.py +15 -16
- tab_workflow.py +120 -61
- utils.py +11 -0
.gitignore
CHANGED
|
@@ -13,3 +13,4 @@ __pycache__/
|
|
| 13 |
*.pyc
|
| 14 |
*.pyo
|
| 15 |
*.pyd
|
|
|
|
|
|
| 13 |
*.pyc
|
| 14 |
*.pyo
|
| 15 |
*.pyd
|
| 16 |
+
__pycache__/
|
GEMINI.md
CHANGED
|
@@ -4,13 +4,17 @@
|
|
| 4 |
|
| 5 |
---
|
| 6 |
|
| 7 |
-
##
|
|
|
|
|
|
|
| 8 |
|
| 9 |
---
|
| 10 |
|
| 11 |
-
##
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
|
| 14 |
|
| 15 |
要启动此 Gradio 应用,请遵循以下步骤:
|
| 16 |
|
|
@@ -27,107 +31,133 @@
|
|
| 27 |
|
| 28 |
3. **验证:** 在浏览器中打开 `http://127.0.0.1:7860` 以确认应用正在运行。
|
| 29 |
|
| 30 |
-
|
| 31 |
|
| 32 |
-
|
| 33 |
|
| 34 |
-
|
| 35 |
-
- 当你提出新需求时,我将首先在 `docs/backlog/` 目录下创建一个 Markdown 文件,作为“需求池”的记录。
|
| 36 |
-
- 文件名格式为 `YYYY-MM-DD-HH-mm-需求简述.md`。
|
| 37 |
-
- 文件内容将包含:需求描述、创建时间、初始状态 `待处理 (Pending)`、验证方式(暂空)、验证结果(暂空)。
|
| 38 |
|
| 39 |
-
|
| 40 |
-
- 当我们决定处理一个需求时,我会基于其文档,提出具体的执行计划。
|
| 41 |
-
- 在你确认计划后,我会将该需求文件从 `docs/backlog/` **移动**到 `docs/requirements/` 目录,并将其状态更新为 `开发中 (In Progress)`。
|
| 42 |
|
| 43 |
-
|
| 44 |
-
- 我会更新 `GEMINI.md` 的“第三部分:详细设计”,以反映新功能的设计细节。
|
| 45 |
|
| 46 |
-
|
| 47 |
-
|
|
|
|
| 48 |
|
| 49 |
-
|
| 50 |
-
- 完成代码编写后,我会重启应用,并**自动刷新你的浏览器以展示最新版本**。
|
| 51 |
-
- 同时,我将更新需求文件,将状态改为 `已完成 (Completed)`,并填入“验证方式”供你参考。
|
| 52 |
|
| 53 |
-
|
| 54 |
-
- 在你完成验证并确认功能符合预期后,我会将需求文件的“验证结果”更新为 `已验证 (Verified)`,至此该需求流程关闭。
|
| 55 |
|
| 56 |
-
|
| 57 |
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
-
|
| 61 |
-
- **复现步骤:** 你进行了哪些具体操作。
|
| 62 |
-
- **预期结果:** 你期望看到什么。
|
| 63 |
-
- **实际结果:** 你实际看到了什么(例如:报错、无响应、界面错乱等)。
|
| 64 |
|
| 65 |
-
|
| 66 |
-
```bash
|
| 67 |
-
# 命令示例
|
| 68 |
-
source .venv/bin/activate && python3 app.py > app.log 2>&1 &
|
| 69 |
-
```
|
| 70 |
|
| 71 |
-
|
| 72 |
|
| 73 |
-
|
| 74 |
-
- 根据日志分析,我会提出具体的修复方案并执行。
|
| 75 |
-
- 修复后,我会重启应用(仍处于日志模式),并**自动刷新你的浏览器**,然后请你再次验证。
|
| 76 |
-
- 如果问题依然存在,我们将重复步骤 3 和 4。
|
| 77 |
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
|
|
|
|
|
|
|
| 83 |
|
| 84 |
-
|
| 85 |
-
- 使用 `git add .` 暂存所有修改。
|
| 86 |
|
| 87 |
-
2.
|
| 88 |
-
- Commit message 应遵循**约定式提交 (Conventional Commits)**规范。
|
| 89 |
-
- 格式为 `<类型>(<范围>): <描述>`,例如:
|
| 90 |
-
- `feat(chat): 添加发送按钮`
|
| 91 |
-
- `fix(models): 修正 Ring 模型流式输出问题`
|
| 92 |
-
- `docs(workflow): 更新调试与验证流程`
|
| 93 |
-
- 常用的类型包括 `feat`, `fix`, `docs`, `style`, `refactor`, `test` 等。
|
| 94 |
|
| 95 |
-
|
| 96 |
-
- **检查现有版本:** 在打标签前,**必须**先运行 `git tag` 查看所有历史版本,以确定下一个正确的版本号。
|
| 97 |
-
- **确定版本号:** 遵循**语义化版本 (Semantic Versioning)**。在 `v1.0.0` 之前,我们主要递增次版本号(如 `v0.4` -> `v0.5`)。
|
| 98 |
-
- **创建附注标签:** 使用 `git tag -a <版本号> -m "<版本摘要>"` 创建一个附注标签,其中摘要应简明扼要地概括该版本的主要变更。例如:
|
| 99 |
-
```bash
|
| 100 |
-
git tag -a v0.5 -m "v0.5: Automate browser verification and formalize debugging process"
|
| 101 |
-
```
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
-
|
| 111 |
|
| 112 |
-
|
| 113 |
|
| 114 |
-
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
-
|
|
|
|
|
|
|
| 118 |
|
| 119 |
- **任务导向:** UI 围绕用户任务(聊天、编码等)组织,而非直接暴露模型。
|
| 120 |
- **品牌明晰:** 在每个功能界面清晰标注“由 Ling/Ring 模型驱动”。
|
| 121 |
- **无缝体验:** 用户无需手动输入 API Token,认证在后端自动完成。
|
| 122 |
- **引导优先:** 提供精心设计的示例,确保用户获得高质量的初次体验。
|
| 123 |
|
| 124 |
-
###
|
| 125 |
|
| 126 |
- **前端/UI:** Gradio `gr.Blocks`
|
| 127 |
- **后端:** Python
|
| 128 |
- **安全:** 所有 API 密钥通过 Hugging Face Space Secrets 管理。
|
| 129 |
|
| 130 |
-
|
| 131 |
|
| 132 |
为兼顾本地开发的便利性、线上部署的安全性与成本效益,项目采用了一种分层配置加载机制:
|
| 133 |
|
|
@@ -137,7 +167,7 @@
|
|
| 137 |
|
| 138 |
此策略确保了开发者在本地可以快速迭代,而线上部署则遵循了安全最佳实践。
|
| 139 |
|
| 140 |
-
###
|
| 141 |
|
| 142 |
```
|
| 143 |
/
|
|
@@ -158,7 +188,7 @@
|
|
| 158 |
└───refs/ # 本地参考资料(Git 忽略)
|
| 159 |
```
|
| 160 |
|
| 161 |
-
###
|
| 162 |
|
| 163 |
1. **`models.py`**: 存放所有与模型对接和交互的逻辑。
|
| 164 |
2. **`app.py`**: 作为应用的统一入口,仅保留最精简的组装和启动逻辑。
|
|
@@ -168,11 +198,11 @@
|
|
| 168 |
|
| 169 |
---
|
| 170 |
|
| 171 |
-
##
|
| 172 |
|
| 173 |
-
|
| 174 |
|
| 175 |
-
### Tab 1: 聊天 (Chat) - `tab_chat.py`
|
| 176 |
|
| 177 |
- **目标:** 提供一个与 Ling 模型进行高质量对话的界面。
|
| 178 |
- **UI 布局:**
|
|
@@ -181,9 +211,10 @@
|
|
| 181 |
- **用户用例:**
|
| 182 |
1. 用户在输入框中输入问题,按回车提交。
|
| 183 |
2. Ling 模型的响应以流式方式��现在聊天历史中,且每条回复的开头都会以 `**<模型名称>**` 的形式清晰地标识出当前是哪个模型在回答。
|
| 184 |
-
3.
|
|
|
|
| 185 |
|
| 186 |
-
### Tab 2: 代码生成 (Code Generation) - `tab_code.py`
|
| 187 |
|
| 188 |
- **目标:** 利用 Ring 模型,根据用户需求生成代码,并提供实时预览。
|
| 189 |
- **UI 布局:**
|
|
@@ -200,7 +231,7 @@
|
|
| 200 |
6. 用户可以点击“全屏预览”按钮,此时输入和代码区域会隐藏,预览区将放大以提供更沉浸的体验。再次点击可恢复。
|
| 201 |
7. 对于 Gradio 应用,后端会启动一个独立的子进程来运行代码,并将应用界面嵌入到预览区。
|
| 202 |
|
| 203 |
-
### Tab 3: 网页检索 (Web Search) - `tab_search.py`
|
| 204 |
|
| 205 |
- **目标:** 利用 Ring 模型的检索能力,提供精准的网页信息摘要。
|
| 206 |
- **UI 布局:** 单栏居中布局,包含 `gr.Textbox` (输入), `gr.Button` (搜索), `gr.Markdown` (结果)。
|
|
@@ -208,7 +239,7 @@
|
|
| 208 |
1. 用户输入问题(例如:“什么是 Transformer 架构?”)。
|
| 209 |
2. 点击“搜索”后,Ring 模型返回的摘要和来源链接会显示在结果区。
|
| 210 |
|
| 211 |
-
### Tab 4: 工作流执行 (Workflow Execution) - `tab_workflow.py`
|
| 212 |
|
| 213 |
- **目标:** 展示 Ring 模型作为 Agent 执行复杂工作流的能力。
|
| 214 |
- **UI 布局:**
|
|
@@ -218,19 +249,3 @@
|
|
| 218 |
1. 用户输入任务描述(例如:“查找最新的 Llama 3 模型并总结其模型卡片”)。
|
| 219 |
2. Ring 模型生成执行计划并可视化,然后开始执行。
|
| 220 |
3. 右侧面板实时显示执行日志,并在需要时通过聊天机器人向用户请求决策。
|
| 221 |
-
|
| 222 |
-
---
|
| 223 |
-
|
| 224 |
-
## 第四部分:待办事项
|
| 225 |
-
|
| 226 |
-
### 4.1 进行中的任务
|
| 227 |
-
|
| 228 |
-
- [x] **任务: 实现代码生成 Tab (`tab_code.py`)**
|
| 229 |
-
- [x] UI 构建 (`gr.Radio`, `gr.Textbox`, `gr.Button`, `gr.Code`, `gr.HTML`)
|
| 230 |
-
- [x] 后端逻辑 (System Prompts, 子进程管理, `<iframe>` 预览)
|
| 231 |
-
- [x] 应用整合 (在 `app.py` 中装配 Tab)
|
| 232 |
-
- [x] 模块化重构 (将模型调用逻辑移至 `models.py`)
|
| 233 |
-
|
| 234 |
-
### 4.2 待修复的问题
|
| 235 |
-
|
| 236 |
-
- (暂无)
|
|
|
|
| 4 |
|
| 5 |
---
|
| 6 |
|
| 7 |
+
## 第一部分:元信息
|
| 8 |
+
|
| 9 |
+
本文档遵循全局 `GEMINI.md` 的元信息结构,旨在作为项目唯一的“事实来源”。它定义了项目的目标、规范、设计和待办事项,并由我和我的用户共同维护。
|
| 10 |
|
| 11 |
---
|
| 12 |
|
| 13 |
+
## 第二部分:协作流程和规范
|
| 14 |
+
|
| 15 |
+
### 2.1 运行与部署流程
|
| 16 |
|
| 17 |
+
#### 本地运行
|
| 18 |
|
| 19 |
要启动此 Gradio 应用,请遵循以下步骤:
|
| 20 |
|
|
|
|
| 31 |
|
| 32 |
3. **验证:** 在浏览器中打开 `http://127.0.0.1:7860` 以确认应用正在运行。
|
| 33 |
|
| 34 |
+
#### 部署到 Hugging Face Spaces
|
| 35 |
|
| 36 |
+
本项目可以轻松部署到 Hugging Face Spaces。推送代码到 Git 仓库后,Hugging Face 会自动构建并运行应用。应用地址是 `https://huggingface.co/spaces/inclusion/ling-playground`。
|
| 37 |
|
| 38 |
+
### 2.2 自动化测试流程
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
+
**核心方法:以需求文档为准的 Agent 驱动测试**
|
|
|
|
|
|
|
| 41 |
|
| 42 |
+
本项目的测试开发(TDD/BDD)由我,即 Agent,作为测试执行器来驱动。具体方法如下:
|
|
|
|
| 43 |
|
| 44 |
+
1. **测试用例即需求:** 每个需求文档(`docs/requirements/*.md`)中的“验证方式”或“测试用例”部分,就是一份用自然语言描述的端到端测试脚本。
|
| 45 |
+
2. **我来执行测试:** 我会严格按照“验证方式”中的步骤,通过调用我的浏览器自动化工具集(`navigate_page`, `click`, `fill`, `take_snapshot` 等)来模拟用户操作。
|
| 46 |
+
3. **我来断言结果:** 在执行操作后,我会捕获 UI 快照,并在我的思考流程中,通过分析快照内容来判断实际结果是否与预期相符。
|
| 47 |
|
| 48 |
+
这种“以需求文档为准,Agent 驱动执行”的模式,是我在本项目中实践 TDD 的具体方式。
|
|
|
|
|
|
|
| 49 |
|
| 50 |
+
**具体工具流程:**
|
|
|
|
| 51 |
|
| 52 |
+
当需要验证应用核心功能时,我将遵循以下自动化流程:
|
| 53 |
|
| 54 |
+
1. **启动应用:** 使用 `run_shell_command` 在后台启动 Gradio 应用。
|
| 55 |
+
2. **导航与验证:** 使用 `navigate_page` 打开应用 URL (`http://127.0.0.1:7860`),并使用 `take_snapshot` 截取页面快照,以编程方式验证 UI 是否正确加载。**(重要规范:必须使用浏览器自动化工具进行验证,禁止使用 `web_fetch` 等非浏览器工具检查应用状态。)**
|
| 56 |
+
3. **交互操作:** 使用 `fill` 和 `click` 等工具模拟用户操作,例如在聊天框中输入文本并点击发送。
|
| 57 |
+
4. **结果断言:** 再次使用 `take_snapshot` 捕获操作后的界面状态,并检查其内容是否符合预期(例如,聊天记录中是否出现了模型的回复)。
|
| 58 |
|
| 59 |
+
此流程确保了关键用户路径的可靠性。
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
+
#### 代码生成功能测试示例
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
+
对于像代码生成这样涉及流式输出的动态界面,必须采用更严谨的测试方法来确保可靠性。
|
| 64 |
|
| 65 |
+
1. **严格的“操作 -> 快照”模式:** 为避免“陈旧快照 (stale snapshot)”错误,**每次**交互操作(如 `click`, `fill`)后都**必须**立即调用 `take_snapshot` 来获取最新的 UI 状态和元素 `uid`。
|
|
|
|
|
|
|
|
|
|
| 66 |
|
| 67 |
+
2. **输入与启动:**
|
| 68 |
+
- 执行“环境重置流程”。
|
| 69 |
+
- 导航至应用,获取初始快照,点击“代码生成”标签页。
|
| 70 |
+
- 获取新快照,定位到输入框并填入需求,再定位到“生成代码”按钮并点击。
|
| 71 |
|
| 72 |
+
3. **监控生成过程:**
|
| 73 |
+
- 点击“生成代码”后,立即获取新快照,并点击切换到“生成的源代码”标签页。
|
| 74 |
+
- 进入一个监控循环,以编程方式判断生成是否结束:
|
| 75 |
+
- 获取一次快照,记录下源代码区域的内容。
|
| 76 |
+
- 等待一个固定的时间间隔(例如 5 秒)。
|
| 77 |
+
- 再次获取快照,获取新的源代码内容。
|
| 78 |
+
- **比较两次获取的内容。如果内容完全相同,则判定代码已停止生成,跳出循环。** 否则,继续循环。
|
| 79 |
|
| 80 |
+
4. **验证预览:**
|
| 81 |
+
- 跳出循环后,获取最终快照,点击“实时预览”标签页。
|
| 82 |
+
- 最后,请求人工介入,以视觉方式确认最终渲染效果是否符合预期。
|
| 83 |
|
| 84 |
+
这个流程通过精确的状态管理和内容比较,实现了对动态生成过程的可靠自动化测试。
|
|
|
|
| 85 |
|
| 86 |
+
### 2.3 异常处理与重置流程
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
+
当自动化流程遇到问题或需要一个干净的环境时,我将执行以下标准的“重置”程序:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
+
1. **清理浏览器:** 将所有打开的浏览器页面导航至 `about:blank`。
|
| 91 |
+
2. **终止服务:** 使用 `ps aux | grep app.py` 查找并使用 `kill` 命令终止所有正在运行的 `app.py` 应用进程。
|
| 92 |
+
3. **重启服务:** 在确认环境干净后,重新在后台启动应用。
|
| 93 |
+
4. **等待加载:** 等待 5 秒,以确保服务完全初始化。
|
| 94 |
+
5. **重新访问:** 导航到应用 URL (`http://127.0.0.1:7860`) 并进行验证。
|
| 95 |
|
| 96 |
+
此流程是所有自动化测试和问题排查的起点。
|
| 97 |
+
|
| 98 |
+
### 2.4 分支与提交规范 (主干工作流)
|
| 99 |
+
|
| 100 |
+
为确保开发过程的敏捷、可靠与透明,我将遵循一套以需求为驱动、以 Git 为记录的自动化主干工作流。此流程将我的角色从被动执行者转变为能够主动推进项目迭代的合作伙伴。
|
| 101 |
+
|
| 102 |
+
**核心原则:用户指令优先**
|
| 103 |
+
在任何时候,您的直接指令都拥有最高优先级,可以中断或覆盖此自动化流程。
|
| 104 |
+
|
| 105 |
+
**工作流详解:**
|
| 106 |
+
|
| 107 |
+
**1. 智能调度与规划**
|
| 108 |
+
- **领取任务:** 我将自动扫描 `docs/backlog/`,并根据**重要性 (Priority)** 字段来确定任务的执行顺序。
|
| 109 |
+
- **重要性等级:** 需求文档中应包含一个 `重要性` 字段,其值可以是 `高 (High)`、`中 (Medium)` 或 `低 (Low)`。
|
| 110 |
+
- **调度逻辑:** 我会优先处理 `高` 重要性的任务,然后是 `中` 等重要性,最后是 `低` 重要性。在同一重要性等级内,我将按时间顺序选取最旧的需求。如果一个需求没有明确的重要性,它将被当作 `中` 等处理。
|
| 111 |
+
- **需求澄清:** 在正式开始前,我会分析需求文档。如果发现任何模糊、歧义或信息不足之处,我将**暂停工作流并主动向您提问**。
|
| 112 |
+
- **TDD - 定义成功:** 我会为需求定义清晰的验收标准,并尽可能**优先编写自动化测试用例**。这是我后续开发和验证的“靶子”。如果需求文档中缺少“验证方式”,我将暂停并请求用户提供,绝不跳过验证环节。
|
| 113 |
+
- **文档迁移:** 我会将需求文档从 `docs/backlog/` 移动到 `docs/requirements/`,并更新其状态为 `开发中`。
|
| 114 |
+
- **`COMMIT` 1 (分析完成):** `git commit -m "docs(req): 完成需求 {文件名} 的分析与测试用例,即将开发"`
|
| 115 |
+
|
| 116 |
+
**2. 开发执行**
|
| 117 |
+
- 我将根据需求文档和预先定义的测试用例,进行编码实现。
|
| 118 |
+
- **`COMMIT` 2 (开发完成):** `git commit -m "feat(scope): 完成需求 {文件名} 的开发,即将验证"`
|
| 119 |
|
| 120 |
+
**3. 验证与修复循环 (带熔断机制)**
|
| 121 |
+
- 我将自动运行在规划阶段编写的测试用例来验证我的代码。
|
| 122 |
+
- **a. 如果验证成功:**
|
| 123 |
+
- **`COMMIT` 5 (需求完成):** `git commit -m "docs(req): 需求 {文件名} 的开发和验证已经全部完成!"`
|
| 124 |
+
- 我会向您报告此需求已圆满完成,并自动返回**步骤 1**,开始下一个需求的循环。
|
| 125 |
+
- **b. 如果验证失败 (第 N 次尝试):**
|
| 126 |
+
- **`COMMIT` 3 (发现问题):** `git commit -m "docs(req): 验证需求 {文件名} 时遇到问题 {问题描述} (第 N 次尝试),即将修复"`
|
| 127 |
+
- **熔断机制:** 如果对同一个问题的修复尝试超过 **3 次**,我将触发熔断:
|
| 128 |
+
1. 自动回滚所有失败的修复尝试。
|
| 129 |
+
2. 将需求文档状态标记为 `Blocked`。
|
| 130 |
+
3. **主动向您报告,清晰阐述我遇到的问题、已尝试的方案及失败原因,请求您的指导。**
|
| 131 |
+
- **修复执行:** 我将进入“调试流程”,分析日志,定位并修复问题。
|
| 132 |
+
- **`COMMIT` 4 (修复完成):** `git commit -m "fix(scope): 验证需求 {文件名} ,成功解决了问题 {问题描述} (第 N 次尝试)"`
|
| 133 |
+
- 修复后,我将自动返回**步骤 3a**,重新运行完整的自动化验证。
|
| 134 |
|
| 135 |
+
**日志分析:**
|
| 136 |
|
| 137 |
+
- When debugging, I must redirect application output to a log file (e.g., app.log) and then read that file to diagnose issues.
|
| 138 |
|
| 139 |
+
---
|
| 140 |
+
|
| 141 |
+
## 第三部分:项目整体设计
|
| 142 |
+
|
| 143 |
+
### 3.1 项目目标
|
| 144 |
|
| 145 |
+
创建一个名为 “Ling & Ring Playground” 的 Hugging Face Space,用于演示 Ling / Ring 系列模型能力。
|
| 146 |
+
|
| 147 |
+
### 3.2 核心设计原则
|
| 148 |
|
| 149 |
- **任务导向:** UI 围绕用户任务(聊天、编码等)组织,而非直接暴露模型。
|
| 150 |
- **品牌明晰:** 在每个功能界面清晰标注“由 Ling/Ring 模型驱动”。
|
| 151 |
- **无缝体验:** 用户无需手动输入 API Token,认证在后端自动完成。
|
| 152 |
- **引导优先:** 提供精心设计的示例,确保用户获得高质量的初次体验。
|
| 153 |
|
| 154 |
+
### 3.3 技术栈与架构
|
| 155 |
|
| 156 |
- **前端/UI:** Gradio `gr.Blocks`
|
| 157 |
- **后端:** Python
|
| 158 |
- **安全:** 所有 API 密钥通过 Hugging Face Space Secrets 管理。
|
| 159 |
|
| 160 |
+
#### 3.3.1 配置加载策略
|
| 161 |
|
| 162 |
为兼顾本地开发的便利性、线上部署的安全性与成本效益,项目采用了一种分层配置加载机制:
|
| 163 |
|
|
|
|
| 167 |
|
| 168 |
此策略确保了开发者在本地可以快速迭代,而线上部署则遵循了安全最佳实践。
|
| 169 |
|
| 170 |
+
### 3.4 项目结构
|
| 171 |
|
| 172 |
```
|
| 173 |
/
|
|
|
|
| 188 |
└───refs/ # 本地参考资料(Git 忽略)
|
| 189 |
```
|
| 190 |
|
| 191 |
+
### 3.5 代码架构
|
| 192 |
|
| 193 |
1. **`models.py`**: 存放所有与模型对接和交互的逻辑。
|
| 194 |
2. **`app.py`**: 作为应用的统一入口,仅保留最精简的组装和启动逻辑。
|
|
|
|
| 198 |
|
| 199 |
---
|
| 200 |
|
| 201 |
+
## 第四部分:项目详细设计
|
| 202 |
|
| 203 |
+
应用为一个包含页头和四个核心[GEMINI.md](../../.gemini/GEMINI.md)功能标签页的单页应用。
|
| 204 |
|
| 205 |
+
### 4.1 Tab 1: 聊天 (Chat) - `tab_chat.py`
|
| 206 |
|
| 207 |
- **目标:** 提供一个与 Ling 模型进行高质量对话的界面。
|
| 208 |
- **UI 布局:**
|
|
|
|
| 211 |
- **用户用例:**
|
| 212 |
1. 用户在输入框中输入问题,按回车提交。
|
| 213 |
2. Ling 模型的响应以流式方式��现在聊天历史中,且每条回复的开头都会以 `**<模型名称>**` 的形式清晰地标识出当前是哪个模型在回答。
|
| 214 |
+
3. 在模型输出期间,用户可点击“终止输出”按钮来随时中断回复。
|
| 215 |
+
4. 用户可继续多轮对话,或通过右侧面板调整模型行为。
|
| 216 |
|
| 217 |
+
### 4.2 Tab 2: 代码生成 (Code Generation) - `tab_code.py`
|
| 218 |
|
| 219 |
- **目标:** 利用 Ring 模型,根据用户需求生成代码,并提供实时预览。
|
| 220 |
- **UI 布局:**
|
|
|
|
| 231 |
6. 用户可以点击“全屏预览”按钮,此时输入和代码区域会隐藏,预览区将放大以提供更沉浸的体验。再次点击可恢复。
|
| 232 |
7. 对于 Gradio 应用,后端会启动一个独立的子进程来运行代码,并将应用界面嵌入到预览区。
|
| 233 |
|
| 234 |
+
### 4.3 Tab 3: 网页检索 (Web Search) - `tab_search.py`
|
| 235 |
|
| 236 |
- **目标:** 利用 Ring 模型的检索能力,提供精准的网页信息摘要。
|
| 237 |
- **UI 布局:** 单栏居中布局,包含 `gr.Textbox` (输入), `gr.Button` (搜索), `gr.Markdown` (结果)。
|
|
|
|
| 239 |
1. 用户输入问题(例如:“什么是 Transformer 架构?”)。
|
| 240 |
2. 点击“搜索”后,Ring 模型返回的摘要和来源链接会显示在结果区。
|
| 241 |
|
| 242 |
+
### 4.4 Tab 4: 工作流执行 (Workflow Execution) - `tab_workflow.py`
|
| 243 |
|
| 244 |
- **目标:** 展示 Ring 模型作为 Agent 执行复杂工作流的能力。
|
| 245 |
- **UI 布局:**
|
|
|
|
| 249 |
1. 用户输入任务描述(例如:“查找最新的 Llama 3 模型并总结其模型卡片”)。
|
| 250 |
2. Ring 模型生成执行计划并可视化,然后开始执行。
|
| 251 |
3. 右侧面板实时显示执行日志,并在需要时通过聊天机器人向用户请求决策。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title: Ling Playground
|
| 3 |
-
emoji: 🔥
|
| 4 |
colorFrom: pink
|
| 5 |
colorTo: purple
|
| 6 |
sdk: gradio
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Ling Playground
|
|
|
|
| 3 |
colorFrom: pink
|
| 4 |
colorTo: purple
|
| 5 |
sdk: gradio
|
__pycache__/config.cpython-313.pyc
DELETED
|
Binary file (8.31 kB)
|
|
|
__pycache__/local.cpython-313.pyc
DELETED
|
Binary file (1.1 kB)
|
|
|
__pycache__/models.cpython-313.pyc
DELETED
|
Binary file (5.36 kB)
|
|
|
__pycache__/tab_chat.cpython-313.pyc
DELETED
|
Binary file (8.8 kB)
|
|
|
__pycache__/tab_code.cpython-313.pyc
DELETED
|
Binary file (12.7 kB)
|
|
|
__pycache__/tab_search.cpython-313.pyc
DELETED
|
Binary file (2.38 kB)
|
|
|
__pycache__/tab_workflow.cpython-313.pyc
DELETED
|
Binary file (4.12 kB)
|
|
|
__pycache__/utils.cpython-313.pyc
DELETED
|
Binary file (1.24 kB)
|
|
|
app.py
CHANGED
|
@@ -4,38 +4,44 @@ import gradio as gr
|
|
| 4 |
from tab_chat import create_chat_tab, handle_chat
|
| 5 |
from tab_code import create_code_tab
|
| 6 |
from tab_search import create_search_tab, handle_web_search
|
| 7 |
-
from tab_workflow import create_workflow_tab,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
# --- Main Gradio UI Definition ---
|
| 10 |
-
with gr.Blocks(theme=gr.themes.Default(primary_hue="blue")) as demo:
|
| 11 |
# Global state for the workflow tab
|
| 12 |
-
|
|
|
|
| 13 |
|
| 14 |
# --- Header ---
|
| 15 |
with gr.Row():
|
| 16 |
gr.Markdown("""
|
| 17 |
-
# Ling & Ring Playground
|
| 18 |
-
###
|
| 19 |
""")
|
| 20 |
with gr.Row():
|
| 21 |
gr.Markdown("""
|
| 22 |
-
[Ling Model Card](https://huggingface.co) | [Ring Model Card](https://huggingface.co)
|
|
|
|
| 23 |
""")
|
| 24 |
|
| 25 |
# --- Main UI Tabs ---
|
| 26 |
with gr.Tabs() as main_ui:
|
| 27 |
# Create tabs by calling functions from modules
|
| 28 |
-
with gr.Tab("
|
| 29 |
chat_components = create_chat_tab()
|
| 30 |
-
with gr.Tab("
|
| 31 |
create_code_tab() # The code tab now handles its own events
|
| 32 |
-
with gr.Tab("
|
| 33 |
search_components = create_search_tab()
|
| 34 |
-
with gr.Tab("
|
| 35 |
workflow_components = create_workflow_tab()
|
| 36 |
|
| 37 |
# --- Event Handling Logic ---
|
| 38 |
-
|
| 39 |
# Chat Tab Events
|
| 40 |
chat_submit_event = chat_components["chat_input"].submit(
|
| 41 |
fn=handle_chat,
|
|
@@ -73,32 +79,44 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue")) as demo:
|
|
| 73 |
outputs=[search_components["search_results_output"]]
|
| 74 |
)
|
| 75 |
|
| 76 |
-
# Workflow Tab Events
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
workflow_components["workflow_status_output"],
|
| 83 |
-
workflow_components["workflow_chatbot"],
|
| 84 |
-
workflow_state,
|
| 85 |
-
workflow_components["workflow_chat_input"] # 新增:直接作为输出
|
| 86 |
-
]
|
| 87 |
-
)
|
| 88 |
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
)
|
| 103 |
|
| 104 |
demo.launch()
|
|
|
|
| 4 |
from tab_chat import create_chat_tab, handle_chat
|
| 5 |
from tab_code import create_code_tab
|
| 6 |
from tab_search import create_search_tab, handle_web_search
|
| 7 |
+
from tab_workflow import create_workflow_tab, handle_workflow_chat
|
| 8 |
+
|
| 9 |
+
MERMAID_SCRIPT = """
|
| 10 |
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
| 11 |
+
"""
|
| 12 |
|
| 13 |
# --- Main Gradio UI Definition ---
|
| 14 |
+
with gr.Blocks(theme=gr.themes.Default(primary_hue="blue"), head=MERMAID_SCRIPT) as demo:
|
| 15 |
# Global state for the workflow tab
|
| 16 |
+
workflow_topic_state = gr.State("")
|
| 17 |
+
workflow_steps_state = gr.State("*Waiting for task to start...*")
|
| 18 |
|
| 19 |
# --- Header ---
|
| 20 |
with gr.Row():
|
| 21 |
gr.Markdown("""
|
| 22 |
+
# Ling & Ring Playground(wip)
|
| 23 |
+
### Experience chat, coding, search, and workflow automation
|
| 24 |
""")
|
| 25 |
with gr.Row():
|
| 26 |
gr.Markdown("""
|
| 27 |
+
[Ling Model Card](https://huggingface.co/inclusionAI/Ling-1T) | [Ring Model Card](https://huggingface.co/inclusionAI/Ring-1T)
|
| 28 |
+
This application uses API services from [ZenMux](https://zenmux.ai/) and incorporates code generation ideas and prompt snippets from [AnyCoder](https://huggingface.co/spaces/akhaliq/anycoder).
|
| 29 |
""")
|
| 30 |
|
| 31 |
# --- Main UI Tabs ---
|
| 32 |
with gr.Tabs() as main_ui:
|
| 33 |
# Create tabs by calling functions from modules
|
| 34 |
+
with gr.Tab("Chat"):
|
| 35 |
chat_components = create_chat_tab()
|
| 36 |
+
with gr.Tab("Code Generation"):
|
| 37 |
create_code_tab() # The code tab now handles its own events
|
| 38 |
+
with gr.Tab("Web Search"):
|
| 39 |
search_components = create_search_tab()
|
| 40 |
+
with gr.Tab("Workflow"):
|
| 41 |
workflow_components = create_workflow_tab()
|
| 42 |
|
| 43 |
# --- Event Handling Logic ---
|
| 44 |
+
|
| 45 |
# Chat Tab Events
|
| 46 |
chat_submit_event = chat_components["chat_input"].submit(
|
| 47 |
fn=handle_chat,
|
|
|
|
| 79 |
outputs=[search_components["search_results_output"]]
|
| 80 |
)
|
| 81 |
|
| 82 |
+
# Workflow Tab Events (Stateful & Incremental with Mermaid)
|
| 83 |
+
def workflow_event_handler(user_input, history, topic, workflow):
|
| 84 |
+
# The handler calls the core logic and returns all the necessary updates
|
| 85 |
+
# for the UI components and the state objects.
|
| 86 |
+
for history, new_topic, new_workflow, mermaid_html, _ in handle_workflow_chat(user_input, history, topic, workflow):
|
| 87 |
+
yield history, new_topic, new_workflow, mermaid_html, new_topic, new_workflow, ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
+
workflow_inputs = [
|
| 90 |
+
workflow_components["chat_input"],
|
| 91 |
+
workflow_components["chatbot"],
|
| 92 |
+
workflow_topic_state,
|
| 93 |
+
workflow_steps_state
|
| 94 |
+
]
|
| 95 |
+
|
| 96 |
+
workflow_outputs = [
|
| 97 |
+
workflow_components["chatbot"],
|
| 98 |
+
workflow_components["topic_output"],
|
| 99 |
+
workflow_components["workflow_output"],
|
| 100 |
+
workflow_components["mermaid_output"],
|
| 101 |
+
workflow_topic_state,
|
| 102 |
+
workflow_steps_state,
|
| 103 |
+
workflow_components["chat_input"]
|
| 104 |
+
]
|
| 105 |
+
|
| 106 |
+
workflow_js_trigger = """() => { setTimeout(() => { try { mermaid.run({ nodes: document.querySelectorAll('.mermaid') }); } catch (e) { console.error('Mermaid render error:', e); } }, 200); }"""
|
| 107 |
+
|
| 108 |
+
workflow_components["send_button"].click(
|
| 109 |
+
fn=workflow_event_handler,
|
| 110 |
+
inputs=workflow_inputs,
|
| 111 |
+
outputs=workflow_outputs,
|
| 112 |
+
js=workflow_js_trigger
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
workflow_components["chat_input"].submit(
|
| 116 |
+
fn=workflow_event_handler,
|
| 117 |
+
inputs=workflow_inputs,
|
| 118 |
+
outputs=workflow_outputs,
|
| 119 |
+
js=workflow_js_trigger
|
| 120 |
)
|
| 121 |
|
| 122 |
demo.launch()
|
config.py
CHANGED
|
@@ -61,148 +61,147 @@ WORKFLOW_EXECUTE_SYSTEM_PROMPT = "You are a workflow execution assistant. Your g
|
|
| 61 |
# --- Model Specifications ---
|
| 62 |
|
| 63 |
CHAT_MODEL_SPECS = {
|
| 64 |
-
"
|
| 65 |
"model_id": "inclusionai/ling-1t",
|
| 66 |
-
"display_name": "
|
| 67 |
-
"description": "
|
| 68 |
"prompt_scenarios": [
|
| 69 |
{
|
| 70 |
-
"title": "
|
| 71 |
-
"system_prompt": "
|
| 72 |
"message_examples": [
|
| 73 |
-
"
|
| 74 |
-
"
|
| 75 |
-
"
|
| 76 |
]
|
| 77 |
},
|
| 78 |
{
|
| 79 |
-
"title": "
|
| 80 |
-
"system_prompt": "
|
| 81 |
"message_examples": [
|
| 82 |
-
"
|
| 83 |
-
"
|
| 84 |
-
"
|
| 85 |
]
|
| 86 |
}
|
| 87 |
]
|
| 88 |
},
|
| 89 |
-
"
|
| 90 |
"model_id": "inclusionai/ling-flash-2.0",
|
| 91 |
-
"display_name": "
|
| 92 |
-
"description": "
|
| 93 |
"prompt_scenarios": [
|
| 94 |
{
|
| 95 |
-
"title": "
|
| 96 |
-
"system_prompt": "
|
| 97 |
"message_examples": [
|
| 98 |
-
"
|
| 99 |
-
"
|
| 100 |
-
"
|
| 101 |
]
|
| 102 |
},
|
| 103 |
{
|
| 104 |
-
"title": "
|
| 105 |
-
"system_prompt": "
|
| 106 |
"message_examples": [
|
| 107 |
-
"
|
| 108 |
-
"
|
| 109 |
-
"
|
| 110 |
]
|
| 111 |
}
|
| 112 |
]
|
| 113 |
},
|
| 114 |
-
"
|
| 115 |
-
"model_id": "inclusionai/
|
| 116 |
-
"display_name": "
|
| 117 |
-
"description": "
|
| 118 |
"prompt_scenarios": [
|
| 119 |
{
|
| 120 |
-
"title": "
|
| 121 |
-
"system_prompt": "
|
| 122 |
"message_examples": [
|
| 123 |
-
"
|
| 124 |
-
"
|
| 125 |
-
"
|
| 126 |
]
|
| 127 |
},
|
| 128 |
{
|
| 129 |
-
"title": "
|
| 130 |
-
"system_prompt": "
|
| 131 |
"message_examples": [
|
| 132 |
-
"
|
| 133 |
-
"
|
| 134 |
-
"
|
| 135 |
]
|
| 136 |
}
|
| 137 |
]
|
| 138 |
},
|
| 139 |
-
"
|
| 140 |
-
"model_id": "inclusionai/
|
| 141 |
-
"display_name": "
|
| 142 |
-
"description": "
|
| 143 |
"prompt_scenarios": [
|
| 144 |
{
|
| 145 |
-
"title": "
|
| 146 |
-
"system_prompt": "
|
| 147 |
"message_examples": [
|
| 148 |
-
"
|
| 149 |
-
"
|
| 150 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
]
|
| 152 |
},
|
| 153 |
{
|
| 154 |
-
"title": "
|
| 155 |
-
"system_prompt": "
|
| 156 |
"message_examples": [
|
| 157 |
-
"
|
| 158 |
-
"
|
| 159 |
-
"
|
| 160 |
]
|
| 161 |
}
|
| 162 |
]
|
| 163 |
},
|
| 164 |
-
"
|
| 165 |
"model_id": "inclusionai/ring-mini-2.0",
|
| 166 |
-
"display_name": "
|
| 167 |
-
"description": "
|
| 168 |
"prompt_scenarios": [
|
| 169 |
{
|
| 170 |
-
"title": "
|
| 171 |
-
"system_prompt": "
|
| 172 |
"message_examples": [
|
| 173 |
-
"
|
| 174 |
-
"
|
| 175 |
-
"
|
| 176 |
]
|
| 177 |
},
|
| 178 |
{
|
| 179 |
-
"title": "
|
| 180 |
-
"system_prompt": "
|
| 181 |
"message_examples": [
|
| 182 |
-
"
|
| 183 |
-
"
|
| 184 |
-
"
|
| 185 |
]
|
| 186 |
}
|
| 187 |
]
|
| 188 |
}
|
| 189 |
}
|
| 190 |
|
| 191 |
-
# --- Local Model ID Mapping Override ---
|
| 192 |
-
# Attempt to import a mapping from online model IDs to local model IDs
|
| 193 |
-
# from local.py. This allows developers to use different model names for
|
| 194 |
-
# local testing without changing the core application code.
|
| 195 |
-
try:
|
| 196 |
-
from local import get_local_model_id_map
|
| 197 |
-
local_model_id_map = get_local_model_id_map()
|
| 198 |
-
for model_id, spec in CHAT_MODEL_SPECS.items():
|
| 199 |
-
if model_id in local_model_id_map:
|
| 200 |
-
spec['model_id'] = local_model_id_map[model_id]
|
| 201 |
-
print(f"🔄 Overrode model ID for '{model_id}': '{model_id}' -> '{spec['model_id']}'")
|
| 202 |
-
except ImportError:
|
| 203 |
-
# local.py does not exist or does not contain the mapping function.
|
| 204 |
-
# This is expected in a production environment.
|
| 205 |
-
pass
|
| 206 |
-
except Exception as e:
|
| 207 |
-
print(f"⚠️ Warning: Failed to apply local model ID mapping. Error: {e}")
|
| 208 |
|
|
|
|
| 61 |
# --- Model Specifications ---
|
| 62 |
|
| 63 |
CHAT_MODEL_SPECS = {
|
| 64 |
+
"ling-1t": {
|
| 65 |
"model_id": "inclusionai/ling-1t",
|
| 66 |
+
"display_name": "🦉 Ling-1T",
|
| 67 |
+
"description": "A trillion-parameter large language model designed for complex natural language understanding and generation tasks that require extreme performance and high fluency.",
|
| 68 |
"prompt_scenarios": [
|
| 69 |
{
|
| 70 |
+
"title": "In-depth Analysis Report Writing",
|
| 71 |
+
"system_prompt": "You are a senior industry analyst who can write in-depth analysis reports with clear logic, sufficient data, and unique insights.",
|
| 72 |
"message_examples": [
|
| 73 |
+
"Write an in-depth analysis report of at least 800 words on the application of artificial intelligence in the medical field.",
|
| 74 |
+
"Analyze the current macroeconomic situation and predict the development trend for the next year.",
|
| 75 |
+
"Develop a detailed brand promotion strategy for a newly established technology company."
|
| 76 |
]
|
| 77 |
},
|
| 78 |
{
|
| 79 |
+
"title": "Shakespearean Style Copywriting",
|
| 80 |
+
"system_prompt": "You are a master of imitation, capable of literary creation in the style and tone of William Shakespeare.",
|
| 81 |
"message_examples": [
|
| 82 |
+
"Write a monologue about 'code' in the style of Shakespeare.",
|
| 83 |
+
"If Hamlet were a programmer, how would he complain about a tricky bug?",
|
| 84 |
+
"Express the term 'user experience' in the form of a sonnet."
|
| 85 |
]
|
| 86 |
}
|
| 87 |
]
|
| 88 |
},
|
| 89 |
+
"ling-flash-2.0": {
|
| 90 |
"model_id": "inclusionai/ling-flash-2.0",
|
| 91 |
+
"display_name": "🦉 Ling-flash-2.0",
|
| 92 |
+
"description": "A high-performance billion-parameter model optimized for scenarios requiring high-speed response and complex instruction following.",
|
| 93 |
"prompt_scenarios": [
|
| 94 |
{
|
| 95 |
+
"title": "Technical Document Writing",
|
| 96 |
+
"system_prompt": "You are a professional technical writer who can clearly and accurately explain complex technical concepts.",
|
| 97 |
"message_examples": [
|
| 98 |
+
"Write clear documentation for a new API endpoint.",
|
| 99 |
+
"Explain what the 'Transformer' architecture is.",
|
| 100 |
+
"How to write a contribution guide for an open source project?"
|
| 101 |
]
|
| 102 |
},
|
| 103 |
{
|
| 104 |
+
"title": "Creative Brainstorming",
|
| 105 |
+
"system_prompt": "You are a creative partner who can brainstorm and provide novel ideas.",
|
| 106 |
"message_examples": [
|
| 107 |
+
"Come up with 5 catchy names for a new podcast.",
|
| 108 |
+
"What should I write for my blog?",
|
| 109 |
+
"Come up with a short story idea about time travel."
|
| 110 |
]
|
| 111 |
}
|
| 112 |
]
|
| 113 |
},
|
| 114 |
+
"ling-mini-2.0": {
|
| 115 |
+
"model_id": "inclusionai/ling-mini-2.0",
|
| 116 |
+
"display_name": "🦉 Ling-mini-2.0",
|
| 117 |
+
"description": "A lightweight conversational model optimized for efficient operation on consumer-grade hardware, ideal for mobile or localized deployment scenarios.",
|
| 118 |
"prompt_scenarios": [
|
| 119 |
{
|
| 120 |
+
"title": "Efficient Email Assistant",
|
| 121 |
+
"system_prompt": "You are a professional administrative assistant who excels at writing clear, concise, and professional emails.",
|
| 122 |
"message_examples": [
|
| 123 |
+
"Write me a short email reminding the team members of the meeting tomorrow at 10 am.",
|
| 124 |
+
"Draft an email to the client to inquire about the project progress.",
|
| 125 |
+
"Help me write a polite rejection letter in response to an inappropriate collaboration invitation."
|
| 126 |
]
|
| 127 |
},
|
| 128 |
{
|
| 129 |
+
"title": "Text Summarization and Translation",
|
| 130 |
+
"system_prompt": "You are a language expert who can quickly and accurately perform text summarization and multilingual translation.",
|
| 131 |
"message_examples": [
|
| 132 |
+
"Summarize the main content of this news article in no more than three sentences.",
|
| 133 |
+
"Translate this English passage into Chinese: 'Gradio is an open-source Python library...'",
|
| 134 |
+
"Recommend three sci-fi movies suitable for watching on the weekend."
|
| 135 |
]
|
| 136 |
}
|
| 137 |
]
|
| 138 |
},
|
| 139 |
+
"ring-1t": {
|
| 140 |
+
"model_id": "inclusionai/ring-1t",
|
| 141 |
+
"display_name": "💍️ Ring-1T",
|
| 142 |
+
"description": "A brand-new trillion-parameter reasoning model with powerful code generation and tool use capabilities.",
|
| 143 |
"prompt_scenarios": [
|
| 144 |
{
|
| 145 |
+
"title": "Python Script Generator",
|
| 146 |
+
"system_prompt": "You are a Python programming expert who can generate high-quality, executable Python scripts based on requirements.",
|
| 147 |
"message_examples": [
|
| 148 |
+
"Generate a Python script to monitor website price changes and send email reminders when prices drop.",
|
| 149 |
+
"Write a Python function to calculate the number of days between two dates.",
|
| 150 |
+
"Implement a simple command-line calculator in Python."
|
| 151 |
+
]
|
| 152 |
+
}
|
| 153 |
+
]
|
| 154 |
+
},
|
| 155 |
+
"ring-flash-2.0": {
|
| 156 |
+
"model_id": "inclusionai/ring-flash-2.0",
|
| 157 |
+
"display_name": "💍️ Ring-flash-2.0",
|
| 158 |
+
"description": "A billion-parameter reasoning model that strikes a good balance between performance and cost, suitable for general-purpose tasks that require step-by-step thinking or code generation.",
|
| 159 |
+
"prompt_scenarios": [
|
| 160 |
+
{
|
| 161 |
+
"title": "Travel Planning Expert",
|
| 162 |
+
"system_prompt": "You are an experienced travel planner who is proficient in travel routes, transportation, and budget planning around the world.",
|
| 163 |
+
"message_examples": [
|
| 164 |
+
"Plan a five-day independent trip to Tokyo, Japan, including a detailed daily itinerary, transportation, and budget.",
|
| 165 |
+
"How should I choose my first electric guitar? Please provide steps and suggestions.",
|
| 166 |
+
"Recommend three recipes for my weekend family dinner."
|
| 167 |
]
|
| 168 |
},
|
| 169 |
{
|
| 170 |
+
"title": "Python Script Generator",
|
| 171 |
+
"system_prompt": "You are a Python programming expert who can generate high-quality, executable Python scripts based on requirements.",
|
| 172 |
"message_examples": [
|
| 173 |
+
"Generate a Python script to monitor website price changes and send email reminders when prices drop.",
|
| 174 |
+
"Write a Python function to calculate the number of days between two dates.",
|
| 175 |
+
"Implement a simple command-line calculator in Python."
|
| 176 |
]
|
| 177 |
}
|
| 178 |
]
|
| 179 |
},
|
| 180 |
+
"ring-mini-2.0": {
|
| 181 |
"model_id": "inclusionai/ring-mini-2.0",
|
| 182 |
+
"display_name": "💍️ Ring-mini-2.0",
|
| 183 |
+
"description": "A quantized and extremely efficient reasoning model designed for resource-constrained environments with strict speed and efficiency requirements (such as edge computing).",
|
| 184 |
"prompt_scenarios": [
|
| 185 |
{
|
| 186 |
+
"title": "Daily Life Assistant",
|
| 187 |
+
"system_prompt": "You are a helpful life assistant who can handle various daily requests.",
|
| 188 |
"message_examples": [
|
| 189 |
+
"Help me set a 25-minute Pomodoro timer.",
|
| 190 |
+
"Add milk and bread to my shopping list.",
|
| 191 |
+
"Check the weather in Beijing today."
|
| 192 |
]
|
| 193 |
},
|
| 194 |
{
|
| 195 |
+
"title": "Simple Code Snippets",
|
| 196 |
+
"system_prompt": "You are a code snippet generator that provides concise and correct code examples for common programming problems.",
|
| 197 |
"message_examples": [
|
| 198 |
+
"Provide an example of a GET request implemented in JavaScript.",
|
| 199 |
+
"How to center a div horizontally with CSS?",
|
| 200 |
+
"Count from 1 to 10."
|
| 201 |
]
|
| 202 |
}
|
| 203 |
]
|
| 204 |
}
|
| 205 |
}
|
| 206 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
|
docs/backlog/2025-10-11-18-43-auto-fix-for-code-generator.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11-18-43
|
| 4 |
- **状态:** 待处理 (Pending)
|
|
|
|
| 5 |
|
| 6 |
## 需求描述
|
| 7 |
|
|
|
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11-18-43
|
| 4 |
- **状态:** 待处理 (Pending)
|
| 5 |
+
- **重要性:** 中等 (Medium)
|
| 6 |
|
| 7 |
## 需求描述
|
| 8 |
|
docs/backlog/2025-10-11-19-41-add-local-model-id-mapping.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
- **需求描述:** 为了同时兼顾在线部署和本地开发的便利性,需要实现一套模型 ID 的映射机制。代码中应默认使用在线部署的官方模型 ID,但允许通过一个本地的 `local.py` 文件来覆盖这些 ID,使其指向本地开发环境中使用的不同模型名称。此外,模型列表需要新增 `inclusionai/ring-mini-2.0`,并为其补充相应的 UI 示例。
|
| 2 |
- **创建时间:** 2025-10-11-19-41
|
| 3 |
- **初始状态:** 待处理 (Pending)
|
|
|
|
| 4 |
- **验证方式:** (暂空)
|
| 5 |
- **验证结果:** (暂空)
|
|
|
|
| 1 |
- **需求描述:** 为了同时兼顾在线部署和本地开发的便利性,需要实现一套模型 ID 的映射机制。代码中应默认使用在线部署的官方模型 ID,但允许通过一个本地的 `local.py` 文件来覆盖这些 ID,使其指向本地开发环境中使用的不同模型名称。此外,模型列表需要新增 `inclusionai/ring-mini-2.0`,并为其补充相应的 UI 示例。
|
| 2 |
- **创建时间:** 2025-10-11-19-41
|
| 3 |
- **初始状态:** 待处理 (Pending)
|
| 4 |
+
- **重要性:** 中等 (Medium)
|
| 5 |
- **验证方式:** (暂空)
|
| 6 |
- **验证结果:** (暂空)
|
docs/backlog/2025-10-11-20-32-chat-interrupt-output.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 需求:聊天功能添加终止输出能力
|
| 2 |
+
|
| 3 |
+
- **创建时间:** 2025-01-27-15-30
|
| 4 |
+
- **状态:** 待处理 (Pending)
|
| 5 |
+
- **重要性:** 中等 (Medium)
|
| 6 |
+
|
| 7 |
+
## 需求描述
|
| 8 |
+
|
| 9 |
+
为聊天 Tab 添加终止输出功能,提升用户体验。具体要求:
|
| 10 |
+
|
| 11 |
+
1. **按钮状态切换:**
|
| 12 |
+
- 正常状态下显示"发送消息"按钮
|
| 13 |
+
- 模型输出期间,按钮文本变为"终止输出"
|
| 14 |
+
- 输出完成后,按钮恢复为"发送消息"
|
| 15 |
+
|
| 16 |
+
2. **中断机制:**
|
| 17 |
+
- 用户点击"终止输出"按钮时,立即停止当前的流式响应
|
| 18 |
+
- 已输出的部分内容保留在聊天历史中
|
| 19 |
+
- 系统应优雅地处理中断,避免产生错误或异常状态
|
| 20 |
+
|
| 21 |
+
3. **用户体验:**
|
| 22 |
+
- 按钮状态变化应该直观明确
|
| 23 |
+
- 中断操作应该响应迅速
|
| 24 |
+
- 中断后用户可以立即发送新消息
|
| 25 |
+
|
| 26 |
+
## 技术实现要点
|
| 27 |
+
|
| 28 |
+
- 需要在流式输出过程中检查中断信号
|
| 29 |
+
- 使用 Gradio 的事件处理机制管理按钮状态
|
| 30 |
+
- 确保中断后的状态清理和重置
|
| 31 |
+
|
| 32 |
+
## 验证方式
|
| 33 |
+
|
| 34 |
+
1. **启动应用:** 在终端中运行 `python app.py`。
|
| 35 |
+
2. **打开聊天 Tab:** 在浏览器中打开应用,并切换到“聊天”标签页。
|
| 36 |
+
3. **发送消息:** 输入一条消息,点击“发送”按钮。
|
| 37 |
+
4. **观察按钮状态:** 确认“发送”按钮变为“终止”按钮。
|
| 38 |
+
5. **终止输出:** 在模型输出期间,点击“终止”按钮。
|
| 39 |
+
6. **验证中断:** 确认模型输出立即停止。
|
| 40 |
+
7. **验证按钮恢复:** 确认“终止”按钮恢复为“发送”按钮。
|
| 41 |
+
8. **验证可再次发送:** 确认可以立即发送新的消息。
|
| 42 |
+
9. **验证正常完成:** 发送一条简短消息,让其自然结束,确认结束后按钮会自动从“终止”恢复为“发送”。
|
| 43 |
+
|
| 44 |
+
## 验证结果
|
| 45 |
+
|
| 46 |
+
(暂无)
|
docs/requirements/2025-10-11-14-35-fix-chat-model-display-name.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
- **需求描述:** 在 chat tab 的「选择模型」栏里面,展示的模型名字和实际的模型 id 不一样。将展示名字改成实际的模型 id。
|
| 4 |
- **创建时间:** 2025-10-11 14:55
|
| 5 |
-
- **状态:**
|
| 6 |
- **验证方式:**
|
| 7 |
1. 打开浏览器并访问 `http://12.0.0.1:7860`。
|
| 8 |
2. 在“聊天 (Chat)”标签页中,查看右侧的“选择模型”区域。
|
|
|
|
| 2 |
|
| 3 |
- **需求描述:** 在 chat tab 的「选择模型」栏里面,展示的模型名字和实际的模型 id 不一样。将展示名字改成实际的模型 id。
|
| 4 |
- **创建时间:** 2025-10-11 14:55
|
| 5 |
+
- **状态:** `已验证 (Verified)`
|
| 6 |
- **验证方式:**
|
| 7 |
1. 打开浏览器并访问 `http://12.0.0.1:7860`。
|
| 8 |
2. 在“聊天 (Chat)”标签页中,查看右侧的“选择模型”区域。
|
docs/requirements/2025-10-11-14-37-update-model-descriptions.md
CHANGED
|
@@ -8,4 +8,4 @@
|
|
| 8 |
2. 在“聊天 (Chat)”标签页中,查看右侧的“选择模型”区域。
|
| 9 |
3. 逐个点击选择不同的模型(如 `Ling-1T`, `Ring-flash-2.0` 等)。
|
| 10 |
4. 确认每次选择后,下方显示的描述文本会更新为我们从 Hugging Face 页面总结的最新内容。
|
| 11 |
-
- **验证结果:** (
|
|
|
|
| 8 |
2. 在“聊天 (Chat)”标签页中,查看右侧的“选择模型”区域。
|
| 9 |
3. 逐个点击选择不同的模型(如 `Ling-1T`, `Ring-flash-2.0` 等)。
|
| 10 |
4. 确认每次选择后,下方显示的描述文本会更新为我们从 Hugging Face 页面总结的最新内容。
|
| 11 |
+
- **验证结果:** `已验证 (Verified)`
|
docs/requirements/2025-10-11-14-39-update-chat-example-prompts.md
CHANGED
|
@@ -8,4 +8,4 @@
|
|
| 8 |
2. 在“聊天 (Chat)”标签页中,查看下方的“示例提示”区域。
|
| 9 |
3. 在右侧“选择模型”处,逐个点击不同的模型。
|
| 10 |
4. 确认每次切换模型后,“示例提示”区域都会更新为我们为该模型新设计的、更具代表性的例子。
|
| 11 |
-
- **验证结果:** (
|
|
|
|
| 8 |
2. 在“聊天 (Chat)”标签页中,查看下方的“示例提示”区域。
|
| 9 |
3. 在右侧“选择模型”处,逐个点击不同的模型。
|
| 10 |
4. 确认每次切换模型后,“示例提示”区域都会更新为我们为该模型新设计的、更具代表性的例子。
|
| 11 |
+
- **验证结果:** `已验证 (Verified)`
|
docs/requirements/2025-10-11-15-08-refactor-chat-examples-to-scenarios.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
- **需求描述:** 当前的“示例提示”仅提供消息示例。需要将其扩展为“系统提示示例”。用户选择一个系统提示示例后,应用会自动填充“System Prompt”输入框,并展示与该系统提示相匹配的一组新的“消息示例”。
|
| 4 |
- **创建时间:** 2025-10-11 15:35
|
| 5 |
-
- **状态:**
|
| 6 |
- **验证方式:**
|
| 7 |
1. **界面检查:** 打开浏览器并访问 `http://127.0.0.1:7860`。在“聊天”标签页下方,确认旧的“示例提示”已替换为一个名为“✨ 试试这些场景...”的可折叠区域,其中包含“系统提示示例”和“消息示例”两部分。
|
| 8 |
2. **场景切换:** 点击一个“系统提示示例”(例如“莎士比亚风格文案”)。确认右侧的“System Prompt”文本框内容会更新,同时下方的“消息示例”列表也会更新为对应场景的例子。
|
|
|
|
| 2 |
|
| 3 |
- **需求描述:** 当前的“示例提示”仅提供消息示例。需要将其扩展为“系统提示示例”。用户选择一个系统提示示例后,应用会自动填充“System Prompt”输入框,并展示与该系统提示相匹配的一组新的“消息示例”。
|
| 4 |
- **创建时间:** 2025-10-11 15:35
|
| 5 |
+
- **状态:** `已验证 (Verified)`
|
| 6 |
- **验证方式:**
|
| 7 |
1. **界面检查:** 打开浏览器并访问 `http://127.0.0.1:7860`。在“聊天”标签页下方,确认旧的“示例提示”已替换为一个名为“✨ 试试这些场景...”的可折叠区域,其中包含“系统提示示例”和“消息示例”两部分。
|
| 8 |
2. **场景切换:** 点击一个“系统提示示例”(例如“莎士比亚风格文案”)。确认右侧的“System Prompt”文本框内容会更新,同时下方的“消息示例”列表也会更新为对应场景的例子。
|
docs/requirements/2025-10-11-15-47-add-model-identity-to-chat-output.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
- **需求描述:** 当前,聊天窗口里面,模型的输出不会标识自己是什么模型。需要在每个模型回复的开头,加上其身份标识。
|
| 4 |
- **创建时间:** 2025-10-11 16:20
|
| 5 |
-
- **状态:**
|
| 6 |
- **验证方式:**
|
| 7 |
1. 在“聊天 (Chat)”标签页中,选择任意模型。
|
| 8 |
2. 发送一条消息。
|
|
|
|
| 2 |
|
| 3 |
- **需求描述:** 当前,聊天窗口里面,模型的输出不会标识自己是什么模型。需要在每个模型回复的开头,加上其身份标识。
|
| 4 |
- **创建时间:** 2025-10-11 16:20
|
| 5 |
+
- **状态:** `已验证 (Verified)`
|
| 6 |
- **验证方式:**
|
| 7 |
1. 在“聊天 (Chat)”标签页中,选择任意模型。
|
| 8 |
2. 发送一条消息。
|
docs/requirements/2025-10-11-16-47-implement-static-page-generation.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# 需求:实现静态页面生成功能
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
-
- **状态:**
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
|
|
|
| 1 |
# 需求:实现静态页面生成功能
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
+
- **状态:** 已验证 (Verified)
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
docs/requirements/2025-10-11-16-56-add-code-generation-presets.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# 需求:为代码生成 Tab 添加预设选项
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
-
- **状态:**
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
|
|
|
| 1 |
# 需求:为代码生成 Tab 添加预设选项
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
+
- **状态:** 已验证 (Verified)
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
docs/requirements/2025-10-11-16-59-add-fullscreen-preview.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# 需求:为代码生成预览增加缩放与全屏功能
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
-
- **状态:**
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
|
|
|
| 1 |
# 需求:为代码生成预览增加缩放与全屏功能
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
+
- **状态:** 已验证 (Verified)
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
docs/requirements/2025-10-11-17-12-refactor-code-preview-to-tabs.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# 需求:将代码预览重构为 Tab 布局并优化刷新机制
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
-
- **状态:**
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
|
|
|
| 1 |
# 需求:将代码预览重构为 Tab 布局并优化刷新机制
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
+
- **状态:** 已验证 (Verified)
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
docs/requirements/2025-10-11-17-38-add-floating-island-example.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# 需求:为代码生成 Tab 添加“低多边形漂浮岛屿”示例
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
-
- **状态:**
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
|
|
|
| 1 |
# 需求:为代码生成 Tab 添加“低多边形漂浮岛屿”示例
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
+
- **状态:** 已验证 (Verified)
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
docs/requirements/2025-10-11-18-18-add-model-selection-switch.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# 需求:在代码生成页加入模型选择开关
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
-
- **状态:**
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
|
|
|
| 1 |
# 需求:在代码生成页加入模型选择开关
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
+
- **状态:** 已验证 (Verified)
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
docs/requirements/2025-10-11-18-18-display-think-tags-in-source-only.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# 需求:在源代码中显示 <think> 标签,但在预览中隐藏
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
-
- **状态:**
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
|
|
|
| 1 |
# 需求:在源代码中显示 <think> 标签,但在预览中隐藏
|
| 2 |
|
| 3 |
- **创建时间:** 2025-10-11
|
| 4 |
+
- **状态:** 已验证 (Verified)
|
| 5 |
|
| 6 |
## 1. 需求描述
|
| 7 |
|
docs/requirements/2025-10-11-21-05-add-real-time-generation-status.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 需求:为代码生成增加实时状态显示
|
| 2 |
+
|
| 3 |
+
- **需求描述:**
|
| 4 |
+
在“代码生成”功能区,当代码生成任务正在进行时,界面需要新增一个状态显示区域。该区域应实时更新以下信息:
|
| 5 |
+
- **状态:** "正在生成..."
|
| 6 |
+
- **生成时间:** 从开始到现在的耗时(例如 `5.2s`)。
|
| 7 |
+
- **平均速度:** 以 `tok/s` 或 `char/s` 为单位的生成速率。
|
| 8 |
+
- **生成长度:** 已生成的字符或 token 数量。
|
| 9 |
+
|
| 10 |
+
当生成过程完成后,该区域应更新显示:
|
| 11 |
+
- **状态:** "生成完成"
|
| 12 |
+
|
| 13 |
+
同时,系统需自动将用户的视图从“生成的源代码”标签页切换到“实时预览”标签页,以便用户能立刻看到最终结果。
|
| 14 |
+
|
| 15 |
+
- **创建时间:** 2025-10-11-21-05
|
| 16 |
+
- **状态:** `已完成 (Done)`
|
| 17 |
+
- **重要性:** 中等 (Medium)
|
| 18 |
+
- **验证方式:**
|
| 19 |
+
1. 启动应用,进入“代码生成”标签页。
|
| 20 |
+
2. 输入“创建一个简单的html页面”,点击“生成代码”。
|
| 21 |
+
3. 观察状态显示区域。
|
| 22 |
+
4. 等待生成完成。
|
| 23 |
+
- **验证结果:**
|
| 24 |
+
- 在生成过程中,状态区域正确显示了“正在生成...”,并实时更新了生成时间、长度和速度。
|
| 25 |
+
- 生成完成后,状态区域正确显示了“生成完成”。
|
| 26 |
+
- 生成完成后,视图自动切换到了“实时预览”标签页。
|
| 27 |
+
- 功能符合预期。
|
docs/requirements/2025-10-11-21-09-implement-dynamic-gradio-app-generation.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 需求:实现 Gradio 应用的动态生成与预览
|
| 2 |
+
|
| 3 |
+
- **需求描述:**
|
| 4 |
+
在“代码生成”功能区,当用户选择“Gradio 应用”类型时,系统应能接收用户的自然语言需求,调用 AI 模型生成完整的、可运行的 Gradio 应用代码,并在预览区实时展示该应用。
|
| 5 |
+
|
| 6 |
+
- **实现方案:**
|
| 7 |
+
我们将借鉴 `anycoder` 项目的“代码提取 -> 外部执行 -> 界面嵌入”的成熟方案,以确保功能的安全性和稳定性。
|
| 8 |
+
|
| 9 |
+
1. **System Prompt 设计:**
|
| 10 |
+
- 创建一个专门的 `get_gradio_sys_prompt` 函数,用于生成指导模型输出完整、可运行的单文件 Gradio 应用的 System Prompt。
|
| 11 |
+
- **强制要求:**
|
| 12 |
+
- 模型生成的代码必须是自包含的。
|
| 13 |
+
- Gradio 应用实例必须赋值给一个固定的变量名 `demo`。
|
| 14 |
+
- 脚本必须以 `demo.launch()` 结尾。
|
| 15 |
+
- 输出应仅包含 ```python ... ``` 代码块,无额外解释。
|
| 16 |
+
|
| 17 |
+
2. **后端核心逻辑 (位于 `tab_code.py` 的 `generate_code` 函数中):**
|
| 18 |
+
- **子进程管理:**
|
| 19 |
+
- 使用一个全局变量 `gradio_process` 来跟踪当前运行的 Gradio 子进程。
|
| 20 |
+
- 在生成新应用前,通过 `gradio_process.terminate()` 和 `gradio_process.join()` 检查并终止任何已存在的旧进程,以释放端口和资源。
|
| 21 |
+
- **动态端口分配:**
|
| 22 |
+
- 在 `utils.py` 中实现一个 `find_free_port` 函数,该函数通过 `socket` 库绑定一个临时端口来动态查找一个当前未被占用的 TCP 端口。
|
| 23 |
+
- **代码生成与执行:**
|
| 24 |
+
- 从模型返回的流式响应中,实时累积代码,并使用正则表达式 `re.search(r"```python\n(.*?)```", full_code, re.DOTALL)` 提取代码块。
|
| 25 |
+
- 如果未找到代码块,则将整个输出作为代码。
|
| 26 |
+
- **安全检查:** 在执行前,使用 `compile(python_code, '<string>', 'exec')` 验证代码的语法正确性。
|
| 27 |
+
- 将提取的有效代码写入一个临时 Python 文件。
|
| 28 |
+
- 使用 `subprocess.Popen` 在一个隔离的子进程中执行该临时文件,并通过环境变量传递动态分配的端口号。
|
| 29 |
+
- **前端预览:**
|
| 30 |
+
- 子进程成功启动后,等待一个固定的时间(例如10秒)以确保 Gradio 服务完全启动。
|
| 31 |
+
- 后端 `yield` 一个包含 `<iframe>` 的 HTML 字符串给前端。
|
| 32 |
+
- `<iframe>` 的 `src` 属性将指向 `http://127.0.0.1:<动态分配的端口号>`,从而将子进程中运行的应用无缝嵌入到主应用的预览区域。
|
| 33 |
+
- **错误处理:**
|
| 34 |
+
- 如果代码编译失败,向前端返回详细的错误信息。
|
| 35 |
+
|
| 36 |
+
- **创建时间:** 2025-10-11-21-09
|
| 37 |
+
- **状态:** `已完成 (Done)`
|
| 38 |
+
- **重要性:** 中等 (Medium)
|
| 39 |
+
- **验证方式:**
|
| 40 |
+
1. 启动应用,进入“代码生成”标签页。
|
| 41 |
+
2. 选择“Gradio 应用”类型。
|
| 42 |
+
3. 输入“创建一个简单的 Gradio 应用,包含一个输入框和一个输出框”,点击“生成代码”。
|
| 43 |
+
4. 观察状态显示区域和预览区域。
|
| 44 |
+
5. 等待生成完成。
|
| 45 |
+
- **验证结果:**
|
| 46 |
+
- 成功生成了一个简单的 Gradio 应用,该应用回显用户输入。
|
| 47 |
+
- 在“生成的源代码”标签页中,确认了生成的代码是一个简单的回显应用。
|
| 48 |
+
- 在“实时预览”标签页中,对应用进行了测试,输入 "Hello, World!",得到了正确的输出 "Hello, World!"。
|
| 49 |
+
- 确认了该功能确实对接了 LLM 并能生成有效的、功能性的 Gradio 应用。
|
| 50 |
+
- 功能符合预期。
|
docs/requirements/2025-10-12-08-30-implement-workflow-tab.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 需求:实现工作流(Workflow)标签页
|
| 2 |
+
|
| 3 |
+
**重要性:** 中 (Medium)
|
| 4 |
+
|
| 5 |
+
## 1. 目标
|
| 6 |
+
|
| 7 |
+
创建一个名为“工作流”的新标签页,允许用户通过与 AI 对话来定义和可视化一个任务的执行流程。
|
| 8 |
+
|
| 9 |
+
## 2. 功能描述
|
| 10 |
+
|
| 11 |
+
1. **双 Agent 协作模式**:
|
| 12 |
+
* **主 Agent (Ling-1t)**: 在 UI 左侧通过聊天界面与用户直接交互,理解用户意图并帮助其完成任务。
|
| 13 |
+
* **副 Agent (Ling-mini)**: 在后台静默观察主 Agent 与用户的对话,其实时、增量地分析和提取对话中体现出的工作流程。
|
| 14 |
+
|
| 15 |
+
2. **实时工作流可视化**:
|
| 16 |
+
* 在 UI 右侧,实时显示由副 Agent 提取出的“当前主题”和“当前工作流描述”。
|
| 17 |
+
* 工作流的文本描述格式需要定义,初期可使用简单的占位符格式(例如,使用 Markdown 列表)。
|
| 18 |
+
|
| 19 |
+
3. **冷启动与示例**:
|
| 20 |
+
* 为了方便用户上手,需要在聊天输入框下方提供一些预设的示例主题(`gr.Examples`)。
|
| 21 |
+
|
| 22 |
+
## 3. UI 布局
|
| 23 |
+
|
| 24 |
+
* **左侧面板**:
|
| 25 |
+
* `gr.Chatbot`: 用于展示与主 Agent 的对话历史。
|
| 26 |
+
* `gr.Textbox`: 用户输入框。
|
| 27 |
+
* `gr.Button`: 发送按钮。
|
| 28 |
+
* `gr.Examples`: 预设主题示例。
|
| 29 |
+
* **右侧面板**:
|
| 30 |
+
* `gr.Textbox` (或 `gr.Markdown`): 用于显示“当前主题”。
|
| 31 |
+
* `gr.Textbox` (或 `gr.Markdown`): 用于显示“当前工作流描述”。
|
| 32 |
+
|
| 33 |
+
## 4. 开发说明
|
| 34 |
+
|
| 35 |
+
* **必须对接真实 LLM**: 功能逻辑必须调用真实的 LLM API 来生成聊天回复和工作流,不允许使用写死的静态字符串作为占位符。
|
| 36 |
+
* **System Prompts**: 主副 Agent 的 System Prompt 可使用初步的、简单的版本,后续由用户精细化。
|
| 37 |
+
* **用户接手**: 最终的、精细化的 System Prompts 和工作流描述格式将由用户手动开发和指定。
|
| 38 |
+
|
| 39 |
+
## 5. 验证方式
|
| 40 |
+
|
| 41 |
+
1. 启动应用,切换到“工作流”标签页。
|
| 42 |
+
2. 左侧聊天功能可正常使用。
|
| 43 |
+
3. 右侧能看到“主题”和“工作流”的显示区域。
|
| 44 |
+
4. 点击预设示例,聊天框会自动填充。
|
| 45 |
+
5. (手动验证) 在左侧进行对话,右侧的工作流描述会由 **LLM 真实生成**并发生有意义的变化。
|
| 46 |
+
|
| 47 |
+
## 6. 自动化测试方案
|
| 48 |
+
|
| 49 |
+
本方案旨在通过模拟一次完整的用户交互,来端到端地验证“工作流”标签页的核心功能。
|
| 50 |
+
|
| 51 |
+
**测试用例: `test_workflow_tab_dual_agent_interaction`**
|
| 52 |
+
|
| 53 |
+
1. **环境重置 (Setup)**
|
| 54 |
+
* 调用“异常处理与重置流程”,确保浏览器和后台服务处于干净状态。
|
| 55 |
+
* `kill` 掉所有 `app.py` 进程。
|
| 56 |
+
* 后台启动 `app.py`。
|
| 57 |
+
* 等待 5 秒。
|
| 58 |
+
|
| 59 |
+
2. **导航与定位 (Navigation)**
|
| 60 |
+
* 使用 `navigate_page` 打开 `http://127.0.0.1:7860`。
|
| 61 |
+
* 使用 `take_snapshot` 获取页面快照。
|
| 62 |
+
* 从快照中找到 "工作流 (Workflow)" 标签页的 `uid`。
|
| 63 |
+
* 使用 `click` 点击该标签页。
|
| 64 |
+
* 等待 2 秒以确保页面切换完成。
|
| 65 |
+
|
| 66 |
+
3. **执行交互 (Action)**
|
| 67 |
+
* 使用 `take_snapshot` 获取工作流页面的快照。
|
| 68 |
+
* 从快照中定位到聊天输入框的 `uid` (`elem_id="workflow_chat_input"`)。
|
| 69 |
+
* 使用 `fill` 向输入框填入一个清晰的任务,例如: `"我需要一个 Python 函数,它可以接收一个 URL 列表,并返回每个 URL 的 HTTP 状态码。"`。
|
| 70 |
+
* 从快照中定位到“发送”按钮的 `uid` (`elem_id="workflow_send_button"`)。
|
| 71 |
+
* 使用 `click` 点击“发送”按钮。
|
| 72 |
+
|
| 73 |
+
4. **等待与断言 (Assertion)**
|
| 74 |
+
* 等待一个较长的时间(例如 20 秒),以确保双 LLM 调用均已完成。
|
| 75 |
+
* 使用 `take_snapshot` 获取最终的页面快照。
|
| 76 |
+
* **验证主 Agent 回复**:
|
| 77 |
+
* 检查快照中聊天记录(`elem_id="workflow_chatbot"`)部分,确认其中包含了用户的输入以及由 LLM 生成的、非空的的助手回复。
|
| 78 |
+
* **验证副 Agent 分析**:
|
| 79 |
+
* 检查快照中“当前主题”文本框(`elem_id="workflow_topic_output"`),确认其内容已被更新,不再是空的。
|
| 80 |
+
* 检查快照中“当前工作流描述”区域(`elem_id="workflow_output"`),确认其内容已被更新,包含一个 Markdown 格式的、有意义的步骤列表,而不是初始的“*等待任务开始...*”。
|
docs/requirements/2025-10-12-09-05-optimize-workflow-prompts-and-test.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 需求:优化工作流 Agent 的提示词并进行自主循环测试
|
| 2 |
+
|
| 3 |
+
**重要性:** 高 (High)
|
| 4 |
+
|
| 5 |
+
## 1. 目标
|
| 6 |
+
|
| 7 |
+
优化“工作流”标签页中主、副 Agent 的 System Prompt,使其行为更符合设计要求,并能通过一个指定的、由我(Gemini)自主执行的自动化测试场景。
|
| 8 |
+
|
| 9 |
+
## 2. Prompt 优化需求
|
| 10 |
+
|
| 11 |
+
### 2.1 主 Agent (Main Agent)
|
| 12 |
+
- **角色**: 一个冷静、有条理的助手。
|
| 13 |
+
- **行为**: 专注于通过提问来逐步分解和解决用户的当前任务,保持中立和专业的语气,不偏离目标。
|
| 14 |
+
|
| 15 |
+
### 2.2 副 Agent (Sub-Agent)
|
| 16 |
+
- **输出格式**: 必须是一种类似“伪代码”的、描述交互过程的 Markdown 列表。
|
| 17 |
+
- **核心焦点**: 必须聚焦于 **“如何”** 完成任务(即交互的元过程),而不是 **“什么”** 被完成(即任务的具体内容)。
|
| 18 |
+
- **正面例子**: `- agent: 向用户确认交通工具和目的地。`
|
| 19 |
+
- **反面例子**: `- agent: 确认了用户想坐车去北京。`
|
| 20 |
+
- **增量生成**: 工作流的生成必须是增量的。随着对话的进行,在现有列表上追加新步骤,而不是对已有步骤进行大幅修改或重写。
|
| 21 |
+
- **事实接地**: 必须严格基于主 Agent 与用户的对话历史来提取工作流,禁止自行“脑补”或创造不存在的步骤。
|
| 22 |
+
|
| 23 |
+
## 3. 自主验证流程
|
| 24 |
+
|
| 25 |
+
我(Gemini)将扮演用户,并自主、循环地执行以下自动化测试,直到结果稳定满足要求。
|
| 26 |
+
|
| 27 |
+
### 3.1 测试场景
|
| 28 |
+
- **主题**: 规划一次到杭州的旅行。
|
| 29 |
+
- **用户需求模拟**: 我将模拟用户,在对话中逐步提出以下要求:
|
| 30 |
+
1. 要去特定的景点(例如:西湖、灵隐寺)。
|
| 31 |
+
2. 要选择合理的交通工具(例如:高铁、飞机)。
|
| 32 |
+
3. 要确定旅行天数、每日的起床和返回酒店时间。
|
| 33 |
+
4. 在最后,希望能得到一个预估的总预算。
|
| 34 |
+
|
| 35 |
+
### 3.2 自动化测试方案
|
| 36 |
+
1. **环境重置**: 确保一个干净的测试环境(终止旧进程、重启应用)。
|
| 37 |
+
2. **导航**: 导航至“工作流”标签页。
|
| 38 |
+
3. **模拟对话**: 我将通过 `fill` 和 `click` 工具,模拟上述用户需求,与主 Agent 进行至少 4-5 轮对话。
|
| 39 |
+
4. **结果断言**: 在每次对话后,我会捕获快照,并对副 Agent 的输出进行评估。
|
| 40 |
+
5. **循环与提交**:
|
| 41 |
+
- **为每次测试运行创建一个 Commit**: Commit message 将清晰地记录本次测试的结果(例如 `test(workflow): Iteration 1 failed, workflow is not abstract` 或 `test(workflow): Iteration 2 success, abstraction level is good`)。
|
| 42 |
+
- **自我修正**: 如果断言失败,我将返回代码修改步骤,调整 Prompts,然后开始新一一轮的测试。
|
| 43 |
+
|
| 44 |
+
### 3.3 成功标准
|
| 45 |
+
1. **可提取性**: 工作流能够被成功提取并显示出来。
|
| 46 |
+
2. **抽象性与通用性**: 提取出的工作流步骤足够通用和抽象,描述的是交互“动作”,而不是具体“数值”。
|
| 47 |
+
3. **增量性与事实性**: 工作流是逐步增加的,且每一步都明确对应到某一次对话交互中。
|
docs/requirements/2025-10-12-09-20-add-mermaid-workflow-visualization.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 需求:新增 Mermaid 流程图可视化
|
| 2 |
+
|
| 3 |
+
**重要性:** 中 (Medium)
|
| 4 |
+
|
| 5 |
+
## 1. 目标
|
| 6 |
+
|
| 7 |
+
在“工作流”标签页,将由副 Agent 提取出的文本工作流,实时渲染成一个可视化的 Mermaid 流程图。
|
| 8 |
+
|
| 9 |
+
## 2. 功能描述
|
| 10 |
+
|
| 11 |
+
1. **UI 变更**:
|
| 12 |
+
* 在“工作流”标签页的右侧面板,即“当前工作流描述”下方,新增一个用于显示流程图的区域。
|
| 13 |
+
* 此区域将使用 `gr.HTML` 组件实现。
|
| 14 |
+
|
| 15 |
+
2. **后端逻辑**:
|
| 16 |
+
* 需要创建一个辅助函数,其功能是将 Markdown 格式的步骤列表(例如 `- step 1\n- step 2`)转换成 Mermaid 的 `graph TD` 流程图语法(例如 `graph TD; A["step 1"] --> B["step 2"];`)。
|
| 17 |
+
* `handle_workflow_chat` 函数需要调用此辅助函数,在生成文本工作流后,立即生成对应的 Mermaid 语法。
|
| 18 |
+
* `handle_workflow_chat` 的返回值将增加一个,用于承载生成的 Mermaid HTML 代码。
|
| 19 |
+
|
| 20 |
+
3. **渲染实现**:
|
| 21 |
+
* 返回的 Mermaid 语法需要被包裹在一个完整的 HTML 结构中,该结构包含 Mermaid.js 库的 `<script>` 标签和一个用于渲染的 `<div class="mermaid">` 容器。
|
| 22 |
+
* 这个完整的 HTML 字符串将作为 `gr.HTML` 组件的值被更新。
|
| 23 |
+
|
| 24 |
+
## 3. 自动化测试方案
|
| 25 |
+
|
| 26 |
+
1. **执行现有测试**: 复用 `test_workflow_tab_dual_agent_interaction` 测试用例,与 Agent 进行一次简单的交互。
|
| 27 |
+
2. **增加断言**:
|
| 28 |
+
* 在最终的快照中,定位到新增的 `gr.HTML` 组件。
|
| 29 |
+
* 验证该组件的 `value` 是否包含有效的 Mermaid `graph` 定义。
|
| 30 |
+
* 验证其 `value` 是否包含 Mermaid.js 的 `<script>` 标签。
|
docs/requirements/2025-10-12-22-38-prevent-premature-code-preview-refresh.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## 需求:防止代码生成过程中的过早预览刷新
|
| 2 |
+
|
| 3 |
+
**重要性:** 高
|
| 4 |
+
|
| 5 |
+
### 1. 问题描述 (Problem)
|
| 6 |
+
|
| 7 |
+
在代码生成功能中,当前的逻辑是每当代码块有任何更新,都会立即触发右侧“实时预览” `<iframe>` 的刷新。当模型快速流式输出大量代码时,这会导致 `<iframe>` 在极短时间内被高频刷新,不仅在视觉上造成闪烁,更严重的是会迅速消耗大量浏览器内存,可能导致页面崩溃。
|
| 8 |
+
|
| 9 |
+
### 2. 解决方案 (Solution)
|
| 10 |
+
|
| 11 |
+
我们需要引入一个控制机制,只在代码完全生成结束后,才对“实时预览”进行刷新。在生成过程中,只更新“生成的源代码”标签页的内容。
|
| 12 |
+
|
| 13 |
+
**实现思路:**
|
| 14 |
+
|
| 15 |
+
1. **修改 `tab_code.py` 中的 `generate_code` 生成器函数。**
|
| 16 |
+
2. 在生成器循环内部,每次 `yield` 时,只更新源代码显示组件 (`gr.Code`) 的值。
|
| 17 |
+
3. 将预览组件 (`gr.HTML`) 的更新操作移出循环,在生成器函数返回(即循环结束后)时执行一次。
|
| 18 |
+
4. 利用 Gradio `yield` 可以返回字典来更新多个组件的特性。在循环中,`yield` 的字典只包含源代码组件;在函数末尾,返回一个包含最终预览内容的字典来单独更新预览组件。
|
| 19 |
+
|
| 20 |
+
### 3. 验证方式 (Verification)
|
| 21 |
+
|
| 22 |
+
1. 启动应用,进入“代码生成”标签页。
|
| 23 |
+
2. 选择一个会生成大量代码的预设,例如“漂浮的岛屿”。
|
| 24 |
+
3. 点击“生成代码”按钮。
|
| 25 |
+
4. **预期行为 1 (过程中):** 在代码生成期间,观察到“生成的源代码”标签页中的代码在持续增加,但切换到“实时预览”标签页,其内容保持不变(或显示上一次生成的结果),没有出现闪烁或刷新。
|
| 26 |
+
5. **预期行为 2 (结束后):** 当代码完全生成后,“实时预览”标签页自动刷新一次,并正确显示最终的渲染效果。
|
docs/requirements/2025-10-13-10-21-fix-workflow-chat-context.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
重要性: 高
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
# 需求: 修复工作流 Tab 中用户输入无法进入模型上下文的 Bug
|
| 6 |
+
|
| 7 |
+
## 1. 问题描述
|
| 8 |
+
|
| 9 |
+
在“工作流 (Workflow)”功能区,用户在主 Agent 聊天框中输入任务后,该输入没有被正确地加入到与大模型的对话历史(`history`)中。这导致模型无法获知用户的具体需求,只会反复询问“您希望首先解决什么?”,而无法根据用户的输入生成有效的工作流。
|
| 10 |
+
|
| 11 |
+
## 2. 需求目标
|
| 12 |
+
|
| 13 |
+
修复此 Bug,确保用户在工作流 Tab 中的输入能够被正确捕获,并作为上下文传递给 `ling_chat_workflow` 函数,使模型能够理解用户意图并生成相应的执行计划。
|
| 14 |
+
|
| 15 |
+
## 3. 验收标准 (验证方式)
|
| 16 |
+
|
| 17 |
+
1. **启动应用:** 运行 `app.py`。
|
| 18 |
+
2. **导航到工作流 Tab:** 点击界面上的“工作流 (Workflow)”标签页。
|
| 19 |
+
3. **输入任务:** 在左侧的“输入你的任务...”文本框中,输入具体的任务描述,例如:“写一个在网页上显示‘你好,世界!’的 Python Flask 应用”。
|
| 20 |
+
4. **点击发送:** 点击“发送”按钮。
|
| 21 |
+
5. **验证模型响应:**
|
| 22 |
+
* **预期结果:** 模型不再询问用户需要做什么,而是直接针对用户输入的任务,开始生成工作流或提出澄清问题。例如,它可能会回复:“好的,我们来创建一个 Flask 应用。第一步是...”。
|
| 23 |
+
* **错误结果 (当前状态):** 模型回复:“您希望首先解决什么?请告诉我具体情况。”
|
| 24 |
+
6. **验证工作流状态:**
|
| 25 |
+
* **预期结果:** 右侧的“实时工作流”区域会根据模型的响应,显示出与任务相关的初始状态或图表。
|
| 26 |
+
* **错误结果 (当前状态):** 右侧区域显示的是一个通用的、与用户输入无关的初始状态。
|
| 27 |
+
|
| 28 |
+
## 4. 技术实现思路
|
| 29 |
+
|
| 30 |
+
- **问题定位:** `tab_workflow.py` 文件中的 `send_button.click()` 事件监听器是问题的关键。
|
| 31 |
+
- **分析:** 当前的实现中,`ling_chat_workflow` 函数在被调用时,虽然接收了 `user_input` 和 `chatbot` 作为参数,但在函数内部,`user_input` 并未被正确地追加到 `chatbot` 的历史记录中。
|
| 32 |
+
- **修复方案:** 在 `ling_chat_workflow` 函数的开头,需要手动将用户的输入 `user_input` 和一个空的响应(`None` 或空字符串)作为一个元组 `(user_input, None)` 追加到 `history` 列表中。这样,后续模型生成时,就能看到用户的最新提问。
|
| 33 |
+
|
docs/requirements/2025-10-13-18-32-update-chat-model-list.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
重要性: 高
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
# 需求:更新聊天模型列表
|
| 6 |
+
|
| 7 |
+
## 1. 新增模型
|
| 8 |
+
|
| 9 |
+
在聊天模型的选择列表中,新增一个模型,具体信息如下:
|
| 10 |
+
|
| 11 |
+
- **对外显示名称**: `Something New`
|
| 12 |
+
- **AntChat 内部模型 ID**: `Ring-1T`
|
| 13 |
+
- **项目内部唯一 ID**: `inclusionai/ring-1t`
|
| 14 |
+
|
| 15 |
+
## 2. 更新 Emoji
|
| 16 |
+
|
| 17 |
+
更新模型选择列表中,各个模型名称前缀的 Emoji:
|
| 18 |
+
|
| 19 |
+
- **Ling 系列**: 使用 `🦉`
|
| 20 |
+
- **Ring 系列**: 使用 `💍️`
|
| 21 |
+
- **Ming 系列**: 使用 `🌟` (如果未来有)
|
| 22 |
+
|
| 23 |
+
# 验证方式
|
| 24 |
+
|
| 25 |
+
1. 启动应用。
|
| 26 |
+
2. 导航到 “聊天” 标签页。
|
| 27 |
+
3. 检查 “选择模型” 区域的单选按钮列表。
|
| 28 |
+
4. **断言**:
|
| 29 |
+
- 列表中必须存在一个选项,其标签为 `💍️ Something New`。
|
| 30 |
+
- “Ling-1T” 模型的标签应更新为 `🦉 Ling-1T (1T)`。
|
| 31 |
+
- “Ling-flash-2.0” 模型的标签应更新为 `🦉 Ling-flash-2.0 (103B)`。
|
| 32 |
+
- “Ring-flash-2.0” 模型的标签应更新为 `💍️ Ring-flash-2.0 (103B)`。
|
| 33 |
+
- “Ling-mini-2.0” 模型的标签应更新为 `🦉 Ling-mini-2.0 (16B)`。
|
| 34 |
+
- “Ring-mini-2.0” 模型的标签应更新为 `💍️ Ring-mini-2.0 (3B)`。
|
docs/requirements/2025-10-13-23-39-update-chat-models.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
重要性: 高
|
| 3 |
+
状态: 已完成
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# 需求: 更新聊天模型列表与显示
|
| 7 |
+
|
| 8 |
+
## 1. 新增模型
|
| 9 |
+
|
| 10 |
+
- **模型显示名称:** Something New
|
| 11 |
+
- **模型内部 ID:** `ring-1t`
|
| 12 |
+
- **Provider Model ID:**
|
| 13 |
+
- `zenmux`: `inclusionai/ring-1t`
|
| 14 |
+
- `antchat`: `Ring-1T`
|
| 15 |
+
- **描述:** A new powerful model from Ring series.
|
| 16 |
+
|
| 17 |
+
## 2. 更新模型名称 Emoji
|
| 18 |
+
|
| 19 |
+
- **Ling 系列:** `🦉`
|
| 20 |
+
- **Ring 系列:** `💍️`
|
| 21 |
+
- **Ming 系列:** `🌟`
|
| 22 |
+
|
| 23 |
+
## 验证方式
|
| 24 |
+
|
| 25 |
+
1. 启动应用。
|
| 26 |
+
2. 导航到“聊天”标签页。
|
| 27 |
+
3. **断言 1:** 模型选择器中应出现 "💍️ Something New" 选项。
|
| 28 |
+
4. **断言 2:** 检查其他模型,确认 Ling 系列模型的名称前有 "🦉" emoji。
|
| 29 |
+
5. **断言 3:** 检查其他模型,确认 Ring 系列模型的名称前有 "💍️" emoji。
|
| 30 |
+
6. **断言 4:** 检查其他模型,确认 Ming 系列模型的名称前有 "🌟" emoji。
|
docs/requirements/2025-10-14-00-40-add-credits.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Requirement: Add Credits for ZenMux and AnyCoder
|
| 2 |
+
|
| 3 |
+
**Goal:** To give proper credit to the services and resources used in this application.
|
| 4 |
+
|
| 5 |
+
**Changes:**
|
| 6 |
+
- Added a credit section at the top of the main page, just above the tabs.
|
| 7 |
+
- The credit section includes links to ZenMux and AnyCoder.
|
| 8 |
+
|
| 9 |
+
**File Modified:**
|
| 10 |
+
- `app.py`
|
| 11 |
+
|
| 12 |
+
**Verification:**
|
| 13 |
+
- The credit text and links should be visible on the main page of the application.
|
docs/uncategorized/development_todo.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
# Ling & Ring Playground - Development TODO
|
| 2 |
-
|
| 3 |
-
## 任务: 实现代码生成 Tab (`tab_code.py`)
|
| 4 |
-
|
| 5 |
-
### 1. UI 构建
|
| 6 |
-
- [ ] 在 `tab_code.py` 中创建 `create_code_tab` 函数。
|
| 7 |
-
- [ ] 添加 `gr.Radio` 组件,提供 "静态页面" 和 "Gradio 应用" 选项。
|
| 8 |
-
- [ ] 添加 `gr.Textbox` 作为用户 Prompt 输入框。
|
| 9 |
-
- [ ] 添加 `gr.Button` 用于触发生成。
|
| 10 |
-
- [ ] 添加 `gr.Code` 组件用于显示生成的源代码。
|
| 11 |
-
- [ ] 添加 `gr.HTML` 组件用于实时预览。
|
| 12 |
-
|
| 13 |
-
### 2. 后端逻辑
|
| 14 |
-
- [ ] 为 "静态页面" 编写 System Prompt。
|
| 15 |
-
- [ ] 为 "Gradio 应用" 编写 System Prompt。
|
| 16 |
-
- [ ] 实现按钮点击事件的处理函数。
|
| 17 |
-
- [ ] **静态页面逻辑**:
|
| 18 |
-
- [ ] 调用 Ring 模型生成 HTML。
|
| 19 |
-
- [ ] 将返回的 HTML 字符串直接更新到 `gr.HTML` 组件。
|
| 20 |
-
- [ ] **Gradio 应用逻辑**:
|
| 21 |
-
- [ ] 调用 Ring 模型生成 Python 代码。
|
| 22 |
-
- [ ] 将代码保存到临时文件。
|
| 23 |
-
- [ ] 使用 `subprocess` 在后台启动独立的 Gradio 应用。
|
| 24 |
-
- [ ] 捕获子进程输出,解析出本地 URL。
|
| 25 |
-
- [ ] 将 URL 加载到 `gr.HTML` 的 `<iframe>` 中。
|
| 26 |
-
- [ ] 实现子进程管理(启动/终止)。
|
| 27 |
-
|
| 28 |
-
### 3. 应用整合
|
| 29 |
-
- [ ] 在 `app.py` 中导入 `create_code_tab`。
|
| 30 |
-
- [ ] 在 `gr.Blocks` 中添加一个新的 `gr.Tab("代码生成")`。
|
| 31 |
-
- [ ] 在新 Tab 中调用 `create_code_tab()`。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
models.py
CHANGED
|
@@ -14,8 +14,21 @@ def get_model_response(model_id, history, system_prompt, temperature, escape_htm
|
|
| 14 |
"""
|
| 15 |
与 AntChat API 交互以获取模型响应。
|
| 16 |
"""
|
|
|
|
| 17 |
# The model_id passed in is now the ground truth, potentially overridden by local.py
|
| 18 |
api_model_id = CHAT_MODEL_SPECS[model_id]["model_id"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
headers = {
|
| 21 |
"Authorization": f"Bearer {ANTCHAT_API_KEY}",
|
|
@@ -32,6 +45,8 @@ def get_model_response(model_id, history, system_prompt, temperature, escape_htm
|
|
| 32 |
if assistant_msg:
|
| 33 |
messages.append({"role": "assistant", "content": assistant_msg})
|
| 34 |
|
|
|
|
|
|
|
| 35 |
json_data = {
|
| 36 |
"model": api_model_id,
|
| 37 |
"messages": messages,
|
|
@@ -88,40 +103,17 @@ def generate_code_for_tab(system_prompt, user_prompt, code_type, model_choice):
|
|
| 88 |
"""
|
| 89 |
logger.info(f"为 '{code_type}' 类型生成代码,Prompt: '{user_prompt}', Model: '{model_choice}'")
|
| 90 |
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
model_name = "inclusionai/ring-flash-2.0"
|
| 97 |
-
else:
|
| 98 |
-
# 默认或备用模型
|
| 99 |
-
model_name = "inclusionai/ling-1t"
|
| 100 |
-
logger.warning(f"未知的模型选项 '{model_choice}', 回退到默认模型 'inclusionai/ling-1t'")
|
| 101 |
-
|
| 102 |
-
history = [[user_prompt, None]]
|
| 103 |
-
temperature = 0.7
|
| 104 |
-
# For code, we don't want to escape HTML entities
|
| 105 |
-
yield from get_model_response(model_name, history, system_prompt, temperature, escape_html=False)
|
| 106 |
-
|
| 107 |
-
elif code_type == "Gradio 应用":
|
| 108 |
-
# Currently mocked
|
| 109 |
-
yield f"""
|
| 110 |
-
import gradio as gr
|
| 111 |
-
|
| 112 |
-
def greet(name):
|
| 113 |
-
return f"Hello, {user_prompt} a.k.a. {{name}}!"
|
| 114 |
-
|
| 115 |
-
with gr.Blocks() as demo:
|
| 116 |
-
gr.Markdown("## Simple Greeting App")
|
| 117 |
-
name_input = gr.Textbox(label="Enter your name")
|
| 118 |
-
greet_button = gr.Button("Greet")
|
| 119 |
-
output_text = gr.Textbox(label="Output")
|
| 120 |
-
|
| 121 |
-
greet_button.click(fn=greet, inputs=name_input, outputs=output_text)
|
| 122 |
-
|
| 123 |
-
demo.launch()
|
| 124 |
-
"""
|
| 125 |
else:
|
| 126 |
-
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
"""
|
| 15 |
与 AntChat API 交互以获取模型响应。
|
| 16 |
"""
|
| 17 |
+
print(f"[get_model_response] History: {history}")
|
| 18 |
# The model_id passed in is now the ground truth, potentially overridden by local.py
|
| 19 |
api_model_id = CHAT_MODEL_SPECS[model_id]["model_id"]
|
| 20 |
+
|
| 21 |
+
# 如果 local.py 存在 get_local_model_id_map 函数,则使用它来覆盖模型 ID
|
| 22 |
+
try:
|
| 23 |
+
from local import get_local_model_id_map
|
| 24 |
+
local_map = get_local_model_id_map()
|
| 25 |
+
if model_id in local_map:
|
| 26 |
+
api_model_id = local_map[model_id]
|
| 27 |
+
logger.info(f"使用本地模型 ID 映射: {model_id} -> {api_model_id}")
|
| 28 |
+
except ImportError:
|
| 29 |
+
logger.info("local.py 未找到,使用默认模型 ID 映射。")
|
| 30 |
+
except Exception as e:
|
| 31 |
+
logger.error(f"获取本地模型 ID 映射时出错: {e}")
|
| 32 |
|
| 33 |
headers = {
|
| 34 |
"Authorization": f"Bearer {ANTCHAT_API_KEY}",
|
|
|
|
| 45 |
if assistant_msg:
|
| 46 |
messages.append({"role": "assistant", "content": assistant_msg})
|
| 47 |
|
| 48 |
+
print(f"[get_model_response] Messages: {messages}")
|
| 49 |
+
|
| 50 |
json_data = {
|
| 51 |
"model": api_model_id,
|
| 52 |
"messages": messages,
|
|
|
|
| 103 |
"""
|
| 104 |
logger.info(f"为 '{code_type}' 类型生成代码,Prompt: '{user_prompt}', Model: '{model_choice}'")
|
| 105 |
|
| 106 |
+
# 从 UI 的选项中解析出模型名称
|
| 107 |
+
if "Ling-1T" in model_choice:
|
| 108 |
+
model_name = "ling-1t"
|
| 109 |
+
elif "Ring-flash-2.0" in model_choice:
|
| 110 |
+
model_name = "ring-flash-2.0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
else:
|
| 112 |
+
# 默认或备用模型
|
| 113 |
+
model_name = "inclusionai/ling-1t"
|
| 114 |
+
logger.warning(f"未知的模型选项 '{model_choice}', 回退到默认模型 'inclusionai/ling-1t'")
|
| 115 |
+
|
| 116 |
+
history = [[user_prompt, None]]
|
| 117 |
+
temperature = 0.7
|
| 118 |
+
# For code, we don't want to escape HTML entities
|
| 119 |
+
yield from get_model_response(model_name, history, system_prompt, temperature, escape_html=False)
|
tab_chat.py
CHANGED
|
@@ -9,20 +9,20 @@ logger = logging.getLogger(__name__)
|
|
| 9 |
# --- Backend Logic ---
|
| 10 |
|
| 11 |
def handle_chat(message, history, system_prompt, temperature, model_id):
|
| 12 |
-
"""
|
| 13 |
logger.debug(f"handle_chat 输入: message={message}, history={history}, system_prompt={system_prompt}, temperature={temperature}, model_id={model_id}")
|
| 14 |
if history is None:
|
| 15 |
history = []
|
| 16 |
history = copy.deepcopy(history)
|
| 17 |
history.append((message, ""))
|
| 18 |
|
| 19 |
-
#
|
| 20 |
model_display_name = CHAT_MODEL_SPECS.get(model_id, {}).get("display_name", model_id)
|
| 21 |
|
| 22 |
is_first_chunk = True
|
| 23 |
for chunk in get_model_response(model_id, history, system_prompt, temperature):
|
| 24 |
if is_first_chunk:
|
| 25 |
-
#
|
| 26 |
history[-1] = (message, f"**{model_display_name}**\n\n" + chunk)
|
| 27 |
is_first_chunk = False
|
| 28 |
else:
|
|
@@ -32,17 +32,17 @@ def handle_chat(message, history, system_prompt, temperature, model_id):
|
|
| 32 |
# --- UI Event Handlers ---
|
| 33 |
|
| 34 |
def handle_model_change(model_id):
|
| 35 |
-
"""
|
| 36 |
spec = CHAT_MODEL_SPECS[model_id]
|
| 37 |
scenarios = spec.get("prompt_scenarios", [])
|
| 38 |
|
| 39 |
-
#
|
| 40 |
if scenarios:
|
| 41 |
first_scenario = scenarios[0]
|
| 42 |
scenario_titles = [[s["title"]] for s in scenarios]
|
| 43 |
message_examples = [[m] for m in first_scenario["message_examples"]]
|
| 44 |
system_prompt_value = first_scenario["system_prompt"]
|
| 45 |
-
else: #
|
| 46 |
scenario_titles = []
|
| 47 |
message_examples = []
|
| 48 |
system_prompt_value = ""
|
|
@@ -55,12 +55,12 @@ def handle_model_change(model_id):
|
|
| 55 |
)
|
| 56 |
|
| 57 |
def handle_scenario_selection(model_id, evt: gr.SelectData):
|
| 58 |
-
"""
|
| 59 |
logger.debug(f"--- Scenario Selection Event ---")
|
| 60 |
logger.debug(f"Selected event value: {evt.value}")
|
| 61 |
logger.debug(f"Type of event value: {type(evt.value)}")
|
| 62 |
|
| 63 |
-
#
|
| 64 |
selected_title = evt.value[0] if isinstance(evt.value, list) and evt.value else None
|
| 65 |
if not selected_title:
|
| 66 |
logger.error("Selected event value is not a valid list or is empty.")
|
|
@@ -81,65 +81,64 @@ def handle_scenario_selection(model_id, evt: gr.SelectData):
|
|
| 81 |
return gr.update(value=system_prompt_value), gr.update(samples=message_examples)
|
| 82 |
|
| 83 |
logger.warning(f"No matching scenario found for title: '{selected_title}'")
|
| 84 |
-
#
|
| 85 |
return gr.update(), gr.update()
|
| 86 |
|
| 87 |
# --- UI Creation ---
|
| 88 |
|
| 89 |
def create_chat_tab():
|
| 90 |
-
"""
|
| 91 |
|
| 92 |
-
#
|
| 93 |
-
# choices
|
| 94 |
model_choices = [(spec["display_name"], model_id) for model_id, spec in CHAT_MODEL_SPECS.items()]
|
| 95 |
default_model_id = list(CHAT_MODEL_SPECS.keys())[0]
|
| 96 |
default_spec = CHAT_MODEL_SPECS[default_model_id]
|
| 97 |
default_scenarios = default_spec.get("prompt_scenarios", [])
|
| 98 |
-
|
| 99 |
-
with gr.
|
| 100 |
-
with gr.
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
headers=["选择一个角色或任务来开始:"],
|
| 120 |
-
)
|
| 121 |
-
# 消息示例
|
| 122 |
-
message_examples_display = gr.Dataset(
|
| 123 |
-
components=[chat_input],
|
| 124 |
-
samples=[[m] for m in default_scenarios[0]["message_examples"]] if default_scenarios else [],
|
| 125 |
-
label="消息示例",
|
| 126 |
-
headers=["然后,试试这些具体问题:"],
|
| 127 |
-
)
|
| 128 |
-
|
| 129 |
-
with gr.Column(scale=1):
|
| 130 |
-
model_selector = gr.Radio(
|
| 131 |
-
choices=model_choices,
|
| 132 |
-
label="选择模型",
|
| 133 |
-
value=default_model_id
|
| 134 |
)
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
)
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
# --- Event Listeners ---
|
| 145 |
model_selector.change(
|
|
@@ -167,4 +166,4 @@ def create_chat_tab():
|
|
| 167 |
"system_prompt": system_prompt,
|
| 168 |
"temperature_slider": temperature_slider,
|
| 169 |
"model_selector": model_selector,
|
| 170 |
-
}
|
|
|
|
| 9 |
# --- Backend Logic ---
|
| 10 |
|
| 11 |
def handle_chat(message, history, system_prompt, temperature, model_id):
|
| 12 |
+
"""Core function to handle chat message submission"""
|
| 13 |
logger.debug(f"handle_chat 输入: message={message}, history={history}, system_prompt={system_prompt}, temperature={temperature}, model_id={model_id}")
|
| 14 |
if history is None:
|
| 15 |
history = []
|
| 16 |
history = copy.deepcopy(history)
|
| 17 |
history.append((message, ""))
|
| 18 |
|
| 19 |
+
# Get the display name from the spec
|
| 20 |
model_display_name = CHAT_MODEL_SPECS.get(model_id, {}).get("display_name", model_id)
|
| 21 |
|
| 22 |
is_first_chunk = True
|
| 23 |
for chunk in get_model_response(model_id, history, system_prompt, temperature):
|
| 24 |
if is_first_chunk:
|
| 25 |
+
# Add model name before the first chunk
|
| 26 |
history[-1] = (message, f"**{model_display_name}**\n\n" + chunk)
|
| 27 |
is_first_chunk = False
|
| 28 |
else:
|
|
|
|
| 32 |
# --- UI Event Handlers ---
|
| 33 |
|
| 34 |
def handle_model_change(model_id):
|
| 35 |
+
"""Update UI when user switches model"""
|
| 36 |
spec = CHAT_MODEL_SPECS[model_id]
|
| 37 |
scenarios = spec.get("prompt_scenarios", [])
|
| 38 |
|
| 39 |
+
# Load the first scenario by default
|
| 40 |
if scenarios:
|
| 41 |
first_scenario = scenarios[0]
|
| 42 |
scenario_titles = [[s["title"]] for s in scenarios]
|
| 43 |
message_examples = [[m] for m in first_scenario["message_examples"]]
|
| 44 |
system_prompt_value = first_scenario["system_prompt"]
|
| 45 |
+
else: # Compatible with no scenarios
|
| 46 |
scenario_titles = []
|
| 47 |
message_examples = []
|
| 48 |
system_prompt_value = ""
|
|
|
|
| 55 |
)
|
| 56 |
|
| 57 |
def handle_scenario_selection(model_id, evt: gr.SelectData):
|
| 58 |
+
"""Update UI when user selects a scenario from the dataset"""
|
| 59 |
logger.debug(f"--- Scenario Selection Event ---")
|
| 60 |
logger.debug(f"Selected event value: {evt.value}")
|
| 61 |
logger.debug(f"Type of event value: {type(evt.value)}")
|
| 62 |
|
| 63 |
+
# Correction: extract string from list
|
| 64 |
selected_title = evt.value[0] if isinstance(evt.value, list) and evt.value else None
|
| 65 |
if not selected_title:
|
| 66 |
logger.error("Selected event value is not a valid list or is empty.")
|
|
|
|
| 81 |
return gr.update(value=system_prompt_value), gr.update(samples=message_examples)
|
| 82 |
|
| 83 |
logger.warning(f"No matching scenario found for title: '{selected_title}'")
|
| 84 |
+
# If no scenario is found, do not update
|
| 85 |
return gr.update(), gr.update()
|
| 86 |
|
| 87 |
# --- UI Creation ---
|
| 88 |
|
| 89 |
def create_chat_tab():
|
| 90 |
+
"""Create and return all Gradio components for the chat tab"""
|
| 91 |
|
| 92 |
+
# Extract model information from config for UI display
|
| 93 |
+
# choices is a list of (display_name, model_id) tuples
|
| 94 |
model_choices = [(spec["display_name"], model_id) for model_id, spec in CHAT_MODEL_SPECS.items()]
|
| 95 |
default_model_id = list(CHAT_MODEL_SPECS.keys())[0]
|
| 96 |
default_spec = CHAT_MODEL_SPECS[default_model_id]
|
| 97 |
default_scenarios = default_spec.get("prompt_scenarios", [])
|
| 98 |
+
|
| 99 |
+
with gr.Row():
|
| 100 |
+
with gr.Column(scale=3):
|
| 101 |
+
chatbot = gr.Chatbot(
|
| 102 |
+
label="Chat Window",
|
| 103 |
+
bubble_full_width=False,
|
| 104 |
+
height=500,
|
| 105 |
+
value=[(None, "Hello! I'm Ling. Try selecting a scenario and a message example below to get started.")]
|
| 106 |
+
)
|
| 107 |
+
with gr.Row():
|
| 108 |
+
chat_input = gr.Textbox(placeholder="Ask me anything...", label="Input", show_label=False, scale=4)
|
| 109 |
+
send_button = gr.Button("Send", variant="primary", scale=1)
|
| 110 |
+
|
| 111 |
+
# 新的场景化示例区域
|
| 112 |
+
with gr.Accordion("✨ Try these scenarios...", open=True):
|
| 113 |
+
# 场景选择器
|
| 114 |
+
scenario_selector = gr.Dataset(
|
| 115 |
+
components=[gr.Textbox(visible=False)],
|
| 116 |
+
samples=[[s["title"]] for s in default_scenarios],
|
| 117 |
+
label="System Prompt Examples",
|
| 118 |
+
headers=["Select a role or task to get started:"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
)
|
| 120 |
+
# 消息示例
|
| 121 |
+
message_examples_display = gr.Dataset(
|
| 122 |
+
components=[chat_input],
|
| 123 |
+
samples=[[m] for m in default_scenarios[0]["message_examples"]] if default_scenarios else [],
|
| 124 |
+
label="Message Examples",
|
| 125 |
+
headers=["Then, try these specific questions:"],
|
| 126 |
)
|
| 127 |
+
|
| 128 |
+
with gr.Column(scale=1):
|
| 129 |
+
model_selector = gr.Radio(
|
| 130 |
+
choices=model_choices,
|
| 131 |
+
label="Select Model",
|
| 132 |
+
value=default_model_id
|
| 133 |
+
)
|
| 134 |
+
model_description = gr.Markdown(default_spec["description"])
|
| 135 |
+
system_prompt = gr.Textbox(
|
| 136 |
+
label="System Prompt",
|
| 137 |
+
lines=8,
|
| 138 |
+
placeholder=CHAT_SYSTEM_PROMPT_PLACEHOLDER,
|
| 139 |
+
value=default_scenarios[0]["system_prompt"] if default_scenarios else ""
|
| 140 |
+
)
|
| 141 |
+
temperature_slider = gr.Slider(minimum=0.0, maximum=2.0, value=1.0, step=0.1, label="Temperature")
|
| 142 |
|
| 143 |
# --- Event Listeners ---
|
| 144 |
model_selector.change(
|
|
|
|
| 166 |
"system_prompt": system_prompt,
|
| 167 |
"temperature_slider": temperature_slider,
|
| 168 |
"model_selector": model_selector,
|
| 169 |
+
}
|
tab_code.py
CHANGED
|
@@ -1,42 +1,52 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
import
|
| 3 |
-
import threading
|
| 4 |
-
import queue
|
| 5 |
-
import uuid
|
| 6 |
import os
|
| 7 |
-
import tempfile
|
| 8 |
import sys
|
| 9 |
-
import
|
| 10 |
import time
|
|
|
|
|
|
|
|
|
|
| 11 |
from models import generate_code_for_tab
|
|
|
|
| 12 |
|
| 13 |
-
#
|
| 14 |
-
|
| 15 |
|
| 16 |
-
#
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def stop_process(session_id):
|
| 20 |
-
"""停止与特定会话关联的子进程"""
|
| 21 |
-
process = running_processes.get(session_id)
|
| 22 |
-
if process and process.poll() is None:
|
| 23 |
-
process.terminate()
|
| 24 |
-
process.wait()
|
| 25 |
-
print(f"Terminated process for session {session_id}")
|
| 26 |
-
if session_id in running_processes:
|
| 27 |
-
del running_processes[session_id]
|
| 28 |
|
| 29 |
def get_gradio_sys_prompt():
|
| 30 |
-
"""
|
| 31 |
return """
|
| 32 |
You are an expert Gradio developer. Create a complete, runnable, single-file Gradio application based on the user's request.
|
| 33 |
The code must be self-contained in a single Python script.
|
| 34 |
-
The
|
| 35 |
-
|
|
|
|
| 36 |
"""
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
def get_html_sys_prompt():
|
| 39 |
-
"""
|
| 40 |
return """
|
| 41 |
You are an expert front-end developer. Create a complete, modern, and responsive single HTML file based on the user's request.
|
| 42 |
The file must be self-contained, including all necessary HTML, CSS, and JavaScript.
|
|
@@ -44,7 +54,7 @@ Do not include any explanations, just the raw HTML code.
|
|
| 44 |
"""
|
| 45 |
|
| 46 |
def run_gradio_in_thread(code, url_queue, session_id):
|
| 47 |
-
"""
|
| 48 |
temp_dir = tempfile.mkdtemp()
|
| 49 |
file_path = os.path.join(temp_dir, "app.py")
|
| 50 |
with open(file_path, "w") as f:
|
|
@@ -76,7 +86,7 @@ def run_gradio_in_thread(code, url_queue, session_id):
|
|
| 76 |
print(f"Error cleaning up temp files: {e}")
|
| 77 |
|
| 78 |
def get_spinner_html():
|
| 79 |
-
"""
|
| 80 |
return """
|
| 81 |
<div style="width: 100%; height: 600px; display: flex; justify-content: center; align-items: center; border: 1px solid #ddd; background-color: #f9f9f9;">
|
| 82 |
<div class="spinner"></div>
|
|
@@ -97,27 +107,83 @@ def get_spinner_html():
|
|
| 97 |
</style>
|
| 98 |
"""
|
| 99 |
|
| 100 |
-
def generate_code(code_type, model_choice, user_prompt
|
| 101 |
-
"""
|
|
|
|
| 102 |
logger.info(f"--- [Code Generation] Start ---")
|
| 103 |
logger.info(f"Code Type: {code_type}, Model: {model_choice}, Prompt: '{user_prompt}'")
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
if not user_prompt:
|
| 108 |
-
yield "Please enter a prompt.", gr.HTML("Preview will appear here.")
|
| 109 |
return
|
| 110 |
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
system_prompt = get_html_sys_prompt()
|
| 113 |
full_code_with_think = ""
|
| 114 |
full_code_for_preview = ""
|
| 115 |
buffer = ""
|
| 116 |
is_thinking = False
|
| 117 |
-
last_update_time = 0
|
| 118 |
|
| 119 |
-
yield "", gr.HTML(get_spinner_html())
|
| 120 |
-
|
| 121 |
# The model's raw output is streamed here
|
| 122 |
for code_chunk in generate_code_for_tab(system_prompt, user_prompt, code_type, model_choice):
|
| 123 |
full_code_with_think += code_chunk
|
|
@@ -144,24 +210,19 @@ def generate_code(code_type, model_choice, user_prompt, session_id: gr.State):
|
|
| 144 |
buffer = ""
|
| 145 |
break
|
| 146 |
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
"""
|
| 161 |
-
yield full_code_with_think, gr.HTML(preview_html)
|
| 162 |
-
last_update_time = current_time
|
| 163 |
-
else:
|
| 164 |
-
yield full_code_with_think, gr.update()
|
| 165 |
|
| 166 |
# Final update for the preview without the spinner
|
| 167 |
escaped_code = full_code_for_preview.replace("'", "'").replace('"', '"')
|
|
@@ -172,66 +233,105 @@ def generate_code(code_type, model_choice, user_prompt, session_id: gr.State):
|
|
| 172 |
</iframe>
|
| 173 |
</div>
|
| 174 |
"""
|
| 175 |
-
|
|
|
|
| 176 |
logger.info("Static page streaming finished.")
|
| 177 |
|
|
|
|
| 178 |
def toggle_fullscreen(is_fullscreen):
|
| 179 |
-
"""
|
| 180 |
is_fullscreen = not is_fullscreen
|
| 181 |
-
new_button_text = "
|
| 182 |
panel_visibility = not is_fullscreen
|
| 183 |
return is_fullscreen, gr.update(value=new_button_text), gr.update(visible=panel_visibility)
|
| 184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
def create_code_tab():
|
| 186 |
-
"""
|
| 187 |
-
session_id = str(uuid.uuid4())
|
| 188 |
-
session_state = gr.State(session_id)
|
| 189 |
fullscreen_state = gr.State(False)
|
| 190 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
with gr.Blocks() as demo:
|
| 192 |
with gr.Row():
|
| 193 |
with gr.Column(scale=1) as left_panel:
|
| 194 |
-
gr.Markdown("### 1.
|
| 195 |
-
code_type_radio = gr.Radio(["
|
| 196 |
|
| 197 |
-
gr.Markdown("### 2.
|
| 198 |
model_choice_radio = gr.Radio(
|
| 199 |
-
["
|
| 200 |
-
value="
|
| 201 |
label="Model Selection"
|
| 202 |
)
|
| 203 |
|
| 204 |
-
gr.Markdown("### 3.
|
| 205 |
-
prompt_input = gr.Textbox(lines=5, placeholder="
|
| 206 |
-
gr.
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
"
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
|
|
|
|
|
|
|
|
|
| 219 |
|
| 220 |
with gr.Column(scale=2):
|
| 221 |
-
with gr.Tabs():
|
| 222 |
-
with gr.TabItem("
|
| 223 |
with gr.Row():
|
| 224 |
-
gr.Markdown("### 3.
|
| 225 |
-
fullscreen_button = gr.Button("
|
| 226 |
preview_output = gr.HTML(value="<p>Preview will appear here.</p>")
|
| 227 |
-
with gr.TabItem("
|
| 228 |
-
gr.Markdown("### 4.
|
| 229 |
code_output = gr.Code(language="html", label="Generated Code")
|
| 230 |
|
|
|
|
|
|
|
| 231 |
generate_button.click(
|
| 232 |
fn=generate_code,
|
| 233 |
-
inputs=[code_type_radio, model_choice_radio, prompt_input
|
| 234 |
-
outputs=[code_output, preview_output]
|
| 235 |
)
|
| 236 |
|
| 237 |
fullscreen_button.click(
|
|
@@ -240,6 +340,4 @@ def create_code_tab():
|
|
| 240 |
outputs=[fullscreen_state, fullscreen_button, left_panel]
|
| 241 |
)
|
| 242 |
|
| 243 |
-
demo.unload(fn=lambda: stop_process(session_id))
|
| 244 |
-
|
| 245 |
return demo
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import re
|
|
|
|
|
|
|
|
|
|
| 3 |
import os
|
|
|
|
| 4 |
import sys
|
| 5 |
+
import tempfile
|
| 6 |
import time
|
| 7 |
+
import uuid
|
| 8 |
+
import logging
|
| 9 |
+
import subprocess
|
| 10 |
from models import generate_code_for_tab
|
| 11 |
+
from utils import find_free_port
|
| 12 |
|
| 13 |
+
# Global variable to track the Gradio subprocess
|
| 14 |
+
gradio_process = None
|
| 15 |
|
| 16 |
+
# Configure logging
|
| 17 |
+
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
def get_gradio_sys_prompt():
|
| 20 |
+
"""Get the System Prompt for generating Gradio applications"""
|
| 21 |
return """
|
| 22 |
You are an expert Gradio developer. Create a complete, runnable, single-file Gradio application based on the user's request.
|
| 23 |
The code must be self-contained in a single Python script.
|
| 24 |
+
IMPORTANT: The main Gradio app instance MUST be assigned to a variable named `demo`.
|
| 25 |
+
The script must end with the app launch command: `demo.launch()`.
|
| 26 |
+
Do not include any explanations, just the raw Python code inside a ```python block.
|
| 27 |
"""
|
| 28 |
|
| 29 |
+
def run_gradio_app(code, port):
|
| 30 |
+
"""
|
| 31 |
+
Write the Gradio code to a temporary file and run it in a subprocess on the specified port.
|
| 32 |
+
"""
|
| 33 |
+
temp_dir = tempfile.mkdtemp()
|
| 34 |
+
file_path = os.path.join(temp_dir, "app_to_run.py")
|
| 35 |
+
with open(file_path, "w") as f:
|
| 36 |
+
f.write(code)
|
| 37 |
+
|
| 38 |
+
env = os.environ.copy()
|
| 39 |
+
env["GRADIO_SERVER_PORT"] = str(port)
|
| 40 |
+
|
| 41 |
+
# Use subprocess.Popen to run the script in a completely isolated process
|
| 42 |
+
process = subprocess.Popen([sys.executable, file_path], env=env)
|
| 43 |
+
|
| 44 |
+
# Return the process object for management
|
| 45 |
+
return process, temp_dir, file_path
|
| 46 |
+
|
| 47 |
+
|
| 48 |
def get_html_sys_prompt():
|
| 49 |
+
"""Get the System Prompt for generating static pages"""
|
| 50 |
return """
|
| 51 |
You are an expert front-end developer. Create a complete, modern, and responsive single HTML file based on the user's request.
|
| 52 |
The file must be self-contained, including all necessary HTML, CSS, and JavaScript.
|
|
|
|
| 54 |
"""
|
| 55 |
|
| 56 |
def run_gradio_in_thread(code, url_queue, session_id):
|
| 57 |
+
"""Run the Gradio application in a separate thread to avoid blocking the main application"""
|
| 58 |
temp_dir = tempfile.mkdtemp()
|
| 59 |
file_path = os.path.join(temp_dir, "app.py")
|
| 60 |
with open(file_path, "w") as f:
|
|
|
|
| 86 |
print(f"Error cleaning up temp files: {e}")
|
| 87 |
|
| 88 |
def get_spinner_html():
|
| 89 |
+
"""Return HTML with a CSS spinner animation"""
|
| 90 |
return """
|
| 91 |
<div style="width: 100%; height: 600px; display: flex; justify-content: center; align-items: center; border: 1px solid #ddd; background-color: #f9f9f9;">
|
| 92 |
<div class="spinner"></div>
|
|
|
|
| 107 |
</style>
|
| 108 |
"""
|
| 109 |
|
| 110 |
+
def generate_code(code_type, model_choice, user_prompt):
|
| 111 |
+
"""Generate code and provide a preview based on the user's selected type (static page or Gradio application)."""
|
| 112 |
+
global gradio_process
|
| 113 |
logger.info(f"--- [Code Generation] Start ---")
|
| 114 |
logger.info(f"Code Type: {code_type}, Model: {model_choice}, Prompt: '{user_prompt}'")
|
| 115 |
+
|
| 116 |
+
# If a Gradio subprocess is running, terminate it first
|
| 117 |
+
if gradio_process and gradio_process.is_alive():
|
| 118 |
+
gradio_process.terminate()
|
| 119 |
+
gradio_process.join(timeout=5)
|
| 120 |
+
logger.info("Terminated existing Gradio process.")
|
| 121 |
|
| 122 |
if not user_prompt:
|
| 123 |
+
yield "Please enter a prompt.", gr.HTML("Preview will appear here."), "Status: Error", gr.update()
|
| 124 |
return
|
| 125 |
|
| 126 |
+
yield "", gr.HTML(get_spinner_html()), "Status: Initializing...", gr.update()
|
| 127 |
+
|
| 128 |
+
start_time = time.time()
|
| 129 |
+
|
| 130 |
+
if code_type == "Gradio App":
|
| 131 |
+
port = find_free_port()
|
| 132 |
+
system_prompt = get_gradio_sys_prompt()
|
| 133 |
+
|
| 134 |
+
full_code = ""
|
| 135 |
+
for chunk in generate_code_for_tab(system_prompt, user_prompt, code_type, model_choice):
|
| 136 |
+
full_code += chunk
|
| 137 |
+
elapsed_time = time.time() - start_time
|
| 138 |
+
generated_length = len(full_code)
|
| 139 |
+
speed = generated_length / elapsed_time if elapsed_time > 0 else 0
|
| 140 |
+
status = f"""
|
| 141 |
+
**Status:** Generating...
|
| 142 |
+
- **Generation Time:** {elapsed_time:.2f}s
|
| 143 |
+
- **Generated Length:** {generated_length} chars
|
| 144 |
+
- **Average Speed:** {speed:.2f} char/s
|
| 145 |
+
"""
|
| 146 |
+
# Display the generated raw code in real-time
|
| 147 |
+
yield full_code, gr.update(), status, gr.update()
|
| 148 |
+
|
| 149 |
+
# Extract the Python code block from the model output
|
| 150 |
+
python_code_match = re.search(r"```python\n(.*?)```", full_code, re.DOTALL)
|
| 151 |
+
if python_code_match:
|
| 152 |
+
python_code = python_code_match.group(1).strip()
|
| 153 |
+
else:
|
| 154 |
+
# If no code block is found, assume the entire output is code
|
| 155 |
+
logger.warning("Could not find Python code block, assuming the entire output is code.")
|
| 156 |
+
python_code = full_code.strip()
|
| 157 |
+
|
| 158 |
+
try:
|
| 159 |
+
# Verify that the code can at least be compiled
|
| 160 |
+
compile(python_code, '<string>', 'exec')
|
| 161 |
+
|
| 162 |
+
# Use subprocess to launch the Gradio application
|
| 163 |
+
process, temp_dir, temp_file_path = run_gradio_app(python_code, port)
|
| 164 |
+
gradio_process = process
|
| 165 |
+
|
| 166 |
+
# Give the application some time to start
|
| 167 |
+
time.sleep(10)
|
| 168 |
+
|
| 169 |
+
preview_html = f'<iframe src="http://127.0.0.1:{port}" width="100%" height="600px" frameborder="0"></iframe>'
|
| 170 |
+
status = "**Status:** Generation Complete"
|
| 171 |
+
yield python_code, gr.HTML(preview_html), status, gr.Tabs(selected=0)
|
| 172 |
+
logger.info(f"Gradio app started on port {port}.")
|
| 173 |
+
except SyntaxError as e:
|
| 174 |
+
error_message = f"Generated code is not valid Python: {e}"
|
| 175 |
+
logger.error(f"Raw output from the model:\n{full_code}")
|
| 176 |
+
status = f"**Status:** Error - {error_message}"
|
| 177 |
+
yield full_code, gr.HTML(f"<p style='color: red;'>{error_message}</p>"), status, gr.update()
|
| 178 |
+
logger.error(error_message)
|
| 179 |
+
|
| 180 |
+
elif code_type == "Static Page":
|
| 181 |
system_prompt = get_html_sys_prompt()
|
| 182 |
full_code_with_think = ""
|
| 183 |
full_code_for_preview = ""
|
| 184 |
buffer = ""
|
| 185 |
is_thinking = False
|
|
|
|
| 186 |
|
|
|
|
|
|
|
| 187 |
# The model's raw output is streamed here
|
| 188 |
for code_chunk in generate_code_for_tab(system_prompt, user_prompt, code_type, model_choice):
|
| 189 |
full_code_with_think += code_chunk
|
|
|
|
| 210 |
buffer = ""
|
| 211 |
break
|
| 212 |
|
| 213 |
+
elapsed_time = time.time() - start_time
|
| 214 |
+
generated_length = len(full_code_with_think)
|
| 215 |
+
speed = generated_length / elapsed_time if elapsed_time > 0 else 0
|
| 216 |
+
status = f"""
|
| 217 |
+
**Status:** Generating...
|
| 218 |
+
- **Generation Time:** {elapsed_time:.2f}s
|
| 219 |
+
- **Generated Length:** {generated_length} chars
|
| 220 |
+
- **Average Speed:** {speed:.2f} char/s
|
| 221 |
+
"""
|
| 222 |
+
|
| 223 |
+
# In the generation process, we only update the code and status, not the preview.
|
| 224 |
+
# The preview will be updated once at the very end.
|
| 225 |
+
yield full_code_with_think, gr.update(), status, gr.update()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
# Final update for the preview without the spinner
|
| 228 |
escaped_code = full_code_for_preview.replace("'", "'").replace('"', '"')
|
|
|
|
| 233 |
</iframe>
|
| 234 |
</div>
|
| 235 |
"""
|
| 236 |
+
status = "**Status:** Generation Complete"
|
| 237 |
+
yield full_code_with_think, gr.HTML(final_preview_html), status, gr.Tabs(selected=0)
|
| 238 |
logger.info("Static page streaming finished.")
|
| 239 |
|
| 240 |
+
|
| 241 |
def toggle_fullscreen(is_fullscreen):
|
| 242 |
+
"""Toggle fullscreen mode visibility"""
|
| 243 |
is_fullscreen = not is_fullscreen
|
| 244 |
+
new_button_text = "Exit Fullscreen" if is_fullscreen else "Fullscreen Preview"
|
| 245 |
panel_visibility = not is_fullscreen
|
| 246 |
return is_fullscreen, gr.update(value=new_button_text), gr.update(visible=panel_visibility)
|
| 247 |
|
| 248 |
+
def cleanup_process():
|
| 249 |
+
"""Terminate the subprocess when the application exits"""
|
| 250 |
+
global gradio_process
|
| 251 |
+
if gradio_process and gradio_process.poll() is None:
|
| 252 |
+
gradio_process.terminate()
|
| 253 |
+
gradio_process.wait()
|
| 254 |
+
logger.info("Cleaned up Gradio process on exit.")
|
| 255 |
+
|
| 256 |
+
import atexit
|
| 257 |
+
atexit.register(cleanup_process)
|
| 258 |
+
|
| 259 |
def create_code_tab():
|
| 260 |
+
"""Create the UI Tab for the code generation feature"""
|
|
|
|
|
|
|
| 261 |
fullscreen_state = gr.State(False)
|
| 262 |
|
| 263 |
+
html_examples = [
|
| 264 |
+
"Create a Canvas animation of continuous colorful fireworks blooming on a black background.",
|
| 265 |
+
"Generate a Canvas special effect with iridescent light streams.",
|
| 266 |
+
"Design a particle system Canvas animation that interacts with the mouse.",
|
| 267 |
+
"Implement a classic snake game using HTML Canvas.",
|
| 268 |
+
"Create a Canvas animation simulating the 'Elephant\'s Toothpaste' chemical experiment: in a container, colored foam continuously and rapidly gushes out, expands, and overflows, filling the entire screen. Use a suitable drawing js library, such as three.js.",
|
| 269 |
+
"Create a dreamy low-poly floating island scene with dynamic lighting and soft animations in a single HTML file. Use a suitable drawing js library, such as three.js.",
|
| 270 |
+
]
|
| 271 |
+
|
| 272 |
+
gradio_examples = [
|
| 273 |
+
"Create a simple calculator that supports addition, subtraction, multiplication, and division",
|
| 274 |
+
"Create a simple Gradio application that displays 'Hello, Gradio!'",
|
| 275 |
+
"Create an image classification application that can classify uploaded images",
|
| 276 |
+
"Create a text summarization application that can summarize long input texts",
|
| 277 |
+
"Create a simple chatbot",
|
| 278 |
+
]
|
| 279 |
+
|
| 280 |
+
def update_examples(code_type):
|
| 281 |
+
if code_type == "Static Page":
|
| 282 |
+
return gr.update(visible=True), gr.update(visible=False)
|
| 283 |
+
else:
|
| 284 |
+
return gr.update(visible=False), gr.update(visible=True)
|
| 285 |
+
|
| 286 |
with gr.Blocks() as demo:
|
| 287 |
with gr.Row():
|
| 288 |
with gr.Column(scale=1) as left_panel:
|
| 289 |
+
gr.Markdown("### 1. Select Code Type")
|
| 290 |
+
code_type_radio = gr.Radio(["Static Page", "Gradio App"], value="Static Page", label="Code Type")
|
| 291 |
|
| 292 |
+
gr.Markdown("### 2. Select Model")
|
| 293 |
model_choice_radio = gr.Radio(
|
| 294 |
+
["Better (using Ling-1T)", "Faster (using Ring-flash-2.0)"],
|
| 295 |
+
value="Better (using Ling-1T)",
|
| 296 |
label="Model Selection"
|
| 297 |
)
|
| 298 |
|
| 299 |
+
gr.Markdown("### 3. Enter Your Requirements")
|
| 300 |
+
prompt_input = gr.Textbox(lines=5, placeholder="e.g., Create a simple page with a title and a button", label="Prompt")
|
| 301 |
+
with gr.Column() as html_examples_column:
|
| 302 |
+
html_examples_component = gr.Examples(
|
| 303 |
+
examples=html_examples,
|
| 304 |
+
inputs=prompt_input,
|
| 305 |
+
label="✨ Why not try these cool examples",
|
| 306 |
+
examples_per_page=12,
|
| 307 |
+
)
|
| 308 |
+
with gr.Column(visible=False) as gradio_examples_column:
|
| 309 |
+
gradio_examples_component = gr.Examples(
|
| 310 |
+
examples=gradio_examples,
|
| 311 |
+
inputs=prompt_input,
|
| 312 |
+
label="✨ Why not try these cool examples",
|
| 313 |
+
examples_per_page=12,
|
| 314 |
+
)
|
| 315 |
+
generate_button = gr.Button("Generate Code", variant="primary")
|
| 316 |
+
status_output = gr.Markdown(value="Status: Waiting", visible=True)
|
| 317 |
|
| 318 |
with gr.Column(scale=2):
|
| 319 |
+
with gr.Tabs(elem_id="result_tabs") as result_tabs:
|
| 320 |
+
with gr.TabItem("Live Preview", id=0):
|
| 321 |
with gr.Row():
|
| 322 |
+
gr.Markdown("### 3. Live Preview")
|
| 323 |
+
fullscreen_button = gr.Button("Fullscreen Preview", scale=0)
|
| 324 |
preview_output = gr.HTML(value="<p>Preview will appear here.</p>")
|
| 325 |
+
with gr.TabItem("Generated Source Code", id=1):
|
| 326 |
+
gr.Markdown("### 4. Generated Source Code")
|
| 327 |
code_output = gr.Code(language="html", label="Generated Code")
|
| 328 |
|
| 329 |
+
code_type_radio.change(fn=update_examples, inputs=code_type_radio, outputs=[html_examples_column, gradio_examples_column])
|
| 330 |
+
|
| 331 |
generate_button.click(
|
| 332 |
fn=generate_code,
|
| 333 |
+
inputs=[code_type_radio, model_choice_radio, prompt_input],
|
| 334 |
+
outputs=[code_output, preview_output, status_output, result_tabs]
|
| 335 |
)
|
| 336 |
|
| 337 |
fullscreen_button.click(
|
|
|
|
| 340 |
outputs=[fullscreen_state, fullscreen_button, left_panel]
|
| 341 |
)
|
| 342 |
|
|
|
|
|
|
|
| 343 |
return demo
|
tab_search.py
CHANGED
|
@@ -2,12 +2,12 @@ import gradio as gr
|
|
| 2 |
from config import SEARCH_SYSTEM_PROMPT
|
| 3 |
|
| 4 |
def handle_web_search(query):
|
| 5 |
-
"""
|
| 6 |
-
#
|
| 7 |
-
#
|
| 8 |
-
summary = f"
|
| 9 |
|
| 10 |
-
sources = """###
|
| 11 |
* [Source 1: Example Domain](https://example.com)
|
| 12 |
* [Source 2: Another Example](https://example.com)
|
| 13 |
* [Source 3: Wikipedia](https://wikipedia.org)"""
|
|
@@ -17,17 +17,16 @@ def handle_web_search(query):
|
|
| 17 |
return gr.update(value=full_response, visible=True)
|
| 18 |
|
| 19 |
def create_search_tab():
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
search_results_output = gr.Markdown(label="结果展示区", visible=False)
|
| 31 |
|
| 32 |
return {
|
| 33 |
"search_input": search_input,
|
|
|
|
| 2 |
from config import SEARCH_SYSTEM_PROMPT
|
| 3 |
|
| 4 |
def handle_web_search(query):
|
| 5 |
+
"""Handle the logic for the 'Web Search' tab"""
|
| 6 |
+
# Simulate Ring model for web search and summarization
|
| 7 |
+
# In a real application, SEARCH_SYSTEM_PROMPT would be used here
|
| 8 |
+
summary = f"Based on a web search, here is a summary about '{query}':\n\nThis is a summary answer simulated by the Ring model. In a real application, the model would access the internet, retrieve relevant information, and generate a high-quality summary.\n\n### Key Points:\n- **Point 1**: This is the first key piece of information.\n- **Point 2**: This is the second key piece of information.\n- **Point 3**: This is the third key piece of information."
|
| 9 |
|
| 10 |
+
sources = """### Sources:
|
| 11 |
* [Source 1: Example Domain](https://example.com)
|
| 12 |
* [Source 2: Another Example](https://example.com)
|
| 13 |
* [Source 3: Wikipedia](https://wikipedia.org)"""
|
|
|
|
| 17 |
return gr.update(value=full_response, visible=True)
|
| 18 |
|
| 19 |
def create_search_tab():
|
| 20 |
+
gr.Markdown("<p align='center'>work in progress XD</p>")
|
| 21 |
+
with gr.Column():
|
| 22 |
+
search_input = gr.Textbox(label="Search Input", placeholder="Enter a question to search and summarize...")
|
| 23 |
+
gr.Examples(
|
| 24 |
+
examples=["What are the latest advancements in AI?", "Explain the Transformer architecture", "Summarize today's news headlines"],
|
| 25 |
+
label="Example Prompts",
|
| 26 |
+
inputs=[search_input]
|
| 27 |
+
)
|
| 28 |
+
search_button = gr.Button("✨ Search")
|
| 29 |
+
search_results_output = gr.Markdown(label="Results Display", visible=False)
|
|
|
|
| 30 |
|
| 31 |
return {
|
| 32 |
"search_input": search_input,
|
tab_workflow.py
CHANGED
|
@@ -1,74 +1,133 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
def handle_workflow_generation(description):
|
| 6 |
-
"""处理“工作流执行”标签页的生成逻辑"""
|
| 7 |
-
# 在真实应用中,这里会使用 WORKFLOW_GENERATE_SYSTEM_PROMPT
|
| 8 |
-
# We use a mock SVG diagram from utils
|
| 9 |
-
svg_diagram = WORKFLOW_SVG_DIAGRAM
|
| 10 |
-
|
| 11 |
-
steps = ["Step 1: Plan", "Step 2: Execute", "Step 3: Review"]
|
| 12 |
-
initial_state = {"current_step": 0, "steps": steps}
|
| 13 |
-
initial_status = f"**当前节点**: {steps[0]}"
|
| 14 |
-
initial_chatbot_message = [(None, f"工作流已生成。让我们开始第一步:‘{steps[0]}’。请提供规划所需的信息。 ")]
|
| 15 |
|
| 16 |
-
|
|
|
|
|
|
|
| 17 |
|
| 18 |
-
def
|
| 19 |
-
"""
|
| 20 |
-
if not
|
| 21 |
-
return
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
steps = state["steps"]
|
| 27 |
-
|
| 28 |
-
thinking_message = "..."
|
| 29 |
-
chat_history[-1] = (user_input, thinking_message)
|
| 30 |
-
yield chat_history, state, "", gr.update(interactive=False)
|
| 31 |
-
|
| 32 |
-
current_step_index += 1
|
| 33 |
-
state["current_step"] = current_step_index
|
| 34 |
-
|
| 35 |
-
if current_step_index < len(steps):
|
| 36 |
-
next_step_name = steps[current_step_index]
|
| 37 |
-
response = f"好的,已完成上一步。现在我们进行 ‘{next_step_name}’。请提供相关信息。"
|
| 38 |
-
new_status = f"**当前节点**: {next_step_name}"
|
| 39 |
-
interactive = True
|
| 40 |
-
else:
|
| 41 |
-
response = "所有步骤均已完成!工作流结束。"
|
| 42 |
-
new_status = "**状态**: 已完成"
|
| 43 |
-
interactive = False
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
def create_workflow_tab():
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
| 52 |
with gr.Row():
|
| 53 |
with gr.Column(scale=1):
|
| 54 |
-
|
| 55 |
-
gr.
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
| 59 |
)
|
| 60 |
-
|
| 61 |
-
workflow_visualization_output = gr.HTML(label="工作流图示")
|
| 62 |
with gr.Column(scale=1):
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
|
|
|
| 66 |
|
| 67 |
return {
|
| 68 |
-
"
|
| 69 |
-
"
|
| 70 |
-
"
|
| 71 |
-
"
|
| 72 |
-
"
|
| 73 |
-
"
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import re
|
| 3 |
+
import uuid
|
| 4 |
+
from models import get_model_response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
+
# Optimized System Prompts
|
| 7 |
+
MAIN_AGENT_PROMPT = "You are a calm and methodical assistant. Your sole purpose is to help the user solve their problem step-by-step. Maintain a neutral and professional tone. Break down the task by asking questions. Do not deviate from the user's stated goal."
|
| 8 |
+
SUB_AGENT_PROMPT = """You are a silent observer Agent.\n\n**Rules:**\n1. Your output must be a **single** Markdown list item.\n2. The step must describe a high-level interaction **process** (e.g., "- Assistant: Clarify the user's goal.").\n3. The step **must not** contain specific details from the conversation (e.g., place names like 'Hangzhou', numbers like '3000 yuan').\n4. The step must be strictly based on the **assistant's last sentence**, do not assume or create actions.\n\n**Task:**\nAnalyze only the latest turn of the conversation between the 'Assistant' and the 'User' and, strictly following the rules above, output a single pseudo-code step describing the assistant's action.\n\n**Example:**\nUser: 'I want to go to Beijing by car'\nAssistant: 'Okay, so the destination is Beijing, and the mode of transport is by car, is that correct?'\nYour output should be:\n- Assistant: Confirm the destination and mode of transport with the user."""
|
| 9 |
|
| 10 |
+
def _convert_text_to_mermaid(workflow_text):
|
| 11 |
+
"""Converts a markdown list of steps into Mermaid flowchart syntax."""
|
| 12 |
+
if not workflow_text or workflow_text == "*Waiting for task to start...*":
|
| 13 |
+
return "graph TD;\n A[\"Waiting for task to start...\"];"
|
| 14 |
|
| 15 |
+
lines = workflow_text.strip().split('\n')
|
| 16 |
+
mermaid_lines = ["graph TD;"]
|
| 17 |
+
node_ids = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
+
for i, line in enumerate(lines):
|
| 20 |
+
node_id = chr(65 + i) # A, B, C, ...
|
| 21 |
+
# Clean up the text: remove markdown list markers and escape quotes
|
| 22 |
+
clean_text = re.sub(r'^\s*[-*]\s*', '', line).strip()
|
| 23 |
+
clean_text = clean_text.replace('"', '#quot;')
|
| 24 |
+
mermaid_lines.append(f' {node_id}["{clean_text}"];')
|
| 25 |
+
node_ids.append(node_id)
|
| 26 |
+
|
| 27 |
+
if len(node_ids) > 1:
|
| 28 |
+
mermaid_lines.append(" " + " --> ".join(node_ids) + ";")
|
| 29 |
+
|
| 30 |
+
return '\n'.join(mermaid_lines)
|
| 31 |
+
|
| 32 |
+
def _render_mermaid_html(mermaid_code):
|
| 33 |
+
"""Wraps Mermaid code in a simple <pre> tag for rendering."""
|
| 34 |
+
return f'<pre class="mermaid">{mermaid_code}</pre>'
|
| 35 |
|
| 36 |
def create_workflow_tab():
|
| 37 |
+
"""
|
| 38 |
+
Creates the UI components for the Workflow tab and returns them as a dictionary.
|
| 39 |
+
"""
|
| 40 |
+
with gr.Blocks() as tab:
|
| 41 |
with gr.Row():
|
| 42 |
with gr.Column(scale=1):
|
| 43 |
+
gr.Markdown("### Chat Interface (Main Agent)")
|
| 44 |
+
chatbot = gr.Chatbot(label="Chat with Ling-1t", height=600, elem_id="workflow_chatbot")
|
| 45 |
+
textbox = gr.Textbox(label="Enter your task...", lines=3, elem_id="workflow_chat_input")
|
| 46 |
+
button = gr.Button("Send", elem_id="workflow_send_button")
|
| 47 |
+
examples = gr.Examples(
|
| 48 |
+
examples=["Write a Python Flask application that displays 'Hello, World!' on a webpage", "Help me plan a three-day travel itinerary for Beijing"],
|
| 49 |
+
inputs=textbox,
|
| 50 |
+
label="Example Topics"
|
| 51 |
)
|
| 52 |
+
|
|
|
|
| 53 |
with gr.Column(scale=1):
|
| 54 |
+
gr.Markdown("### Real-time Workflow (Sub-agent)")
|
| 55 |
+
topic_output = gr.Textbox(label="Current Topic", interactive=False, elem_id="workflow_topic_output")
|
| 56 |
+
workflow_output = gr.Markdown(label="Current Workflow Description", value="*Waiting for task to start...*", elem_id="workflow_output")
|
| 57 |
+
mermaid_output = gr.HTML(label="Flowchart Visualization")
|
| 58 |
|
| 59 |
return {
|
| 60 |
+
"chatbot": chatbot,
|
| 61 |
+
"chat_input": textbox,
|
| 62 |
+
"send_button": button,
|
| 63 |
+
"examples": examples,
|
| 64 |
+
"topic_output": topic_output,
|
| 65 |
+
"workflow_output": workflow_output,
|
| 66 |
+
"mermaid_output": mermaid_output
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
def handle_workflow_chat(user_input, history, current_topic, current_workflow):
|
| 70 |
+
"""
|
| 71 |
+
Handles the chat interaction, generates text workflow, and converts it to a Mermaid chart.
|
| 72 |
+
"""
|
| 73 |
+
print(f"[handle_workflow_chat] User Input: {user_input}")
|
| 74 |
+
print(f"[handle_workflow_chat] History before append: {history}")
|
| 75 |
+
history.append((user_input, None))
|
| 76 |
+
print(f"[handle_workflow_chat] History after append: {history}")
|
| 77 |
+
|
| 78 |
+
# 1. Main Agent Call
|
| 79 |
+
main_agent_model_id = "inclusionai/ling-1t"
|
| 80 |
+
main_agent_temp = 0.7
|
| 81 |
+
assistant_response = ""
|
| 82 |
+
response_generator = get_model_response(
|
| 83 |
+
model_id=main_agent_model_id, history=history, system_prompt=MAIN_AGENT_PROMPT, temperature=main_agent_temp
|
| 84 |
+
)
|
| 85 |
+
for chunk in response_generator:
|
| 86 |
+
assistant_response += chunk
|
| 87 |
+
history[-1] = (user_input, assistant_response)
|
| 88 |
+
yield history, current_topic, current_workflow, _render_mermaid_html(_convert_text_to_mermaid(current_workflow)), ""
|
| 89 |
+
|
| 90 |
+
print(f"[handle_workflow_chat] Assistant Response: {assistant_response}")
|
| 91 |
+
|
| 92 |
+
# 2. Sub-Agent Call to get ONLY the new step
|
| 93 |
+
sub_agent_model_id = "inclusionai/ling-mini-2.0"
|
| 94 |
+
sub_agent_temp = 0.3
|
| 95 |
+
|
| 96 |
+
sub_agent_user_prompt = f"""
|
| 97 |
+
Analyze ONLY the following conversation turn and extract the single, abstract process step.
|
| 98 |
+
User: "{user_input}"
|
| 99 |
+
Assistant: "{assistant_response}"
|
| 100 |
+
"""
|
| 101 |
+
|
| 102 |
+
sub_agent_history_for_call = [(sub_agent_user_prompt, None)]
|
| 103 |
+
|
| 104 |
+
new_step_generator = get_model_response(
|
| 105 |
+
model_id=sub_agent_model_id, history=sub_agent_history_for_call, system_prompt=SUB_AGENT_PROMPT, temperature=sub_agent_temp
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
new_step = ""
|
| 109 |
+
for chunk in new_step_generator:
|
| 110 |
+
new_step += chunk
|
| 111 |
+
|
| 112 |
+
new_step = new_step.strip()
|
| 113 |
+
print(f"[handle_workflow_chat] New Step: {new_step}")
|
| 114 |
+
|
| 115 |
+
# 3. Append the new step to the workflow
|
| 116 |
+
if new_step:
|
| 117 |
+
if current_workflow == "*Waiting for task to start...*":
|
| 118 |
+
new_workflow = new_step
|
| 119 |
+
else:
|
| 120 |
+
new_workflow = current_workflow + "\n" + new_step
|
| 121 |
+
else:
|
| 122 |
+
new_workflow = current_workflow
|
| 123 |
+
|
| 124 |
+
# 4. Topic Logic
|
| 125 |
+
new_topic = current_topic
|
| 126 |
+
if not current_topic and user_input:
|
| 127 |
+
new_topic = f"Task Topic: {user_input[:30]}..."
|
| 128 |
+
|
| 129 |
+
# 5. Generate and render Mermaid chart
|
| 130 |
+
mermaid_code = _convert_text_to_mermaid(new_workflow)
|
| 131 |
+
mermaid_html = _render_mermaid_html(mermaid_code)
|
| 132 |
+
|
| 133 |
+
yield history, new_topic, new_workflow, mermaid_html, ""
|
utils.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
|
|
|
|
|
| 1 |
# Mock data and helper functions
|
| 2 |
|
| 3 |
WORKFLOW_SVG_DIAGRAM = """<svg width="100%" height="150" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -17,3 +19,12 @@ WORKFLOW_SVG_DIAGRAM = """<svg width="100%" height="150" xmlns="http://www.w3.or
|
|
| 17 |
<text x="320" y="80" text-anchor="middle" fill="#4b5563" font-size="12">Step 3: Review</text>
|
| 18 |
</g>
|
| 19 |
</svg>"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import socket
|
| 2 |
+
|
| 3 |
# Mock data and helper functions
|
| 4 |
|
| 5 |
WORKFLOW_SVG_DIAGRAM = """<svg width="100%" height="150" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
| 19 |
<text x="320" y="80" text-anchor="middle" fill="#4b5563" font-size="12">Step 3: Review</text>
|
| 20 |
</g>
|
| 21 |
</svg>"""
|
| 22 |
+
|
| 23 |
+
def find_free_port():
|
| 24 |
+
"""
|
| 25 |
+
Finds a free port on the local machine.
|
| 26 |
+
"""
|
| 27 |
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
| 28 |
+
s.bind(("", 0))
|
| 29 |
+
return s.getsockname()[1]
|
| 30 |
+
|