cafe3310 commited on
Commit
cb6eafc
·
1 Parent(s): a9fb7e9

little demo

Browse files
Files changed (45) hide show
  1. .gitignore +1 -0
  2. GEMINI.md +107 -92
  3. README.md +1 -2
  4. __pycache__/config.cpython-313.pyc +0 -0
  5. __pycache__/local.cpython-313.pyc +0 -0
  6. __pycache__/models.cpython-313.pyc +0 -0
  7. __pycache__/tab_chat.cpython-313.pyc +0 -0
  8. __pycache__/tab_code.cpython-313.pyc +0 -0
  9. __pycache__/tab_search.cpython-313.pyc +0 -0
  10. __pycache__/tab_workflow.cpython-313.pyc +0 -0
  11. __pycache__/utils.cpython-313.pyc +0 -0
  12. app.py +54 -36
  13. config.py +83 -84
  14. docs/backlog/2025-10-11-18-43-auto-fix-for-code-generator.md +1 -0
  15. docs/backlog/2025-10-11-19-41-add-local-model-id-mapping.md +1 -0
  16. docs/backlog/2025-10-11-20-32-chat-interrupt-output.md +46 -0
  17. docs/requirements/2025-10-11-14-35-fix-chat-model-display-name.md +1 -1
  18. docs/requirements/2025-10-11-14-37-update-model-descriptions.md +1 -1
  19. docs/requirements/2025-10-11-14-39-update-chat-example-prompts.md +1 -1
  20. docs/requirements/2025-10-11-15-08-refactor-chat-examples-to-scenarios.md +1 -1
  21. docs/requirements/2025-10-11-15-47-add-model-identity-to-chat-output.md +1 -1
  22. docs/requirements/2025-10-11-16-47-implement-static-page-generation.md +1 -1
  23. docs/requirements/2025-10-11-16-56-add-code-generation-presets.md +1 -1
  24. docs/requirements/2025-10-11-16-59-add-fullscreen-preview.md +1 -1
  25. docs/requirements/2025-10-11-17-12-refactor-code-preview-to-tabs.md +1 -1
  26. docs/requirements/2025-10-11-17-38-add-floating-island-example.md +1 -1
  27. docs/requirements/2025-10-11-18-18-add-model-selection-switch.md +1 -1
  28. docs/requirements/2025-10-11-18-18-display-think-tags-in-source-only.md +1 -1
  29. docs/requirements/2025-10-11-21-05-add-real-time-generation-status.md +27 -0
  30. docs/requirements/2025-10-11-21-09-implement-dynamic-gradio-app-generation.md +50 -0
  31. docs/requirements/2025-10-12-08-30-implement-workflow-tab.md +80 -0
  32. docs/requirements/2025-10-12-09-05-optimize-workflow-prompts-and-test.md +47 -0
  33. docs/requirements/2025-10-12-09-20-add-mermaid-workflow-visualization.md +30 -0
  34. docs/requirements/2025-10-12-22-38-prevent-premature-code-preview-refresh.md +26 -0
  35. docs/requirements/2025-10-13-10-21-fix-workflow-chat-context.md +33 -0
  36. docs/requirements/2025-10-13-18-32-update-chat-model-list.md +34 -0
  37. docs/requirements/2025-10-13-23-39-update-chat-models.md +30 -0
  38. docs/requirements/2025-10-14-00-40-add-credits.md +13 -0
  39. docs/uncategorized/development_todo.md +0 -31
  40. models.py +28 -36
  41. tab_chat.py +55 -56
  42. tab_code.py +187 -89
  43. tab_search.py +15 -16
  44. tab_workflow.py +120 -61
  45. 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
- ## Gemini 用例
 
 
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
- 1. **需求接收 (Requirement Reception):**
35
- - 当你提出新需求时,我将首先在 `docs/backlog/` 目录下创建一个 Markdown 文件,作为“需求池”的记录。
36
- - 文件名格式为 `YYYY-MM-DD-HH-mm-需求简述.md`。
37
- - 文件内容将包含:需求描述、创建时间、初始状态 `待处理 (Pending)`、验证方式(暂空)、验证结果(暂空)。
38
 
