Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 122 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,139 @@
## Embedded AI WorkflowVS Code 插件
## Embedded AI Workflow · VS Code 插件

一个在 VS Code 中可视化编排嵌入式 AI 生成流程的插件。通过工作流面板,将需求分解为可复用的步骤,辅助自动化生成并组织 STM32/ESP32 等 MCU 的工程代码
一个在 VS Code 中用“工作流 + AI”自动生成 MCU 工程代码的插件。你可以在面板中选择节点、导入/导出工作流 JSON,选择目标平台(STM32 HAL 或 ESP-IDF),一键生成最小可编译的示例工程(含构建文件),生成内容会写入工作区的 `generated/` 目录

### 功能概览
- **命令**: 通过 VS Code 命令面板运行 `Open Embedded AI Workflow`(命令 ID:`workflow.openPanel`)打开工作流面板。
- **Webview 面板**: 插件提供一个可视化面板(保留上下文、允许脚本),用于承载工作流 UI。
- **工程结构清晰**: TypeScript 源码、静态资源和编译产物分离。
### 功能特性
- **工作流面板**:内置节点库(GPIO Out、Task、Timer、UART 等),点击即可向工作流 JSON 添加节点。
- **导入/导出**:从磁盘导入工作流 JSON,或将当前工作流导出为 JSON 文件。
- **目标平台选择**:支持 STM32 HAL 与 ESP-IDF 两种平台,自动附带对应的构建文件。
- **AI 生成代码(可配置)**:默认对接 OpenAI,也支持自定义 HTTP 接口。AI 按你的工作流 JSON 生成工程代码清单与文件内容。
- **生成目录**:所有生成文件写入到工作区根目录下的 `generated/` 目录,便于查看与版本管理。
- **构建文件**:
- STM32 HAL:附带 `CMakeLists.txt`,示例 `src/main.c`
- ESP-IDF:附带根 `CMakeLists.txt` 与 `main/CMakeLists.txt`、示例 `main/main.c`,并附 `platformio.ini` 以便使用 PlatformIO 构建

### 目录结构
- `embedded-ai-workflow/`
- `src/extension.ts`: 插件入口,注册命令并打开面板。
- `src/panel/WorkflowPanel.ts`: Webview 面板逻辑与 HTML 内容。
- `media/`: Webview 前端的 `main.js`、`styles.css` 等静态资源。
- `out/`: TypeScript 编译后的 JS 输出(由 `tsc` 生成)。
- `package.json`: 插件元数据、命令、脚本和依赖。
- `tsconfig.json`: TypeScript 配置。
### 截图 / GIF(占位说明)
- 工作流面板左侧为节点库与平台选择、导入/导出按钮;右侧为工作流 JSON 编辑区与“生成代码”按钮。
- 点击节点库中的“GPIO Out”、“Task”等按钮,会自动向 JSON 中添加对应节点。
- 选择目标平台后,点击“生成代码”,在 VS Code 状态区显示进度与结果。
- 生成完成后,工作区会新建 `generated/` 目录,内含工程文件与构建脚本。

### 开发与调试
前置条件:Node.js 18+、VS Code 1.88+。
> 注:截图/GIF 将在后续上传至仓库的 `media/` 目录并引用到本文档。

---

## 安装

1. 安装依赖并启动监听编译:
### 从 VS Code Marketplace(推荐)
1. 打开 VS Code 扩展市场,搜索 “Embedded AI Workflow”。
2. 点击安装,安装完成后即可在命令面板中使用。

或使用命令行:
```bash
cd embedded-ai-workflow
npm install
npm run watch
code --install-extension your-publisher.embedded-ai-workflow
```
2. 在 VS Code 中按 `F5`(Run Extension)启动插件调试宿主,打开命令面板执行 `Open Embedded AI Workflow`。

### 构建与打包
使用 `vsce` 打包扩展为 `.vsix`:
### 从源码/VSIX 安装
```bash
npm install -g @vscode/vsce
cd embedded-ai-workflow
npm install
npm run compile
npm install -g @vscode/vsce
npm run package

# 生成的 .vsix 位于项目根目录,版本号以实际输出为准
code --install-extension embedded-ai-workflow-<version>.vsix
```

---

## 使用教程:点亮 LED(工作流 → 生成 STM32 HAL 工程)