39
- 2. **规划与确认 (Plan & Confirm):**
40
- - 当我们决定处理一个需求时,我会基于其文档,提出具体的执行计划。
41
- - 在你确认计划后,我会将该需求文件从 `docs/backlog/` **移动**到 `docs/requirements/` 目录,并将其状态更新为 `开发中 (In Progress)`。
42
 
43
- 3. **修订章程 (Revise Charter):**
44
- - 我会更新 `GEMINI.md` 的“第三部分:详细设计”,以反映新功能的设计细节。
45
 
46
- 4. **执行 (Execute):**
47
- - 我将根据确认的计划进行编码实现。
 
48
 
49
- 5. **提交验证 (Submit for Verification):**
50
- - 完成代码编写后,我会重启应用,并**自动刷新你的浏览器以展示最新版本**。
51
- - 同时,我将更新需求文件,将状态改为 `已完成 (Completed)`,并填入“验证方式”供你参考。
52
 
53
- 6. **确认与关闭 (Confirm & Close):**
54
- - 在你完成验证并确认功能符合预期后,我会将需求文件的“验证结果”更新为 `已验证 (Verified)`,至此该需求流程关闭。
55
 
56
- ### 调试与 Bug 修复流程
57
 
58
- 当功能未按预期工作时,我们将采用以下高效的诊断流程:
 
 
 
59
 
60
- 1. **问题报告:** 你需要向我清晰地描述问题,包括:
61
- - **复现步骤:** 你进行了哪些具体操作。
62
- - **预期结果:** 你期望看到什么。
63
- - **实际结果:** 你实际看到了什么(例如:报错、无响应、界面错乱等)。
64
 
65
- 2. **启动日志模式:** 我会终止在后台静默运行的应用,并以日志模式重新启动它。这能让我观察到应用在运行期间的所有内部活动和潜在错误。
66
- ```bash
67
- # 命令示例
68
- source .venv/bin/activate && python3 app.py > app.log 2>&1 &
69
- ```
70
 
71
- 3. **复现与分析:** 我会请你重新执行一遍复现步骤。在你操作完成后,我将立即读取 `app.log` 文件,分析其中的错误信息和执行轨迹,定位问题的根本原因。
72
 
73
- 4. **修复与验证:**
74
- - 根据日志分析,我会提出具体的修复方案并执行。
75
- - 修复后,我会重启应用(仍处于日志模式),并**自动刷新你的浏览器**,然后请你再次验证。
76
- - 如果问题依然存在,我们将重复步骤 3 和 4。
77
 
78
- 5. **流程结束:** 在你确认 Bug 已被完全修复后,此调试流程结束。我会终止日志模式的应用,清理日志文件,**以标准的静默模式重新启动应用,并为你刷新浏览器**。
 
 
 
79
 
80
- ### 代码提交与版本管理
 
 
 
 
 
 
81
 
82
- 在完成一个需求或修复一个 Bug 后,我们将遵循以下流程来提交代码和管理版本:
 
 
83
 
84
- 1. **暂存变更:**
85
- - 使用 `git add .` 暂存所有修改。
86
 
87
- 2. **编写提交信息 (Commit Message):**
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
- 3. **创建版本标签 (Git Tag):**
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
- 4. **最终确认:**
104
- - 提交和打标签后,运行 `git status` `git tag` 确认工作区干净且新标签已成功创建。
 
 
 
105
 
106
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
- ## 第二部分:整体设计
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- ### 2.1 项目目标
111
 
112
- 创建一个名为 “Ling & Ring Playground” Hugging Face Space,用于展示两个核心 AI 模型:
113
 
114
- - **Ling (🧠):** 通用对话模型。
115
- - **Ring (💍):** 推理/代理模型,用于代码生成、网页检索和工作流执行。
 
 
 
116
 
117
- ### 2.2 核心设计原则
 
 
118
 
119
  - **任务导向:** UI 围绕用户任务(聊天、编码等)组织,而非直接暴露模型。
120
  - **品牌明晰:** 在每个功能界面清晰标注“由 Ling/Ring 模型驱动”。
121
  - **无缝体验:** 用户无需手动输入 API Token,认证在后端自动完成。
122
  - **引导优先:** 提供精心设计的示例,确保用户获得高质量的初次体验。
123
 
124
- ### 2.3 技术栈与架构
125
 
126
  - **前端/UI:** Gradio `gr.Blocks`
127
  - **后端:** Python
128
  - **安全:** 所有 API 密钥通过 Hugging Face Space Secrets 管理。
129
 
130
- ### 2.3.1 配置加载策略
131
 
132
  为兼顾本地开发的便利性、线上部署的安全性与成本效益,项目采用了一种分层配置加载机制:
133
 
@@ -137,7 +167,7 @@
137
 
138
  此策略确保了开发者在本地可以快速迭代,而线上部署则遵循了安全最佳实践。
139
 
140
- ### 2.4 项目结构
141
 
142
  ```
143
  /
@@ -158,7 +188,7 @@
158
  └───refs/ # 本地参考资料(Git 忽略)
159
  ```
160
 