1) 打开命令面板(Ctrl/Cmd+Shift+P)运行:`Workflow: Open Embedded AI Workflow`。
2) 在左侧“节点库”点击添加:`GPIO Out` 与 `Task`;或直接在右侧粘贴以下工作流 JSON:
```json
{
"nodes": [
{"id": 1, "type": "GPIO Out", "label": "LED"},
{"id": 2, "type": "Task", "label": "BlinkTask"}
],
"edges": [
{"from": 2, "to": 1}
]
}
```
3) 选择目标平台:`STM32 HAL`。
4) 点击“生成代码”。
5) 生成完成后,在工作区根目录查看 `generated/`:
- `CMakeLists.txt`
- `src/main.c`
- `README.md`

> 若未配置 AI Key 或访问失败,插件会使用本地 Mock 模板生成最小示例,便于你快速验证流程。

---

## 配置

在 VS Code 设置中搜索 “Embedded AI Workflow”,或在 `settings.json` 中手动配置:

```json
{
"embedded-ai-workflow.ai.provider": "openai", // openai | http
"embedded-ai-workflow.ai.openai.baseUrl": "https://api.openai.com/v1",
"embedded-ai-workflow.ai.openai.model": "gpt-4o-mini",
"embedded-ai-workflow.ai.openai.apiKey": "", // 留空则读取环境变量 OPENAI_API_KEY

"embedded-ai-workflow.ai.http.endpoint": "", // 自定义 HTTP:POST { workflow, platform }
"embedded-ai-workflow.ai.http.apiKeyHeader": "Authorization",
"embedded-ai-workflow.ai.http.apiKey": ""
}
```

- 环境变量支持:`OPENAI_API_KEY`
- 消息返回格式:AI 需返回严格 JSON:
```json
{
"files": [
{ "path": "src/main.c", "content": "..." },
{ "path": "CMakeLists.txt", "content": "..." }
]
}
```
生成的 `.vsix` 可通过 VS Code 安装:

---

## 开发与调试

前置条件:Node.js 18+、VS Code 1.88+。

```bash
code --install-extension embedded-ai-workflow-0.0.1.vsix
cd embedded-ai-workflow
npm install
npm run watch
```
在 VS Code 中按 `F5`(Run Extension)启动插件调试宿主,执行命令 `Workflow: Open Embedded AI Workflow` 打开面板。

---

## 未来规划 / Roadmap

- 更多 MCU 支持:nRF52、RP2040、AVR、MSP430、GD32 等生态与 SDK 集成
- 节点插件生态:定义节点插件 API 与社区市场,支持第三方节点库
- 可视化调试:运行时可视化、数据流/事件流追踪、任务级断点与日志
- 画布编辑器:在面板中直接连线、拖拽、参数化节点
- 模板与知识库:内置工程模板、RAG 检索厂商 HAL/驱动文档提升生成质量
- 项目脚手架:一键拉取并配置对应平台 SDK/Toolchain
- 离线模型支持:可选本地/边缘大模型,保护知识产权

---

## 贡献

欢迎 Issue / PR!请先阅读 `embedded-ai-workflow/` 内代码结构,提交前确保 `npm run compile` 通过。

### 使用方法
1. 打开命令面板(Ctrl/Cmd+Shift+P),执行 `Open Embedded AI Workflow`。
2. 在出现的面板中配置或运行你的 AI 工作流(当前为占位 UI,可按需扩展)。
---

### 许可证
本项目采用 MIT 许可证。
## 许可证

### 备注
仓库中与“AI 工作流插件”无关的示例/旧代码已移除或将被清理,实际插件代码位于 `embedded-ai-workflow/` 目录下。
MIT License
104 changes: 99 additions & 5 deletions embedded-ai-workflow/media/main.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,105 @@
(function(){
const vscode = typeof acquireVsCodeApi === 'function' ? acquireVsCodeApi() : undefined;
const button = document.getElementById('action');
if (button) {
button.addEventListener('click', () => {
if (vscode) {
vscode.postMessage({ type: 'hello', text: 'Hello from Webview' });
const generateBtn = document.getElementById('generate');
const workflowTextarea = document.getElementById('workflowJson');
const statusEl = document.getElementById('status');
const platformSel = document.getElementById('platform');
const importBtn = document.getElementById('importJson');
const exportBtn = document.getElementById('exportJson');

function setStatus(level, text){
if (!statusEl) return;
const color = level === 'error' ? '#ef4444' : level === 'warn' ? '#f59e0b' : level === 'success' ? '#22c55e' : '#9ca3af';
const prefix = level === 'error' ? '错误' : level === 'warn' ? '警告' : level === 'success' ? '成功' : '状态';
statusEl.innerHTML = `<div style="white-space:pre-wrap;line-height:1.4;color:${color}"><strong>${prefix}:</strong> ${escapeHtml(String(text))}</div>`;
}

function escapeHtml(str){
return str.replace(/[&<>"']/g, (c)=>({
'&':'&amp;', '<':'&lt;', '>':'&gt;', '"':'&quot;', "'":'&#39;'
})[c] || c);
}

// Node library: clicking a node template inserts into JSON
function setupNodeLibrary(){
const lib = document.getElementById('nodeLibrary');
if (!lib) return;
lib.addEventListener('click', (e)=>{
const target = e.target;
if (!target || !target.classList || !target.classList.contains('node')) return;
try {
const raw = workflowTextarea && 'value' in workflowTextarea ? workflowTextarea.value : '';
const wf = JSON.parse(raw || '{"nodes":[],"edges":[]}');
wf.nodes = Array.isArray(wf.nodes) ? wf.nodes : [];
const type = target.getAttribute('data-node-type') || 'Node';
const label = target.getAttribute('data-node-label') || type;
const nextId = computeNextId(wf.nodes);
wf.nodes.push({ id: nextId, type, label });
workflowTextarea.value = JSON.stringify(wf, null, 2);
setStatus('success', `已添加节点:${type}`);
} catch (e) {
setStatus('error', `无法添加节点,JSON 解析失败:${e && e.message ? e.message : String(e)}`);
}
});
}

function computeNextId(nodes){
let maxId = 0;
if (Array.isArray(nodes)){
for (const n of nodes){
const v = typeof n.id === 'number' ? n.id : parseInt(n.id, 10);
if (!isNaN(v)) maxId = Math.max(maxId, v);
}
}
return maxId + 1;
}

if (generateBtn && vscode) {
generateBtn.addEventListener('click', () => {
try {
const raw = workflowTextarea && 'value' in workflowTextarea ? workflowTextarea.value : '';
const json = JSON.parse(raw || '{}');
const platform = platformSel && 'value' in platformSel ? platformSel.value : 'stm32-hal';
setStatus('info', '已提交生成请求...');
vscode.postMessage({ type: 'generate-code', workflow: json, platform });
} catch (e) {
setStatus('error', `JSON 解析失败:${e && e.message ? e.message : String(e)}`);
}
});
}

if (importBtn && vscode) {
importBtn.addEventListener('click', () => {
vscode.postMessage({ type: 'import-workflow' });
});
}

if (exportBtn && vscode) {
exportBtn.addEventListener('click', () => {
try {
const raw = workflowTextarea && 'value' in workflowTextarea ? workflowTextarea.value : '';
const json = JSON.parse(raw || '{}');
vscode.postMessage({ type: 'export-workflow', workflow: json });
} catch (e) {
setStatus('error', `无法导出,JSON 解析失败:${e && e.message ? e.message : String(e)}`);
}
});
}

window.addEventListener('message', (event)=>{
const msg = event.data;
if (!msg || !msg.type) return;
if (msg.type === 'status') {
setStatus(msg.level || 'info', msg.text || '');
} else if (msg.type === 'workflow-loaded') {
if (workflowTextarea) {
workflowTextarea.value = JSON.stringify(msg.workflow || {}, null, 2);
setStatus('success', '已导入工作流 JSON');
}
} else if (msg.type === 'save-complete') {
setStatus('success', '已导出工作流 JSON');
}
});

setupNodeLibrary();
})();
25 changes: 25 additions & 0 deletions embedded-ai-workflow/media/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,29 @@ h1 {

.button:hover {
background: rgba(34,211,238,0.08);
}

textarea#workflowJson {
width: 680px;
max-width: 90vw;
background: #0b1220;
color: var(--text);
border: 1px solid #1f2937;
border-radius: 8px;
padding: 10px 12px;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 12px;
}

.panel.sidebar .button.node {
justify-self: stretch;
}

.select {
width: 100%;
background: #0b1220;
color: var(--text);
border: 1px solid #1f2937;
border-radius: 8px;
padding: 6px 8px;
}
Loading