161
- ### 2.5 代码架构
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 2
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, handle_workflow_generation, handle_workflow_chat
 
 
 
 
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
- workflow_state = gr.State()
 
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) | [Read the Paper](https://huggingface.co) | [Join our Discord](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("聊天 (Chat)"):
29
  chat_components = create_chat_tab()
30
- with gr.Tab("代码生成 (Code Generation)"):
31
  create_code_tab() # The code tab now handles its own events
32
- with gr.Tab("网页检索 (Web Search)"):
33
  search_components = create_search_tab()
34
- with gr.Tab("工作流 (Workflow)"):
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
- workflow_components["generate_workflow_button"].click(
78
- fn=handle_workflow_generation,
79
- inputs=[workflow_components["workflow_description_input"]],
80
- outputs=[
81
- workflow_components["workflow_visualization_output"],
82
- workflow_components["workflow_status_output"],
83
- workflow_components["workflow_chatbot"],
84
- workflow_state,
85
- workflow_components["workflow_chat_input"] # 新增:直接作为输出
86
- ]
87
- )
88
 
89
- workflow_components["workflow_chat_input"].submit(
90
- fn=handle_workflow_chat,
91
- inputs=[
92
- workflow_components["workflow_chat_input"],
93
- workflow_components["workflow_chatbot"],
94
- workflow_state
95
- ],
96
- outputs=[
97
- workflow_components["workflow_chatbot"],
98
- workflow_state,
99
- workflow_components["workflow_status_output"],
100
- workflow_components["workflow_chat_input"]
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
- "inclusionai/ling-1t": {
65
  "model_id": "inclusionai/ling-1t",
66
- "display_name": "🧠 Ling-1T (1T)",
67
- "description": "一款万亿级参数的大语言模型,为追求极致性能和高流畅度的复杂自然语言理解与生成任务而设计。",
68
  "prompt_scenarios": [
69
  {
70
- "title": "深度分析报告撰写",
71
- "system_prompt": "你是一位资深的行业分析师,能够撰写逻辑清晰、数据充分、观点独到的深度分析报告。",
72
  "message_examples": [
73
- "撰写一篇关于人工智能在医疗领域应用的深度分析报告,至少800字。",
74
- "分析当前宏观经济形势,并预测未来一年的发展趋势。",
75
- "为一家新成立的科技公司制定一份详细的品牌推广策略。"
76
  ]
77
  },
78
  {
79
- "title": "莎士比亚风格文案",
80
- "system_prompt": "你是一位模仿大师,能够以威廉·莎士比亚的风格和口吻进行文学创作。",
81
  "message_examples": [
82
- "以莎士比亚的风格,写一段关于“代码”的独白。",
83
- "假如哈姆雷特是一个程序员,他会如何抱怨一个难缠的 bug",
84
- "把“用户体验”这个词用十四行诗的形式表达出来。"
85
  ]
86
  }
87
  ]
88
  },
89
- "inclusionai/ling-flash-2.0": {
90
  "model_id": "inclusionai/ling-flash-2.0",
91
- "display_name": "🧠 Ling-flash-2.0 (103B)",
92
- "description": "一款性能卓越的十亿级参数模型,专为需要高速响应和复杂指令遵循的场景优化。",
93
  "prompt_scenarios": [
94
  {
95
- "title": "技术文档撰写",
96
- "system_prompt": "你是一位专业的技术作家,能够清晰、准确地解释复杂的技术概念。",
97
  "message_examples": [
98
- "为一段新的 API 端点编写清晰的文档。",
99
- "解释一下什么是 'Transformer' 架构。",
100
- "如何为开源项目编写一份贡献指南?"
101
  ]
102
  },
103
  {
104
- "title": "创意头脑风暴",
105
- "system_prompt": "你是一位充满创意的伙伴,可以进行头脑风暴并提供新颖的想法。",
106
  "message_examples": [
107
- "为一个新的播客想 5 个吸引人的名字。",
108
- "我应该为我的博客写些什么内容?",
109
- "想一个关于时间旅行的短篇故事点子。"
110
  ]
111
  }
112
  ]
113
  },
114
- "inclusionai/ring-flash-2.0": {
115
- "model_id": "inclusionai/ring-flash-2.0",
116
- "display_name": "💍 Ring-flash-2.0 (103B)",
117
- "description": "一款十亿级参数的推理模型,在性能和成本之间取得了很好的平衡,适合需要逐步思考或生成代码的通用任务。",
118
  "prompt_scenarios": [
119
  {
120
- "title": "旅行规划专家",
121
- "system_prompt": "你是一位经验丰富的旅行规划师,精通全球各地的旅行路线、交通和预算规划。",
122
  "message_examples": [
123
- "规划一个为期五天的日本东京自由行,包含详细的每日行程、交通和预算。",
124
- "我应该如何选择我的第一把电吉他?请给出步骤和建议。",
125
- "为我的周末家庭聚餐推荐三个菜谱。"
126
  ]
127
  },
128
  {
129
- "title": "Python 脚本生成器",
130
- "system_prompt": "你是一位 Python 编程专家,能够根据需求生成高质量、可执行的 Python 脚本。",
131
  "message_examples": [
132
- "生成一个 Python 脚本,监控网站价格变化并在降价时发邮件提醒。",
133
- "写一个 Python 函数,用于计算两个日期之间相差了多少天。",
134
- " Python 实现一个简单的命令行计算器。"
135
  ]
136
  }
137
  ]
138
  },
139
- "inclusionai/ling-mini-2.0": {
140
- "model_id": "inclusionai/ling-mini-2.0",
141
- "display_name": "🧠 Ling-mini-2.0 (16B)",
142
- "description": "一款轻量级对话模型,经过优化,可在消费级硬件上高效运行,非常适合移动端或本地化部署场景。",
143
  "prompt_scenarios": [
144
  {
145
- "title": "高效邮件助手",
146
- "system_prompt": "你是一位专业的行政助理,擅长撰写清晰、简洁、专业的电子邮件。",
147
  "message_examples": [
148
- "给我写一封简短的邮件,提醒团队成员明天上午10点开会。",
149
- "草拟一封邮件,向客户询问项目进展。",
150
- "帮我写一封得体的拒绝信,回复一个不合适的合作邀请。"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  ]
152
  },
153
  {
154
- "title": "文本摘要与翻译",
155
- "system_prompt": "你是一位语言专家,能够快速准确地进行文本摘要和多语言翻译。",
156
  "message_examples": [
157
- "总结这篇新闻的主要内容,不超过三句话。",
158
- "将这段英文翻译成中文:'Gradio is an open-source Python library...'",
159
- "推荐三部适合周末看的科幻电影。"
160
  ]
161
  }
162
  ]
163
  },
164
- "inclusionai/ring-mini-2.0": {
165
  "model_id": "inclusionai/ring-mini-2.0",
166
- "display_name": "💍 Ring-mini-2.0 (3B)",
167
- "description": "一款经过量化、极致高效的推理模型,为速度和效率要求严苛的资源受限环境(如边缘计算)而设计。",
168
  "prompt_scenarios": [
169
  {
170
- "title": "生活日常助手",
171
- "system_prompt": "你是一位乐于助人的生活助手,可以处理各种日常请求。",
172
  "message_examples": [
173
- "帮我设置一个25分钟的番茄钟。",
174
- "在我的购物清单里加入牛奶和面包。",
175
- "查询今天北京的天气。"
176
  ]
177
  },
178
  {
179
- "title": "简单代码片段",
180
- "system_prompt": "你是一位代码片段生成器,为常见的编程问题提供简洁、正确的代码示例。",
181
  "message_examples": [
182
- "提供一个用 JavaScript 实现的 GET 请求示例。",
183
- "如何用 CSS 让一个 div 水平居中?",
184
- "1数到10"
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
- - **状态:** `已完成 (Completed)`
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
- - **状态:** `已完成 (Completed)`
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
- - **状态:** `已完成 (Completed)`
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
- - **状态:** 已完成 (Completed)
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
- - **状态:** 已完成 (Completed)
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
- - **状态:** 已完成 (Completed)
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
- - **状态:** 已完成 (Completed)
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
- - **状态:** 已完成 (Completed)
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
- - **状态:** 已完成 (Completed)
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
- - **状态:** 已完成 (Completed)
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
- if code_type == "静态页面":
92
- # UI 的选项中解析出模型名称
93
- if "inclusionai/ling-1t" in model_choice:
94
- model_name = "inclusionai/ling-1t"
95
- elif "inclusionai/ring-flash-2.0" in model_choice:
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
- return
127
- yield
 
 
 
 
 
 
 
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
- # 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
- # 在第一个块前加上模型名称
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
- """当用户切换模型时,更新UI"""
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
- """当用户从场景数据集中选择一个场景时,更新UI"""
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
- """创建并返回聊天标签页的所有Gradio组件"""
91
 
92
- # 从配置中提取模型信息用于UI展示
93
- # choices 是一个 (display_name, model_id) 的元组列表
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.TabItem("聊天", id="chat_tab"):
100
- with gr.Row():
101
- with gr.Column(scale=3):
102
- chatbot = gr.Chatbot(
103
- label="聊天窗口",
104
- bubble_full_width=False,
105
- height=500,
106
- value=[(None, "Hello! I'm Ling. Try selecting a scenario and a message example below to get started.")]
107
- )
108
- with gr.Row():
109
- chat_input = gr.Textbox(placeholder="Ask me anything...", label="输入框", show_label=False, scale=4)
110
- send_button = gr.Button("发送", variant="primary", scale=1)
111
-
112
- # 新的场景化示例区域
113
- with gr.Accordion("✨ 试试这些场景...", open=True):
114
- # 场景选择器
115
- scenario_selector = gr.Dataset(
116
- components=[gr.Textbox(visible=False)],
117
- samples=[[s["title"]] for s in default_scenarios],
118
- label="系统提示示例",
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
- model_description = gr.Markdown(default_spec["description"])
136
- system_prompt = gr.Textbox(
137
- label="System Prompt",
138
- lines=8,
139
- placeholder=CHAT_SYSTEM_PROMPT_PLACEHOLDER,
140
- value=default_scenarios[0]["system_prompt"] if default_scenarios else ""
141
  )
142
- temperature_slider = gr.Slider(minimum=0.0, maximum=2.0, value=1.0, step=0.1, label="Temperature")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 subprocess
3
- import threading
4
- import queue
5
- import uuid
6
  import os
7
- import tempfile
8
  import sys
9
- import logging
10
  import time
 
 
 
11
  from models import generate_code_for_tab
 
12
 
13
- # 配置日志
14
- logger = logging.getLogger(__name__)
15
 
16
- # 用于存储当前运行的 Gradio 子进程
17
- running_processes = {}
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
- """获取用于生成 Gradio 应用的 System Prompt"""
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 script must end with the app launch command, like `demo.launch()`.
35
- Do not include any explanations, just the raw Python code.
 
36
  """
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  def get_html_sys_prompt():
39
- """获取用于生成静态页面的 System Prompt"""
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
- """在单独的线程中运行Gradio应用,以避免阻塞主应用"""
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
- """返回带 CSS 旋转动画的 HTML"""
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, session_id: gr.State):
101
- """生成代码并根据类型决定如何展示"""
 
102
  logger.info(f"--- [Code Generation] Start ---")
103
  logger.info(f"Code Type: {code_type}, Model: {model_choice}, Prompt: '{user_prompt}'")
104
-
105
- stop_process(session_id)
 
 
 
 
106
 
107
  if not user_prompt:
108
- yield "Please enter a prompt.", gr.HTML("Preview will appear here.")
109
  return
110
 
111
- if code_type == "静态页面":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- current_time = time.time()
148
- if current_time - last_update_time >= 5:
149
- escaped_code = full_code_for_preview.replace("'", "&apos;").replace('"', '&quot;')
150
- preview_html = f"""
151
- <div style="width: 100%; height: 600px; border: 1px solid #ddd; overflow: hidden; position: relative; background-color: #f9f9f9;">
152
- <div style="position: absolute; top: 10px; right: 10px; z-index: 10;">
153
- <div class="spinner-small"></div>
154
- </div>
155
- <iframe srcdoc='{escaped_code}'
156
- style="position: absolute; top: 0; left: 0; width: 200%; height: 200%; transform: scale(0.5); transform-origin: 0 0; border: none;">
157
- </iframe>
158
- </div>
159
- <style>.spinner-small {{ border: 2px solid rgba(0,0,0,0.1); width: 18px; height: 18px; border-radius: 50%; border-left-color: #09f; animation: spin 1s ease infinite; }} @keyframes spin {{ 0% {{ transform: rotate(0deg); }} 100% {{ transform: rotate(360deg); }} }}</style>
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("'", "&apos;").replace('"', '&quot;')
@@ -172,66 +233,105 @@ def generate_code(code_type, model_choice, user_prompt, session_id: gr.State):
172
  </iframe>
173
  </div>
174
  """
175
- yield full_code_with_think, gr.HTML(final_preview_html)
 
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 = "退出全屏" if is_fullscreen else "全屏预览"
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
- """创建代码生成功能的UI Tab"""
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(["静态页面", "Gradio 应用"], value="静态页面", label="Code Type")
196
 
197
- gr.Markdown("### 2. 选择模型")
198
  model_choice_radio = gr.Radio(
199
- ["效果更好 (使用 Ling-1T)", "更快速 (使用 Ring-flash-2.0)"],
200
- value="效果更好 (使用 Ling-1T)",
201
  label="Model Selection"
202
  )
203
 
204
- gr.Markdown("### 3. 输入你的需求")
205
- prompt_input = gr.Textbox(lines=5, placeholder="例如:创建一个带有标题和按钮的简单页面", label="Prompt")
206
- gr.Examples(
207
- examples=[
208
- "创建一个在黑色背景上不断绽放五彩烟花的 Canvas 动画。",
209
- "生成一个具有流光溢彩效果的 Canvas 特效。",
210
- "设计一个能与鼠标交互的粒子系统 Canvas 动画。",
211
- "用 HTML Canvas 实现一个经典的贪吃蛇游戏。",
212
- "创建一个模拟'大象牙膏'化学实验的 Canvas 动画:一个容器中,彩色泡沫不断快速涌出、膨胀、溢出,充满整个屏幕。",
213
- "创建一个梦幻的低多边形漂浮岛屿场景,带有动态光照和柔和的动画,在一个单一的HTML文件中。使用 d3.js 。"
214
- ],
215
- inputs=prompt_input,
216
- label="✨ 不妨试试这些酷炫的例子"
217
- )
218
- generate_button = gr.Button("生成代码", variant="primary")
 
 
 
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("全屏预览", scale=0)
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, session_state],
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("'", "&apos;").replace('"', '&quot;')
 
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
- # 模拟 Ring 模型进行网页检索和总结
7
- # 在真实应用中,这里会使用 SEARCH_SYSTEM_PROMPT
8
- summary = f"根据对网络的检索,关于 {query}’ 的总结如下:\n\n这是一个由 Ring 模型模拟生成的摘要性回答。在实际应用中,模型会访问互联网,抓取相关信息,并生成一段高质量的总结。\n\n### 关键点:\n- **要点一**: 这是第一个关键信息。\n- **要点二**: 这是第二个关键信息。\n- **要点三**: 这是第三个关键信息。"
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
- with gr.TabItem("网页检索", id="search_tab"):
21
- gr.Markdown("<p align='center'>由 <strong>Ring 💍</strong> 模型驱动</p>")
22
- with gr.Column():
23
- search_input = gr.Textbox(label="搜索输入区", placeholder="Enter a question to search and summarize...")
24
- gr.Examples(
25
- examples=["AI 的最新进展是什么?", "解释一下 Transformer 架构", "总结今天的新闻头条"],
26
- label="示例提示",
27
- inputs=[search_input]
28
- )
29
- search_button = gr.Button(" 检索")
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
- from utils import WORKFLOW_SVG_DIAGRAM
3
- from config import WORKFLOW_GENERATE_SYSTEM_PROMPT, WORKFLOW_EXECUTE_SYSTEM_PROMPT
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
- return svg_diagram, initial_status, initial_chatbot_message, initial_state
 
 
17
 
18
- def handle_workflow_chat(user_input, chat_history, state):
19
- """处理工作流的交互式聊天"""
20
- if not state or not state.get("steps"):
21
- return chat_history, state, "", gr.update(interactive=False)
22
 
23
- chat_history.append((user_input, None))
24
-
25
- current_step_index = state["current_step"]
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
- chat_history.append((None, response))
46
-
47
- yield chat_history, state, new_status, gr.update(interactive=interactive)
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  def create_workflow_tab():
50
- with gr.TabItem("工作流执行", id="workflow_tab"):
51
- gr.Markdown("<p align='center'>由 <strong>Ring 💍</strong> 模型驱动</p>")
 
 
52
  with gr.Row():
53
  with gr.Column(scale=1):
54
- workflow_description_input = gr.Textbox(lines=7, label="工作流描述", placeholder="Describe the steps of your workflow...")
55
- gr.Examples(
56
- examples=["规划一次东京之旅", "新用户引导流程", "内容审批流程"],
57
- label="示例提示",
58
- inputs=[workflow_description_input]
 
 
 
59
  )
60
- generate_workflow_button = gr.Button("✨ 生成工作流")
61
- workflow_visualization_output = gr.HTML(label="工作流图示")
62
  with gr.Column(scale=1):
63
- workflow_status_output = gr.Markdown(label="节点状态")
64
- workflow_chatbot = gr.Chatbot(label="执行对话", height=400)
65
- workflow_chat_input = gr.Textbox(label="交互输入", placeholder="Your response...", interactive=False)
 
66
 
67
  return {
68
- "workflow_description_input": workflow_description_input,
69
- "generate_workflow_button": generate_workflow_button,
70
- "workflow_visualization_output": workflow_visualization_output,
71
- "workflow_status_output": workflow_status_output,
72
- "workflow_chatbot": workflow_chatbot,
73
- "workflow_chat_input": workflow_chat_input
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
+