diff --git a/.gitignore b/.gitignore index b944c4e..aa0810a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,38 @@ .env -.vscode -nanobot-0.1.4.post4 -data -_research -.trae +.env.* + +# OS / editor .DS_Store +Thumbs.db +.vscode/ +.idea/ + +# Python +__pycache__/ +*.py[cod] +.venv/ +venv/ + +# Node +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Build / artifacts +dist/ +build/ +*.log + +# Runtime data (mounted as DATA_ROOT in Docker) +data/ + +# Intermediate docs/state (do not commit) +.claude/ +.work/ + +# Local scratch +_research/ +.trae/ +nanobot-*/ \ No newline at end of file diff --git a/README.md b/README.md index acff494..104ad64 100644 --- a/README.md +++ b/README.md @@ -1,450 +1,149 @@ -[🇨🇳 简体中文](./README.md) | [🇬🇧 English](./README_en.md) +## 项目简介 -# 🦞 龙虾问数 (DataClaw) + -> **释放你的数据潜能,让分析像养龙虾一样简单爽快!** 🌊📊 -> 龙虾问数 (DataClaw) 是一个智能的、AI 驱动的数据分析平台。通过自然语言与你的数据对话,瞬间生成可视化图表,轻松搭建仪表盘——从此告别繁琐的 SQL 语句! +### 项目概述 -*** +**全源灵动**是一个面向数据分析与知识检索场景的 AI 平台:用自然语言问数、自动生成图表与报告,并管理分析产物,帮助团队更快得到可验证、可复用的结论。 -## ✨ 为什么选择龙虾问数? +### 项目起源 -受够了为了画个简单的柱状图而写半天复杂的 SQL 语句吗?龙虾问数就是你的私人数据科学家。借助强大的大语言模型 (LLM) 和智能 Agent 工作流,它能将你的自然语言提问精准转化为数据库查询,提取数据,并即时渲染出美观的可视化图表。 +数据分散在不同系统与文件中时,手写 SQL、手工出图、反复同步口径的成本高且难复用。全源灵动将“提问 → 查询 → 可视化 → 产出”串联为一条可复用的分析链路。 -无论你是要查询庞大的 Supabase/PostgreSQL 数据库,还是随手丢进一个 CSV 文件,龙虾问数都能轻松拿捏!🚀 +### 项目定位 -## 🌟 核心特性 +- 目标用户:业务/数据分析、运营、产品、研发等需要自助分析与报告产出的角色 +- 适用场景:NL2SQL 问数、知识库检索增强、自动图表与报告产出、任务委派与跟踪(A2A) -- **🗣️ 自然语言转 SQL**: 用大白话提问!它能理解你的数据表结构,生成准确的 SQL,甚至在报错时进行自我纠正 (Self-correction)。 -- **📚 智能知识库检索 (RAG)**: 支持上传 Word、PPT、PDF 等多种格式文档,通过向量检索增强回答,让你的私有文档“开口说话”。 -- **📈 即时数据可视化**: 拒绝枯燥的生肉表格,根据数据特征自动生成交互式图表。 -- **🗂️ 动态多数据源**: 无缝连接 PostgreSQL、Supabase,以及本地 CSV/Excel 文件上传解析。 -- **🧠 灵活的模型接入**: 原生集成 LiteLLM,支持随插随用 OpenAI、DeepSeek、智谱、通义千问 (DashScope)、火山引擎或任何兼容的 LLM 提供商。 -- **🛠️ 强大的 Agent 技能拓展**: 基于核心 `nanobot`框架(`OpenClaw`的精简版)构建。支持通过斜杠命令 (`/`) 快速调用自定义工具 (Skills),完美贴合特定业务逻辑。 -- **📊 可定制仪表盘 (Dashboard)**: 一键将对话中生成的图表固定到看板,拖拽布局,随时查看核心指标。 -- **📦 智能产物管理 (Artifact)**: 自动提取对话中生成的各种文件(网页报告、PDF、PPT、图片等),提供一键内嵌预览与下载功能,让成果触手可及。 +### 核心价值 -*** +- 更快:把“写 SQL / 查资料 / 出图”合并为一次对话驱动的分析流程 +- 更稳:对关键结果提供可回溯的查询与产物(Artifact) +- 更灵活:支持多数据源与可配置的模型接入 -## 📸 界面预览 +## 整体架构与技术栈 -
-

💬 对话式分析界面

- -
-
-

📊 可定制仪表盘

- -
-
-

📚 智能知识库问答

- -
-
-

📦 智能产物预览 (Artifact)

- -
- -
- -## 🏗️ 项目架构 - -DataClaw 的架构主要由四个核心部分组成: - -1. **`frontend/`** 🎨: 闪亮的外壳。基于 **React 19**、**Vite**、**TailwindCSS** 和 **Zustand** 构建。拥有类似微信/ChatGPT的对话界面、支持流式思考过程渲染以及交互式图表展示。 -2. **`backend/`** ⚙️: 强健的肌肉。一个 **FastAPI** 后端服务,负责管理项目、数据源连接、用户会话持久化以及作为 API 网关。 -3. **`nanobot/`** 🧠: 智慧的大脑。核心的 AI Agent 框架,负责处理意图路由、NL2SQL 转换、Schema 缓存管理以及与 LLM 的底层交互。 -4. **`data/`** 🗄️: 运行时数据目录。与代码目录解耦,存放上传文件、会话、技能工作区、报告与配置缓存。 +### 系统架构图 ```mermaid graph TD - User([👤 用户 / 浏览器]) -->|HTTP / WebSocket| Frontend - - subgraph DataClaw 系统 - Frontend[🎨 frontend
React / Vite / Zustand] - Backend[⚙️ backend
FastAPI / 会话 / 数据源管理] - Nanobot[🧠 nanobot
AI Agent / NL2SQL / RAG] - Data[🗄️ data
运行时文件 / 产物缓存] - - Frontend -->|REST API / SSE| Backend - Backend <-->|任务委派 / 函数调用| Nanobot - Backend -->|读写| Data - Nanobot -->|读写| Data - end - - subgraph 外部服务与数据 - LLM([🤖 LLM 供应商
OpenAI, DeepSeek, 智谱等]) - DB[(📊 数据源
PostgreSQL, CSV, Supabase等)] - end - - Nanobot <-->|Prompt / Completion| LLM - Backend <-->|连接 / 查询| DB - Nanobot -.->|Schema 检索 / SQL 执行| DB + User([用户/浏览器]) -->|HTTP| WebUI[前端 WebUI] + WebUI -->|REST/SSE| API[后端 API] + API -->|读写| Data[(DATA_ROOT)] + API -->|连接/查询| DB[(数据源)] + API -->|调用| LLM([外部 LLM]) ``` -*** +### 技术栈分类 -## 🚀 快速开始 +- 后端:FastAPI(Python 3.11+) +- 前端:React 19 + Vite + TailwindCSS + Zustand +- 数据库: + - 业务元数据/会话:SQLite(默认落在 `DATA_ROOT`) + - 数据分析数据源:PostgreSQL / ClickHouse / 上传文件(CSV/Excel 等) +- 部署:Docker Compose(推荐) -准备好大显身手了吗?让我们把龙虾问数在你的本地跑起来! +## 项目目录概览 -### 1. 配置环境变量 🔧 +```text +. +├── dataclaw-api/ # 后端 FastAPI(API 网关、会话与数据源管理、Agent 能力) +├── dataclaw-ui/ # 前端 React + Vite(Nginx 生产静态托管) +├── agent-core/ # Agent 核心(nanobot) +├── dataclaw-voice/ # Whisper 语音服务(可选) +├── data/ # 运行时数据目录(可挂载为 DATA_ROOT) +└── docker-compose.yml # 一键启动 +``` -在项目根目录下,复制并重命名环境配置模板: +## 核心业务功能 + +- **自然语言转 SQL**:理解数据表结构,生成/修正 SQL 并执行查询 +- **知识库检索(RAG)**:上传多种格式文档,向量检索增强问答 +- **即时可视化**:根据数据特征生成交互式图表 +- **动态多数据源**:PostgreSQL / Supabase / 本地文件上传解析等 +- **模型配置**:支持接入 OpenAI 兼容接口的外部模型服务 +- **产物管理(Artifact)**:自动提取与管理报告/图片/PDF/PPT 等结果文件 +- **A2A 任务委派与跟踪**:把任务委派到远端 Agent,并可订阅状态与产物 + +## 实际应用场景示例 + +1. **运营日报/周报**:提问“近 7 天新增用户趋势与转化漏斗”,自动生成图表与报告 +2. **产品指标分析**:对留存、转化、渠道贡献等指标进行归因分析,并固化仪表盘 +3. **知识库问答**:上传制度/方案/投标文件等,自动总结与检索引用 + +## 帮助解决的核心问题 + +- SQL 门槛高、分析链路长、结果难复用 +- 多数据源/多口径带来的协作成本高 +- 报告产出与分享效率低 + +## 快速开始 + +### 环境要求 + +- Docker Desktop(推荐) +- (本地开发模式可选)Python 3.11+、Node.js 20+ + +### 安装步骤 + +在项目根目录复制环境配置模板: ```bash cp .env.example .env ``` -请记得编辑根目录下的 `.env` 文件,填入你的实际配置(如 QQ 邮箱 SMTP 授权码等)。 - -> **QQ 邮箱获取 SMTP 授权码最新教程:** -> 1. 登录 QQ 邮箱网页版 (mail.qq.com) -> 2. 点击页面顶部的“设置” -> “账号”选项卡 -> 3. 向下滚动找到“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务” -> 4. 确保“POP3/SMTP服务”已点击“开启” -> 5. 点击下方的“生成授权码”,使用手机 QQ 扫码或按提示发送短信 -> 6. 验证通过后将获得一串 **16位随机字母组合**,将其复制填入 `.env` 文件中的 `SMTP_PASSWORD` 字段 - -### 2. 生产模式(推荐,无需 Node.js)📦 - -#### 2.1 打包 wheel(产物输出到根目录 `dist/`) - -如需自行打包发布,请按以下顺序执行:先构建前端,再构建后端 wheel,并将产物输出到项目根目录 `dist/`(与 `backend/` 同级)。 +### 运行步骤(Docker 全量启动) ```bash -# 1) 构建前端静态资源 -cd frontend -npm install -npm run build - -# 2) 构建后端 wheel,并输出到根目录 dist/ -cd ../backend -uv build --wheel --out-dir ../dist +docker compose up -d --build ``` -构建完成后,wheel 位于项目根目录 `dist/`,例如 `dist/dataclaw-0.1.0-py3-none-any.whl`。 +访问地址: +- 前端:`http://localhost:10002/` +- 后端(直连):`http://localhost:10001/docs` -#### 2.2 安装与启动(使用 2.1 的 wheel) +运行时数据目录: +- 默认挂载 `./data` 到容器内 `/app/data`(对应 `DATA_ROOT=/app/data`),用于存放上传文件、会话与配置等。 -请确保你已安装 Python 3.11+ 与 `uv`。生产模式使用已打包的 wheel(2.1 产物),无需安装 Node.js。 +### 默认账号 + +系统首次注册的用户将自动成为管理员(示例:用户名 `admin`,密码 `admin`)。 + +### 常见问题 + +- **后端构建失败(APT 连接失败)**:可使用国内镜像构建: ```bash -# 在项目根目录创建独立虚拟环境(不依赖 source 激活) -uv venv .venv -source .venv/bin/activate - -# 安装已打包的 wheel 到该虚拟环境 -uv pip install ./dist/dataclaw-*.whl - -# 启动服务(默认 http://127.0.0.1:8000) -dataclaw start +APT_CN_MIRROR=true docker compose build ``` -常用服务控制命令(同一虚拟环境): +- **接口返回 401**:需要先登录并携带 `Authorization: Bearer ` +- **接口返回 422**:请求体不符合后端 schema(例如 A2A `message:send` 需要 `message.parts[0].data.project_id`) -```bash -# 查看运行状态 -dataclaw status +## 运行示例截图 -# 自定义监听地址/端口 -dataclaw start --host 0.0.0.0 --port 8000 +![image-20260513161644464](images/image-20260513161644464.png) -# 停止服务 -dataclaw stop -``` +![image-20260513161419895](images/image-20260513161419895.png) -可选环境变量: +![image-20260513161428375](images/image-20260513161428375.png) -```bash -export DATA_ROOT=/absolute/path/to/data -``` +![image-20260513161445508](images/image-20260513161445508.png) -若未设置,默认使用仓库根目录下的 `data/`。服务状态文件与日志默认位于 `DATA_ROOT/run/`。 +![image-20260513162417349](images/image-20260513162417349.png) -### 3. 开发模式(需要 Node.js)🧪 +![image-20260513161532777](images/image-20260513161532777.png) -如果你要调试代码,请使用开发模式。开发模式直接跑源码,不依赖 wheel。 +![image-20260513161554077](images/image-20260513161554077.png) -```bash -cd backend -# 安装/同步后端依赖(会在 backend/.venv 中准备环境) -uv sync +![image-20260513161558547](images/image-20260513161558547.png) -# 启动 FastAPI 服务器 -uv run uvicorn main:app --reload --port 8000 -``` +![image-20260513161604618](images/image-20260513161604618.png) -```bash -cd frontend -# 安装依赖(仅开发模式需要 Node.js) -npm install +![image-20260513161610084](images/image-20260513161610084.png) -# 启动 Vite 开发服务器 -npm run dev -``` +![image-20260513161616405](images/image-20260513161616405.png) -*提示:请确保* *`nanobot`* *核心库已根据项目工作区的要求正确链接或以可编辑模式 (editable mode) 安装。* +![image-20260513161621292](images/image-20260513161621292.png) -### 4. 语音识别服务(可选)🎙️ - -若你希望使用聊天输入框中的语音输入能力,请单独启动 `whisper` 服务: - -```bash -cd whisper -uv venv -source .venv/bin/activate -uv pip install -r requirements.txt -uv run python main.py -``` - -默认服务地址:`http://localhost:8001` -健康检查接口:`GET /health` - -前端配置方式: -1. 点击左下角用户名,打开菜单; -2. 进入「语音输入配置」; -3. 填写服务地址(例如 `http://localhost:8001`); -4. 点击「测试连接」通过后保存。 - -### 5. 初始账号配置 👤 -系统首次注册的用户将自动成为管理员。您可以在登录页面直接点击“注册”按钮创建您的管理员账号(例如:用户名 `admin`,密码 `admin`),随后即可登录并管理项目、数据源和用户。 - -### 6. A2A 模式使用指南 🤖 - -A2A(Agent2Agent)用于让 DataClaw 把任务委托给远端 Agent,并保持任务可跟踪(状态流、产物流、取消、重试)。 - -#### 6.1 在前端启用 A2A(推荐) - -1. 进入 **Skills** 页面,切到 **A2A** 标签页。 -2. 点击新增远端 Agent,填写: - - `name`: 远端 Agent 名称 - - `base_url`: 远端 DataClaw/A2A 网关地址(如 `https://agent-b.example.com`) - - `auth_scheme`: `none` 或 `bearer` - - `auth_token`: 当 `auth_scheme=bearer` 时填写 -3. 点击健康检查,确认 `healthy=true`。 -4. 回到聊天页,开启 **A2A Mode**,选择 `route_mode` 与目标 Agent 后发送问题。 -5. 在消息卡片与 A2A 任务面板中查看 `SUBMITTED/WORKING/COMPLETED/FAILED` 等状态,可执行取消与重试。 - -`route_mode` 建议: -- `auto`: 按项目灰度配置与策略自动决策 -- `local`: 强制本地执行 -- `a2a`: 强制远端 A2A 执行(需选远端 Agent) -- `a2a_first`: 先远端,失败按回退链执行 -- `local_first`: 先本地,按需再回退 - -#### 6.2 API 示例(生产常用) - -以下示例假设服务地址为 `http://127.0.0.1:8000`,并已获取登录令牌 `${TOKEN}`。 - -**示例 1:查看本机 A2A Agent Card** - -```bash -curl -H "Authorization: Bearer ${TOKEN}" \ - http://127.0.0.1:8000/api/v1/a2a/agent-card -``` - -**示例 2:注册远端 Agent** - -```bash -curl -X POST http://127.0.0.1:8000/api/v1/a2a/remote-agents \ - -H "Authorization: Bearer ${TOKEN}" \ - -H "Content-Type: application/json" \ - -d '{ - "project_id": 1, - "name": "Agent-B", - "base_url": "https://agent-b.example.com", - "auth_scheme": "bearer", - "auth_token": "remote-agent-token" - }' -``` - -**示例 3:以 A2A 优先模式发起任务** - -```bash -curl -X POST http://127.0.0.1:8000/api/v1/a2a/messages/send \ - -H "Authorization: Bearer ${TOKEN}" \ - -H "Content-Type: application/json" \ - -d '{ - "project_id": 1, - "message": "请分析最近30天订单转化趋势并给出建议", - "session_id": "chat:demo-a2a", - "remote_agent_id": 3, - "route_mode": "a2a_first", - "fallback_chain": ["a2a", "local", "mcp"], - "idempotency_key": "demo-a2a-001" - }' -``` - -返回结果中的 `task.id` 可用于订阅与管理任务。 - -**示例 4:订阅任务流(SSE)** - -```bash -curl -N -H "Authorization: Bearer ${TOKEN}" \ - http://127.0.0.1:8000/api/v1/a2a/tasks//subscribe -``` - -你会收到类似事件: -- `TaskStatusUpdateEvent`(状态变更) -- `TaskArtifactUpdateEvent`(产物更新) -- `done`(流结束) - -**示例 5:取消任务** - -```bash -curl -X POST -H "Authorization: Bearer ${TOKEN}" \ - http://127.0.0.1:8000/api/v1/a2a/tasks//cancel -``` - -**示例 6:为任务配置 Webhook 回调(离线接收结果)** - -```bash -curl -X POST http://127.0.0.1:8000/api/v1/a2a/tasks//webhooks \ - -H "Authorization: Bearer ${TOKEN}" \ - -H "Content-Type: application/json" \ - -d '{ - "target_url": "https://your-system.example.com/a2a/webhook", - "secret": "your-webhook-secret", - "auth_header": "Bearer your-internal-token" - }' -``` - -#### 6.3 一个完整实战流程 - -场景:你有一个“本地数据分析 Agent”,还接入了“外部行业知识 Agent-B”。 - -1. 在 Skills -> A2A 注册 `Agent-B` 并完成健康检查。 -2. 在聊天页开启 A2A,选择 `route_mode=a2a_first`。 -3. 输入问题: - `请结合外部行业知识与本地销量数据,生成本季度增长策略。` -4. 系统先尝试委托给 `Agent-B`;若远端异常则按回退链降级到本地/MCP。 -5. 在任务面板查看状态流与最终产物,必要时可取消或重试。 - -生产建议: -- 对业务侧请求始终传 `idempotency_key`,避免重复任务。 -- 为长任务配置 webhook,避免客户端断线丢失进度。 -- 在项目级 rollout 配置灰度比例,先小流量启用 A2A 再全量放开。 - -#### 6.4 本地调试 A2A(双实例联调) - -推荐在本机同时启动两个后端实例: -- 实例 A(调用方):`http://127.0.0.1:8000` -- 实例 B(被调用远端 Agent):`http://127.0.0.1:8001` - -分别用两个终端启动(建议使用不同 `DATA_ROOT`,避免数据目录冲突): - -```bash -# 终端1:实例 A -cd backend -DATA_ROOT=/tmp/dataclaw-a uv run uvicorn main:app --reload --port 8000 -``` - -```bash -# 终端2:实例 B -cd backend -DATA_ROOT=/tmp/dataclaw-b uv run uvicorn main:app --reload --port 8001 -``` - -然后在两个实例分别注册并登录,拿到 token: - -```bash -# 分别注册(每个实例首次注册用户会成为管理员) -curl -X POST http://127.0.0.1:8000/api/v1/auth/register \ - -H "Content-Type: application/json" \ - -d '{"username":"admin_a","email":"a@test.com","password":"admin12345"}' - -curl -X POST http://127.0.0.1:8001/api/v1/auth/register \ - -H "Content-Type: application/json" \ - -d '{"username":"admin_b","email":"b@test.com","password":"admin12345"}' - -# 登录并保存 token -TOKEN_A=$(curl -s -X POST http://127.0.0.1:8000/api/v1/auth/login \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "username=admin_a&password=admin12345" | jq -r '.access_token') - -TOKEN_B=$(curl -s -X POST http://127.0.0.1:8001/api/v1/auth/login \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "username=admin_b&password=admin12345" | jq -r '.access_token') -``` - -最后,在实例 A 中把实例 B 注册成远端 Agent(`auth_token` 使用 `TOKEN_B`): - -```bash -curl -X POST http://127.0.0.1:8000/api/v1/a2a/remote-agents \ - -H "Authorization: Bearer ${TOKEN_A}" \ - -H "Content-Type: application/json" \ - -d "{ - \"project_id\": 1, - \"name\": \"local-agent-b\", - \"base_url\": \"http://127.0.0.1:8001\", - \"auth_scheme\": \"bearer\", - \"auth_token\": \"${TOKEN_B}\" - }" -``` - -完成后即可在实例 A 发起 A2A 任务、订阅任务流、取消任务,完成本地端到端联调。 - -*** - -## 🔌 数据源配置说明 - -DataClaw 支持连接多种类型的数据源,以满足不同场景的分析需求。你可以在界面的 **Data Sources** 菜单中点击 **+** 新建并配置它们。以下是常见数据源的详细接入指南: - -
-▶ PostgreSQL (pgsql) - -连接标准的关系型数据库。你既可以通过表单填充分散的参数,也可以直接粘贴完整的 Connection String。 - -- **Host**: 数据库的主机地址。如果你是在本地电脑运行了数据库(如使用 pgAdmin),请填入 `127.0.0.1`(不要填 `localhost`,以避免 Unix Socket 解析错误)。 -- **Port**: 默认一般为 `5432`。 -- **Database**: 你要连接的具体数据库名称。 -- **Username / Password**: 数据库的认证凭据(默认用户通常是 `postgres`)。 -- **Connection String (可选)**: 也可以直接输入类似 `postgresql://postgres:你的密码@127.0.0.1:5432/你的数据库名` 的字符串,它将覆盖上述单独的输入框配置。 -
- -
-▶ Supabase - -专门针对 Supabase 云端 PostgreSQL 数据库优化的连接方式,强制开启 SSL 且默认使用连接池以提高稳定性。 - -- 推荐直接使用 **Connection String** 配置: - 进入你的 Supabase 项目控制台 -> `Project Settings` -> `Database` -> `Connection string` -> 选择 `URI` 选项卡。 - 复制那串类似 `postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres?sslmode=require` 的链接并填入。 -- *注意*: Supabase 默认开启了 Transaction Pooler(端口 6543)。如果想要直连(Direct connection),请将端口改为 `5432`,并确保 URL 中包含 `sslmode=require`。 -
- -
-▶ SQLite - -轻量级的本地文件型数据库,非常适合快速测试或分析单机应用数据。 - -- **File Upload**: 你可以直接点击按钮,从本地上传 `.db`、`.sqlite` 或 `.sqlite3` 后缀的数据库文件。文件会被安全地保存在服务端的上传目录中供分析使用。 -- **File Path (进阶)**: 如果服务部署在服务器上,且 SQLite 文件已存在于服务器的某个绝对路径中,你也可以直接在输入框中填入该文件的绝对路径(如 `/data/my_app.db`)。 -
- -
-▶ CSV - -最常见的数据交换格式,即插即用,无需复杂的数据库配置。 - -- **File Upload**: 与 SQLite 类似,点击按钮选择本地的 `.csv` 文件上传即可。系统会在后台利用 DuckDB 或 Pandas 等引擎将其虚拟化为一个可供 SQL 查询的表。 -- 上传成功后,在对话界面中,你可以直接把这个 CSV 文件当作一张数据库表来“提问”! -
- -*** - -## 🤝 参与贡献 - -有个好点子?发现了一个 Bug?非常欢迎你的加入!随时可以提交 Issue 或 Pull Request。让我们一起让数据分析变得更加有趣! - -*** - -## 💖 特别鸣谢 - -DataClaw 的开发深受以下优秀开源项目的启发,特此致谢: - -- [WrenAI](https://github.com/Canner/WrenAI): 强大的 Text-to-SQL 解决方案,其架构和思路给了我们很大的启发。 -- [Aix-DB](https://github.com/apconw/Aix-DB): 在智能数据分析和交互式体验方面提供了极好的参考。 - -
+![image-20260513161626519](images/image-20260513161626519.png) diff --git a/README_en.md b/README_en.md deleted file mode 100644 index 12fbfeb..0000000 --- a/README_en.md +++ /dev/null @@ -1,400 +0,0 @@ -[🇨🇳 简体中文](./README.md) | [🇬🇧 English](./README_en.md) - -# 🦞 DataClaw - -> **Unleash the claws on your data, making analysis as easy and refreshing as raising lobsters!** 🌊📊 -> DataClaw is your intelligent, AI-powered Data Analysis Platform. Chat with your data, visualize insights instantly, and build dashboards—all through natural language. No SQL degree required! - -*** - -## ✨ Why DataClaw? - -Tired of writing complex SQL queries just to get a simple bar chart? DataClaw acts as your personal data scientist. Powered by advanced LLMs and an intelligent agentic workflow, it translates your questions into database queries, fetches the data, and renders beautiful visualizations on the fly. - -Whether you're querying a massive Supabase/PostgreSQL database or just tossing in a CSV file, DataClaw's got you covered! 🚀 - -## 🌟 Key Features - -- **🗣️ Chat to SQL**: Ask questions in plain English (or Chinese!). DataClaw understands your schema, generates accurate SQL, and self-corrects if things go sideways. -- **📚 Smart Knowledge Base (RAG)**: Support uploading Word, PPT, PDF and other document formats. Enhance answers through vector retrieval, making your private documents "speak". -- **📈 Instant Visualizations**: Returns not just raw tables, but auto-generated interactive charts tailored to your data's shape. -- **🗂️ Multi-Source Ready**: Connects seamlessly to PostgreSQL, Supabase, and local CSV/Excel uploads. -- **🧠 Bring Your Own LLM**: Native integration with LiteLLM. Plug in OpenAI, DeepSeek, Zhipu, DashScope, Volcengine, or any compatible provider. -- **🛠️ Extensible Agent Skills**: Built on top of the powerful `nanobot` framework (a lightweight version of `OpenClaw`). Add custom tools and slash commands (`/`) to tailor the agent to your specific business logic. -- **📊 Customizable Dashboards**: Pin your favorite chat-generated charts to a drag-and-drop dashboard for quick access. -- **📦 Intelligent Artifact Management**: Automatically extracts generated files (HTML reports, PDFs, PPTs, images, etc.) from conversations, providing embedded previews and one-click downloads. - -*** - -## 📸 Screenshots - -
-

💬 Chat Interface

- -
-
-

📊 Customizable Dashboard

- -
-
-

📚 Smart Knowledge Base

- -
-
-

📦 Artifact Preview

- -
- -
- -## 🏗️ Architecture - -DataClaw's architecture mainly consists of four core components: - -1. **`frontend/`** 🎨: The shiny shell. Built with **React 19**, **Vite**, **TailwindCSS**, and **Zustand**. It features a chat-like interface, streaming AI responses, and interactive Vega charts. -2. **`backend/`** ⚙️: The muscle. A **FastAPI** application managing projects, data source connections, user sessions, and API gateways. -3. **`nanobot/`** 🧠: The brain. The core AI agent framework handling NL2SQL, schema caching, prompt injection, and LLM routing. -4. **`data/`** 🗄️: Runtime data root. Decoupled from code directories and used for uploads, sessions, workspace skills, reports, and cached configs. - -```mermaid -graph TD - User([👤 User / Browser]) -->|HTTP / WebSocket| Frontend - - subgraph DataClaw System - Frontend[🎨 frontend
React / Vite / Zustand] - Backend[⚙️ backend
FastAPI / Session / Data Sources] - Nanobot[🧠 nanobot
AI Agent / NL2SQL / RAG] - Data[🗄️ data
Runtime Files / Artifacts] - - Frontend -->|REST API / SSE| Backend - Backend <-->|Task Delegation / Calling| Nanobot - Backend -->|Read / Write| Data - Nanobot -->|Read / Write| Data - end - - subgraph External Services - LLM([🤖 LLM Providers
OpenAI, DeepSeek, etc.]) - DB[(📊 Data Sources
PostgreSQL, CSV, Supabase)] - end - - Nanobot <-->|Prompt / Completion| LLM - Backend <-->|Connect / Query| DB - Nanobot -.->|Schema Retrieval / Exec| DB -``` - -*** - -## 🚀 Quick Start - -Ready to dive in? Let's get DataClaw running on your local machine! - -### 1. Configure Environment Variables 🔧 - -In the root directory of the project, copy and rename the environment template: - -```bash -cp .env.example .env -``` - -Please edit the `.env` file in the root directory and fill in your actual configurations (e.g., QQ Mail SMTP Auth Code). - -> **Guide to getting QQ Mail SMTP Auth Code:** -> 1. Log in to QQ Mail web version (mail.qq.com) -> 2. Click "Settings" (设置) at the top of the page -> "Account" (账号) tab -> 3. Scroll down to find the "POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV Service" section -> 4. Ensure "POP3/SMTP Service" is toggled to "On" (开启) -> 5. Click "Generate Authorization Code" (生成授权码) below it, scan the QR code with mobile QQ or send an SMS as prompted -> 6. After verification, you will get a **16-digit random letter combination**. Copy and paste it into the `SMTP_PASSWORD` field in your `.env` file - -### 2. Production Mode (Recommended, No Node.js Required) 📦 - -Ensure you have Python 3.11+ installed. The pre-built React frontend is bundled in the Python wheel, so you don't need Node.js for production deployment. - -#### 2.1 Build the wheel (output to `dist/`) - -```bash -# First, build the frontend -cd frontend -npm install -npm run build - -# Then, build the backend wheel -cd ../backend -uv build --wheel --out-dir ../dist -``` - -Once built, the wheel is located in the project root `dist/` directory, e.g., `dist/dataclaw-0.1.0-py3-none-any.whl`. - -#### 2.2 Install and Run (using the wheel from 2.1) - -Ensure Python 3.11+ and `uv` are installed. Production mode uses the packaged wheel from 2.1, and does not require Node.js. - -```bash -# Create an isolated virtual environment in project root -uv venv .venv -source .venv/bin/activate - -# Install built wheel into that environment -uv pip install ./dist/dataclaw-*.whl - -# Start the service (defaults to http://127.0.0.1:8000) -dataclaw start -``` - -Common service control commands: - -```bash -# Check running status -dataclaw status - -# Custom host/port -dataclaw start --host 0.0.0.0 --port 8000 - -# Stop the service -dataclaw stop -``` - -Optional environment variable: - -```bash -export DATA_ROOT=/absolute/path/to/data -``` - -If not set, DataClaw uses the repository-level `data/` directory by default. Service state files and logs are located in `DATA_ROOT/run/`. - -### 3. Development Mode (Requires Node.js) 🧪 - -If you want to debug source code, use development mode (runs source directly, no wheel required): - -```bash -cd backend -# Sync backend dependencies (environment is prepared under backend/.venv) -uv sync - -# Start the FastAPI server -uv run uvicorn main:app --reload --port 8000 -``` - -```bash -cd frontend -# Install dependencies -npm install - -# Start the Vite development server -npm run dev -``` - -*Note: Ensure your* *`nanobot`* *is properly linked or installed in editable mode as per the project workspace.* - -### 4. Optional Voice Service 🎙️ - -If you want to use voice input in chat, run the standalone `whisper` service: - -```bash -cd whisper -uv venv -uv pip install -r requirements.txt -uv run python main.py -``` - -Default service URL: `http://localhost:8001` -Health endpoint: `GET /health` - -Frontend setup: -1. Click the username in the bottom-left to open the user menu; -2. Open `Voice Input Settings`; -3. Fill in the service URL (e.g. `http://localhost:8001`); -4. Click `Test Connection`, then `Save`. - -### 5. Initial Account Setup 👤 -The first user to register in the system will automatically be granted admin privileges. You can simply click the "Register" button on the login page to create your admin account (e.g., Username: `admin`, Password: `admin`), and then log in to manage projects, data sources, and users. - -### 6. A2A Mode Guide 🤖 - -A2A (Agent2Agent) lets DataClaw delegate tasks to remote agents with full task lifecycle controls (status stream, artifact stream, cancel, retry). - -#### 6.1 Enable A2A in UI (Recommended) - -1. Open **Skills** page and switch to the **A2A** tab. -2. Add a remote agent with: - - `name` - - `base_url` (for example `https://agent-b.example.com`) - - `auth_scheme` (`none` or `bearer`) - - `auth_token` (required when `auth_scheme=bearer`) -3. Run health check and confirm `healthy=true`. -4. Go to Chat, enable **A2A Mode**, choose `route_mode` and remote agent, then send your prompt. -5. Track task states in Chat (`SUBMITTED/WORKING/COMPLETED/FAILED`) and use cancel/retry when needed. - -`route_mode` quick reference: -- `auto`: Use project rollout policy and routing strategy -- `local`: Force local execution -- `a2a`: Force remote A2A execution -- `a2a_first`: Try remote first, then fallback chain -- `local_first`: Try local first - -#### 6.2 API Examples - -Assume service URL is `http://127.0.0.1:8000` and your bearer token is `${TOKEN}`. - -```bash -# 1) Get local Agent Card -curl -H "Authorization: Bearer ${TOKEN}" \ - http://127.0.0.1:8000/api/v1/a2a/agent-card - -# 2) Register remote agent -curl -X POST http://127.0.0.1:8000/api/v1/a2a/remote-agents \ - -H "Authorization: Bearer ${TOKEN}" \ - -H "Content-Type: application/json" \ - -d '{ - "project_id": 1, - "name": "Agent-B", - "base_url": "https://agent-b.example.com", - "auth_scheme": "bearer", - "auth_token": "remote-agent-token" - }' - -# 3) Send task with a2a_first route -curl -X POST http://127.0.0.1:8000/api/v1/a2a/messages/send \ - -H "Authorization: Bearer ${TOKEN}" \ - -H "Content-Type: application/json" \ - -d '{ - "project_id": 1, - "message": "Analyze order conversion trend for last 30 days and propose actions", - "session_id": "chat:demo-a2a", - "remote_agent_id": 3, - "route_mode": "a2a_first", - "fallback_chain": ["a2a", "local", "mcp"], - "idempotency_key": "demo-a2a-001" - }' - -# 4) Subscribe task stream -curl -N -H "Authorization: Bearer ${TOKEN}" \ - http://127.0.0.1:8000/api/v1/a2a/tasks//subscribe - -# 5) Cancel task -curl -X POST -H "Authorization: Bearer ${TOKEN}" \ - http://127.0.0.1:8000/api/v1/a2a/tasks//cancel -``` - -#### 6.3 Local Debugging for A2A (Two-Instance Setup) - -Use two local backend instances: -- Instance A (caller): `http://127.0.0.1:8000` -- Instance B (remote agent): `http://127.0.0.1:8001` - -Run them in two terminals: - -```bash -# Terminal 1 - Instance A -cd backend -DATA_ROOT=/tmp/dataclaw-a uv run uvicorn main:app --reload --port 8000 -``` - -```bash -# Terminal 2 - Instance B -cd backend -DATA_ROOT=/tmp/dataclaw-b uv run uvicorn main:app --reload --port 8001 -``` - -Create/login users and fetch tokens: - -```bash -# Register (first user becomes admin) - run once per instance -curl -X POST http://127.0.0.1:8000/api/v1/auth/register \ - -H "Content-Type: application/json" \ - -d '{"username":"admin_a","email":"a@test.com","password":"admin12345"}' - -curl -X POST http://127.0.0.1:8001/api/v1/auth/register \ - -H "Content-Type: application/json" \ - -d '{"username":"admin_b","email":"b@test.com","password":"admin12345"}' - -# Login and keep tokens -TOKEN_A=$(curl -s -X POST http://127.0.0.1:8000/api/v1/auth/login \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "username=admin_a&password=admin12345" | jq -r '.access_token') - -TOKEN_B=$(curl -s -X POST http://127.0.0.1:8001/api/v1/auth/login \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "username=admin_b&password=admin12345" | jq -r '.access_token') -``` - -Then register B as remote agent in A, using `TOKEN_B` as `auth_token`: - -```bash -curl -X POST http://127.0.0.1:8000/api/v1/a2a/remote-agents \ - -H "Authorization: Bearer ${TOKEN_A}" \ - -H "Content-Type: application/json" \ - -d "{ - \"project_id\": 1, - \"name\": \"local-agent-b\", - \"base_url\": \"http://127.0.0.1:8001\", - \"auth_scheme\": \"bearer\", - \"auth_token\": \"${TOKEN_B}\" - }" -``` - -Finally, send/subscribe/cancel tasks from A. This validates the complete local A2A flow. - -*** - -## 🔌 Data Source Configuration Guide - -DataClaw supports connecting to various types of data sources to meet different analysis needs. You can click **+** in the **Data Sources** menu to create and configure them. Here are detailed connection guides for common data sources: - -
-▶ PostgreSQL (pgsql) - -Connects to standard relational databases. You can either fill in the individual parameters through the form or paste a complete Connection String directly. - -- **Host**: The host address of the database. If you are running the database on your local machine (e.g., using pgAdmin), please enter `127.0.0.1` (do not enter `localhost` to avoid Unix Socket resolution errors). -- **Port**: Typically defaults to `5432`. -- **Database**: The specific name of the database you want to connect to. -- **Username / Password**: Database authentication credentials (the default user is usually `postgres`). -- **Connection String (Optional)**: You can also directly input a string like `postgresql://postgres:your_password@127.0.0.1:5432/your_database_name`, which will override the individual input fields above. -
- -
-▶ Supabase - -A connection method specifically optimized for Supabase cloud PostgreSQL databases, enforcing SSL and using connection pools by default to improve stability. - -- We recommend using the **Connection String** configuration directly: - Go to your Supabase project console -> `Project Settings` -> `Database` -> `Connection string` -> Select the `URI` tab. - Copy the link that looks like `postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres?sslmode=require` and paste it in. -- *Note*: Supabase enables Transaction Pooler by default (Port 6543). If you want a Direct connection, change the port to `5432` and ensure the URL includes `sslmode=require`. -
- -
-▶ SQLite - -A lightweight local file-based database, perfect for quick testing or analyzing single-machine application data. - -- **File Upload**: You can directly click the button to upload a `.db`, `.sqlite`, or `.sqlite3` database file from your local machine. The file will be securely saved in the server's upload directory for analysis. -- **File Path (Advanced)**: If the service is deployed on a server and the SQLite file already exists at an absolute path on the server, you can also enter the absolute path directly in the input box (e.g., `/data/my_app.db`). -
- -
-▶ CSV - -The most common data exchange format, plug-and-play, no complex database configuration required. - -- **File Upload**: Similar to SQLite, click the button to select and upload a local `.csv` file. The system will use engines like DuckDB or Pandas in the background to virtualize it into an SQL-queryable table. -- Once uploaded successfully, you can query this CSV file directly as if it were a database table in the chat interface! -
- -*** - -## 🤝 Contributing - -Got a cool idea? Found a bug? We'd love your help! Feel free to open an issue or submit a pull request. Let's make data analysis fun again! - -*** - -## 💖 Acknowledgements - -The development of DataClaw was deeply inspired by the following excellent open-source projects. Special thanks to: - -- [WrenAI](https://github.com/Canner/WrenAI): A powerful Text-to-SQL solution whose architecture and concepts provided great inspiration. -- [Aix-DB](https://github.com/apconw/Aix-DB): Provided an excellent reference for intelligent data analysis and interactive user experience. - -
diff --git a/nanobot/.dockerignore b/agent-core/.dockerignore similarity index 100% rename from nanobot/.dockerignore rename to agent-core/.dockerignore diff --git a/nanobot/.github/workflows/ci.yml b/agent-core/.github/workflows/ci.yml similarity index 100% rename from nanobot/.github/workflows/ci.yml rename to agent-core/.github/workflows/ci.yml diff --git a/nanobot/.gitignore b/agent-core/.gitignore similarity index 100% rename from nanobot/.gitignore rename to agent-core/.gitignore diff --git a/nanobot/COMMUNICATION.md b/agent-core/COMMUNICATION.md similarity index 100% rename from nanobot/COMMUNICATION.md rename to agent-core/COMMUNICATION.md diff --git a/nanobot/CONTRIBUTING.md b/agent-core/CONTRIBUTING.md similarity index 100% rename from nanobot/CONTRIBUTING.md rename to agent-core/CONTRIBUTING.md diff --git a/nanobot/Dockerfile b/agent-core/Dockerfile similarity index 100% rename from nanobot/Dockerfile rename to agent-core/Dockerfile diff --git a/nanobot/LICENSE b/agent-core/LICENSE similarity index 100% rename from nanobot/LICENSE rename to agent-core/LICENSE diff --git a/nanobot/README.md b/agent-core/README.md similarity index 100% rename from nanobot/README.md rename to agent-core/README.md diff --git a/nanobot/SECURITY.md b/agent-core/SECURITY.md similarity index 100% rename from nanobot/SECURITY.md rename to agent-core/SECURITY.md diff --git a/nanobot/bridge/package.json b/agent-core/bridge/package.json similarity index 100% rename from nanobot/bridge/package.json rename to agent-core/bridge/package.json diff --git a/nanobot/bridge/src/index.ts b/agent-core/bridge/src/index.ts similarity index 100% rename from nanobot/bridge/src/index.ts rename to agent-core/bridge/src/index.ts diff --git a/nanobot/bridge/src/server.ts b/agent-core/bridge/src/server.ts similarity index 100% rename from nanobot/bridge/src/server.ts rename to agent-core/bridge/src/server.ts diff --git a/nanobot/bridge/src/types.d.ts b/agent-core/bridge/src/types.d.ts similarity index 100% rename from nanobot/bridge/src/types.d.ts rename to agent-core/bridge/src/types.d.ts diff --git a/nanobot/bridge/src/whatsapp.ts b/agent-core/bridge/src/whatsapp.ts similarity index 100% rename from nanobot/bridge/src/whatsapp.ts rename to agent-core/bridge/src/whatsapp.ts diff --git a/nanobot/bridge/tsconfig.json b/agent-core/bridge/tsconfig.json similarity index 100% rename from nanobot/bridge/tsconfig.json rename to agent-core/bridge/tsconfig.json diff --git a/nanobot/case/code.gif b/agent-core/case/code.gif similarity index 100% rename from nanobot/case/code.gif rename to agent-core/case/code.gif diff --git a/nanobot/case/memory.gif b/agent-core/case/memory.gif similarity index 100% rename from nanobot/case/memory.gif rename to agent-core/case/memory.gif diff --git a/nanobot/case/scedule.gif b/agent-core/case/scedule.gif similarity index 100% rename from nanobot/case/scedule.gif rename to agent-core/case/scedule.gif diff --git a/nanobot/case/search.gif b/agent-core/case/search.gif similarity index 100% rename from nanobot/case/search.gif rename to agent-core/case/search.gif diff --git a/nanobot/core_agent_lines.sh b/agent-core/core_agent_lines.sh old mode 100755 new mode 100644 similarity index 100% rename from nanobot/core_agent_lines.sh rename to agent-core/core_agent_lines.sh diff --git a/nanobot/docker-compose.yml b/agent-core/docker-compose.yml similarity index 100% rename from nanobot/docker-compose.yml rename to agent-core/docker-compose.yml diff --git a/nanobot/docs/CHANNEL_PLUGIN_GUIDE.md b/agent-core/docs/CHANNEL_PLUGIN_GUIDE.md similarity index 100% rename from nanobot/docs/CHANNEL_PLUGIN_GUIDE.md rename to agent-core/docs/CHANNEL_PLUGIN_GUIDE.md diff --git a/nanobot/nanobot/__init__.py b/agent-core/nanobot/__init__.py similarity index 100% rename from nanobot/nanobot/__init__.py rename to agent-core/nanobot/__init__.py diff --git a/nanobot/nanobot/__main__.py b/agent-core/nanobot/__main__.py similarity index 100% rename from nanobot/nanobot/__main__.py rename to agent-core/nanobot/__main__.py diff --git a/nanobot/nanobot/agent/__init__.py b/agent-core/nanobot/agent/__init__.py similarity index 100% rename from nanobot/nanobot/agent/__init__.py rename to agent-core/nanobot/agent/__init__.py diff --git a/nanobot/nanobot/agent/context.py b/agent-core/nanobot/agent/context.py similarity index 100% rename from nanobot/nanobot/agent/context.py rename to agent-core/nanobot/agent/context.py diff --git a/nanobot/nanobot/agent/hook.py b/agent-core/nanobot/agent/hook.py similarity index 100% rename from nanobot/nanobot/agent/hook.py rename to agent-core/nanobot/agent/hook.py diff --git a/nanobot/nanobot/agent/loop.py b/agent-core/nanobot/agent/loop.py similarity index 100% rename from nanobot/nanobot/agent/loop.py rename to agent-core/nanobot/agent/loop.py diff --git a/nanobot/nanobot/agent/memory.py b/agent-core/nanobot/agent/memory.py similarity index 100% rename from nanobot/nanobot/agent/memory.py rename to agent-core/nanobot/agent/memory.py diff --git a/nanobot/nanobot/agent/runner.py b/agent-core/nanobot/agent/runner.py similarity index 100% rename from nanobot/nanobot/agent/runner.py rename to agent-core/nanobot/agent/runner.py diff --git a/nanobot/nanobot/agent/skills.py b/agent-core/nanobot/agent/skills.py similarity index 100% rename from nanobot/nanobot/agent/skills.py rename to agent-core/nanobot/agent/skills.py diff --git a/nanobot/nanobot/agent/subagent.py b/agent-core/nanobot/agent/subagent.py similarity index 100% rename from nanobot/nanobot/agent/subagent.py rename to agent-core/nanobot/agent/subagent.py diff --git a/nanobot/nanobot/agent/tools/__init__.py b/agent-core/nanobot/agent/tools/__init__.py similarity index 100% rename from nanobot/nanobot/agent/tools/__init__.py rename to agent-core/nanobot/agent/tools/__init__.py diff --git a/nanobot/nanobot/agent/tools/base.py b/agent-core/nanobot/agent/tools/base.py similarity index 100% rename from nanobot/nanobot/agent/tools/base.py rename to agent-core/nanobot/agent/tools/base.py diff --git a/nanobot/nanobot/agent/tools/cron.py b/agent-core/nanobot/agent/tools/cron.py similarity index 100% rename from nanobot/nanobot/agent/tools/cron.py rename to agent-core/nanobot/agent/tools/cron.py diff --git a/nanobot/nanobot/agent/tools/filesystem.py b/agent-core/nanobot/agent/tools/filesystem.py similarity index 100% rename from nanobot/nanobot/agent/tools/filesystem.py rename to agent-core/nanobot/agent/tools/filesystem.py diff --git a/nanobot/nanobot/agent/tools/mcp.py b/agent-core/nanobot/agent/tools/mcp.py similarity index 100% rename from nanobot/nanobot/agent/tools/mcp.py rename to agent-core/nanobot/agent/tools/mcp.py diff --git a/nanobot/nanobot/agent/tools/message.py b/agent-core/nanobot/agent/tools/message.py similarity index 100% rename from nanobot/nanobot/agent/tools/message.py rename to agent-core/nanobot/agent/tools/message.py diff --git a/nanobot/nanobot/agent/tools/registry.py b/agent-core/nanobot/agent/tools/registry.py similarity index 100% rename from nanobot/nanobot/agent/tools/registry.py rename to agent-core/nanobot/agent/tools/registry.py diff --git a/nanobot/nanobot/agent/tools/shell.py b/agent-core/nanobot/agent/tools/shell.py similarity index 100% rename from nanobot/nanobot/agent/tools/shell.py rename to agent-core/nanobot/agent/tools/shell.py diff --git a/nanobot/nanobot/agent/tools/spawn.py b/agent-core/nanobot/agent/tools/spawn.py similarity index 100% rename from nanobot/nanobot/agent/tools/spawn.py rename to agent-core/nanobot/agent/tools/spawn.py diff --git a/nanobot/nanobot/agent/tools/web.py b/agent-core/nanobot/agent/tools/web.py similarity index 100% rename from nanobot/nanobot/agent/tools/web.py rename to agent-core/nanobot/agent/tools/web.py diff --git a/nanobot/nanobot/bus/__init__.py b/agent-core/nanobot/bus/__init__.py similarity index 100% rename from nanobot/nanobot/bus/__init__.py rename to agent-core/nanobot/bus/__init__.py diff --git a/nanobot/nanobot/bus/events.py b/agent-core/nanobot/bus/events.py similarity index 100% rename from nanobot/nanobot/bus/events.py rename to agent-core/nanobot/bus/events.py diff --git a/nanobot/nanobot/bus/queue.py b/agent-core/nanobot/bus/queue.py similarity index 100% rename from nanobot/nanobot/bus/queue.py rename to agent-core/nanobot/bus/queue.py diff --git a/nanobot/nanobot/channels/__init__.py b/agent-core/nanobot/channels/__init__.py similarity index 100% rename from nanobot/nanobot/channels/__init__.py rename to agent-core/nanobot/channels/__init__.py diff --git a/nanobot/nanobot/channels/base.py b/agent-core/nanobot/channels/base.py similarity index 100% rename from nanobot/nanobot/channels/base.py rename to agent-core/nanobot/channels/base.py diff --git a/nanobot/nanobot/channels/dingtalk.py b/agent-core/nanobot/channels/dingtalk.py similarity index 100% rename from nanobot/nanobot/channels/dingtalk.py rename to agent-core/nanobot/channels/dingtalk.py diff --git a/nanobot/nanobot/channels/discord.py b/agent-core/nanobot/channels/discord.py similarity index 100% rename from nanobot/nanobot/channels/discord.py rename to agent-core/nanobot/channels/discord.py diff --git a/nanobot/nanobot/channels/email.py b/agent-core/nanobot/channels/email.py similarity index 100% rename from nanobot/nanobot/channels/email.py rename to agent-core/nanobot/channels/email.py diff --git a/nanobot/nanobot/channels/feishu.py b/agent-core/nanobot/channels/feishu.py similarity index 100% rename from nanobot/nanobot/channels/feishu.py rename to agent-core/nanobot/channels/feishu.py diff --git a/nanobot/nanobot/channels/manager.py b/agent-core/nanobot/channels/manager.py similarity index 100% rename from nanobot/nanobot/channels/manager.py rename to agent-core/nanobot/channels/manager.py diff --git a/nanobot/nanobot/channels/matrix.py b/agent-core/nanobot/channels/matrix.py similarity index 100% rename from nanobot/nanobot/channels/matrix.py rename to agent-core/nanobot/channels/matrix.py diff --git a/nanobot/nanobot/channels/mochat.py b/agent-core/nanobot/channels/mochat.py similarity index 100% rename from nanobot/nanobot/channels/mochat.py rename to agent-core/nanobot/channels/mochat.py diff --git a/nanobot/nanobot/channels/qq.py b/agent-core/nanobot/channels/qq.py similarity index 100% rename from nanobot/nanobot/channels/qq.py rename to agent-core/nanobot/channels/qq.py diff --git a/nanobot/nanobot/channels/registry.py b/agent-core/nanobot/channels/registry.py similarity index 100% rename from nanobot/nanobot/channels/registry.py rename to agent-core/nanobot/channels/registry.py diff --git a/nanobot/nanobot/channels/slack.py b/agent-core/nanobot/channels/slack.py similarity index 100% rename from nanobot/nanobot/channels/slack.py rename to agent-core/nanobot/channels/slack.py diff --git a/nanobot/nanobot/channels/telegram.py b/agent-core/nanobot/channels/telegram.py similarity index 100% rename from nanobot/nanobot/channels/telegram.py rename to agent-core/nanobot/channels/telegram.py diff --git a/nanobot/nanobot/channels/wecom.py b/agent-core/nanobot/channels/wecom.py similarity index 100% rename from nanobot/nanobot/channels/wecom.py rename to agent-core/nanobot/channels/wecom.py diff --git a/nanobot/nanobot/channels/weixin.py b/agent-core/nanobot/channels/weixin.py similarity index 100% rename from nanobot/nanobot/channels/weixin.py rename to agent-core/nanobot/channels/weixin.py diff --git a/nanobot/nanobot/channels/whatsapp.py b/agent-core/nanobot/channels/whatsapp.py similarity index 100% rename from nanobot/nanobot/channels/whatsapp.py rename to agent-core/nanobot/channels/whatsapp.py diff --git a/nanobot/nanobot/cli/__init__.py b/agent-core/nanobot/cli/__init__.py similarity index 100% rename from nanobot/nanobot/cli/__init__.py rename to agent-core/nanobot/cli/__init__.py diff --git a/nanobot/nanobot/cli/commands.py b/agent-core/nanobot/cli/commands.py similarity index 100% rename from nanobot/nanobot/cli/commands.py rename to agent-core/nanobot/cli/commands.py diff --git a/nanobot/nanobot/cli/models.py b/agent-core/nanobot/cli/models.py similarity index 100% rename from nanobot/nanobot/cli/models.py rename to agent-core/nanobot/cli/models.py diff --git a/nanobot/nanobot/cli/onboard.py b/agent-core/nanobot/cli/onboard.py similarity index 100% rename from nanobot/nanobot/cli/onboard.py rename to agent-core/nanobot/cli/onboard.py diff --git a/nanobot/nanobot/cli/stream.py b/agent-core/nanobot/cli/stream.py similarity index 100% rename from nanobot/nanobot/cli/stream.py rename to agent-core/nanobot/cli/stream.py diff --git a/nanobot/nanobot/command/__init__.py b/agent-core/nanobot/command/__init__.py similarity index 100% rename from nanobot/nanobot/command/__init__.py rename to agent-core/nanobot/command/__init__.py diff --git a/nanobot/nanobot/command/builtin.py b/agent-core/nanobot/command/builtin.py similarity index 100% rename from nanobot/nanobot/command/builtin.py rename to agent-core/nanobot/command/builtin.py diff --git a/nanobot/nanobot/command/router.py b/agent-core/nanobot/command/router.py similarity index 100% rename from nanobot/nanobot/command/router.py rename to agent-core/nanobot/command/router.py diff --git a/nanobot/nanobot/config/__init__.py b/agent-core/nanobot/config/__init__.py similarity index 100% rename from nanobot/nanobot/config/__init__.py rename to agent-core/nanobot/config/__init__.py diff --git a/nanobot/nanobot/config/loader.py b/agent-core/nanobot/config/loader.py similarity index 100% rename from nanobot/nanobot/config/loader.py rename to agent-core/nanobot/config/loader.py diff --git a/nanobot/nanobot/config/paths.py b/agent-core/nanobot/config/paths.py similarity index 100% rename from nanobot/nanobot/config/paths.py rename to agent-core/nanobot/config/paths.py diff --git a/nanobot/nanobot/config/schema.py b/agent-core/nanobot/config/schema.py similarity index 100% rename from nanobot/nanobot/config/schema.py rename to agent-core/nanobot/config/schema.py diff --git a/nanobot/nanobot/cron/__init__.py b/agent-core/nanobot/cron/__init__.py similarity index 100% rename from nanobot/nanobot/cron/__init__.py rename to agent-core/nanobot/cron/__init__.py diff --git a/nanobot/nanobot/cron/service.py b/agent-core/nanobot/cron/service.py similarity index 100% rename from nanobot/nanobot/cron/service.py rename to agent-core/nanobot/cron/service.py diff --git a/nanobot/nanobot/cron/types.py b/agent-core/nanobot/cron/types.py similarity index 100% rename from nanobot/nanobot/cron/types.py rename to agent-core/nanobot/cron/types.py diff --git a/nanobot/nanobot/heartbeat/__init__.py b/agent-core/nanobot/heartbeat/__init__.py similarity index 100% rename from nanobot/nanobot/heartbeat/__init__.py rename to agent-core/nanobot/heartbeat/__init__.py diff --git a/nanobot/nanobot/heartbeat/service.py b/agent-core/nanobot/heartbeat/service.py similarity index 100% rename from nanobot/nanobot/heartbeat/service.py rename to agent-core/nanobot/heartbeat/service.py diff --git a/nanobot/nanobot/providers/__init__.py b/agent-core/nanobot/providers/__init__.py similarity index 100% rename from nanobot/nanobot/providers/__init__.py rename to agent-core/nanobot/providers/__init__.py diff --git a/nanobot/nanobot/providers/anthropic_provider.py b/agent-core/nanobot/providers/anthropic_provider.py similarity index 100% rename from nanobot/nanobot/providers/anthropic_provider.py rename to agent-core/nanobot/providers/anthropic_provider.py diff --git a/nanobot/nanobot/providers/azure_openai_provider.py b/agent-core/nanobot/providers/azure_openai_provider.py similarity index 100% rename from nanobot/nanobot/providers/azure_openai_provider.py rename to agent-core/nanobot/providers/azure_openai_provider.py diff --git a/nanobot/nanobot/providers/base.py b/agent-core/nanobot/providers/base.py similarity index 100% rename from nanobot/nanobot/providers/base.py rename to agent-core/nanobot/providers/base.py diff --git a/nanobot/nanobot/providers/openai_codex_provider.py b/agent-core/nanobot/providers/openai_codex_provider.py similarity index 100% rename from nanobot/nanobot/providers/openai_codex_provider.py rename to agent-core/nanobot/providers/openai_codex_provider.py diff --git a/nanobot/nanobot/providers/openai_compat_provider.py b/agent-core/nanobot/providers/openai_compat_provider.py similarity index 100% rename from nanobot/nanobot/providers/openai_compat_provider.py rename to agent-core/nanobot/providers/openai_compat_provider.py diff --git a/nanobot/nanobot/providers/registry.py b/agent-core/nanobot/providers/registry.py similarity index 100% rename from nanobot/nanobot/providers/registry.py rename to agent-core/nanobot/providers/registry.py diff --git a/nanobot/nanobot/providers/transcription.py b/agent-core/nanobot/providers/transcription.py similarity index 100% rename from nanobot/nanobot/providers/transcription.py rename to agent-core/nanobot/providers/transcription.py diff --git a/nanobot/nanobot/security/__init__.py b/agent-core/nanobot/security/__init__.py similarity index 100% rename from nanobot/nanobot/security/__init__.py rename to agent-core/nanobot/security/__init__.py diff --git a/nanobot/nanobot/security/network.py b/agent-core/nanobot/security/network.py similarity index 100% rename from nanobot/nanobot/security/network.py rename to agent-core/nanobot/security/network.py diff --git a/nanobot/nanobot/session/__init__.py b/agent-core/nanobot/session/__init__.py similarity index 100% rename from nanobot/nanobot/session/__init__.py rename to agent-core/nanobot/session/__init__.py diff --git a/nanobot/nanobot/session/manager.py b/agent-core/nanobot/session/manager.py similarity index 100% rename from nanobot/nanobot/session/manager.py rename to agent-core/nanobot/session/manager.py diff --git a/nanobot/nanobot/skills/README.md b/agent-core/nanobot/skills/README.md similarity index 100% rename from nanobot/nanobot/skills/README.md rename to agent-core/nanobot/skills/README.md diff --git a/nanobot/nanobot/skills/clawhub/SKILL.md b/agent-core/nanobot/skills/clawhub/SKILL.md similarity index 100% rename from nanobot/nanobot/skills/clawhub/SKILL.md rename to agent-core/nanobot/skills/clawhub/SKILL.md diff --git a/nanobot/nanobot/skills/cron/SKILL.md b/agent-core/nanobot/skills/cron/SKILL.md similarity index 100% rename from nanobot/nanobot/skills/cron/SKILL.md rename to agent-core/nanobot/skills/cron/SKILL.md diff --git a/nanobot/nanobot/skills/github/SKILL.md b/agent-core/nanobot/skills/github/SKILL.md similarity index 100% rename from nanobot/nanobot/skills/github/SKILL.md rename to agent-core/nanobot/skills/github/SKILL.md diff --git a/nanobot/nanobot/skills/memory/SKILL.md b/agent-core/nanobot/skills/memory/SKILL.md similarity index 100% rename from nanobot/nanobot/skills/memory/SKILL.md rename to agent-core/nanobot/skills/memory/SKILL.md diff --git a/nanobot/nanobot/skills/skill-creator/SKILL.md b/agent-core/nanobot/skills/skill-creator/SKILL.md similarity index 100% rename from nanobot/nanobot/skills/skill-creator/SKILL.md rename to agent-core/nanobot/skills/skill-creator/SKILL.md diff --git a/nanobot/nanobot/skills/skill-creator/scripts/init_skill.py b/agent-core/nanobot/skills/skill-creator/scripts/init_skill.py old mode 100755 new mode 100644 similarity index 100% rename from nanobot/nanobot/skills/skill-creator/scripts/init_skill.py rename to agent-core/nanobot/skills/skill-creator/scripts/init_skill.py diff --git a/nanobot/nanobot/skills/skill-creator/scripts/package_skill.py b/agent-core/nanobot/skills/skill-creator/scripts/package_skill.py old mode 100755 new mode 100644 similarity index 100% rename from nanobot/nanobot/skills/skill-creator/scripts/package_skill.py rename to agent-core/nanobot/skills/skill-creator/scripts/package_skill.py diff --git a/nanobot/nanobot/skills/skill-creator/scripts/quick_validate.py b/agent-core/nanobot/skills/skill-creator/scripts/quick_validate.py similarity index 100% rename from nanobot/nanobot/skills/skill-creator/scripts/quick_validate.py rename to agent-core/nanobot/skills/skill-creator/scripts/quick_validate.py diff --git a/nanobot/nanobot/skills/summarize/SKILL.md b/agent-core/nanobot/skills/summarize/SKILL.md similarity index 100% rename from nanobot/nanobot/skills/summarize/SKILL.md rename to agent-core/nanobot/skills/summarize/SKILL.md diff --git a/nanobot/nanobot/skills/tmux/SKILL.md b/agent-core/nanobot/skills/tmux/SKILL.md similarity index 100% rename from nanobot/nanobot/skills/tmux/SKILL.md rename to agent-core/nanobot/skills/tmux/SKILL.md diff --git a/nanobot/nanobot/skills/tmux/scripts/find-sessions.sh b/agent-core/nanobot/skills/tmux/scripts/find-sessions.sh old mode 100755 new mode 100644 similarity index 100% rename from nanobot/nanobot/skills/tmux/scripts/find-sessions.sh rename to agent-core/nanobot/skills/tmux/scripts/find-sessions.sh diff --git a/nanobot/nanobot/skills/tmux/scripts/wait-for-text.sh b/agent-core/nanobot/skills/tmux/scripts/wait-for-text.sh old mode 100755 new mode 100644 similarity index 100% rename from nanobot/nanobot/skills/tmux/scripts/wait-for-text.sh rename to agent-core/nanobot/skills/tmux/scripts/wait-for-text.sh diff --git a/nanobot/nanobot/skills/weather/SKILL.md b/agent-core/nanobot/skills/weather/SKILL.md similarity index 100% rename from nanobot/nanobot/skills/weather/SKILL.md rename to agent-core/nanobot/skills/weather/SKILL.md diff --git a/nanobot/nanobot/templates/AGENTS.md b/agent-core/nanobot/templates/AGENTS.md similarity index 100% rename from nanobot/nanobot/templates/AGENTS.md rename to agent-core/nanobot/templates/AGENTS.md diff --git a/nanobot/nanobot/templates/HEARTBEAT.md b/agent-core/nanobot/templates/HEARTBEAT.md similarity index 100% rename from nanobot/nanobot/templates/HEARTBEAT.md rename to agent-core/nanobot/templates/HEARTBEAT.md diff --git a/nanobot/nanobot/templates/SOUL.md b/agent-core/nanobot/templates/SOUL.md similarity index 100% rename from nanobot/nanobot/templates/SOUL.md rename to agent-core/nanobot/templates/SOUL.md diff --git a/nanobot/nanobot/templates/TOOLS.md b/agent-core/nanobot/templates/TOOLS.md similarity index 100% rename from nanobot/nanobot/templates/TOOLS.md rename to agent-core/nanobot/templates/TOOLS.md diff --git a/nanobot/nanobot/templates/USER.md b/agent-core/nanobot/templates/USER.md similarity index 100% rename from nanobot/nanobot/templates/USER.md rename to agent-core/nanobot/templates/USER.md diff --git a/backend/app/__init__.py b/agent-core/nanobot/templates/__init__.py similarity index 100% rename from backend/app/__init__.py rename to agent-core/nanobot/templates/__init__.py diff --git a/nanobot/nanobot/templates/memory/MEMORY.md b/agent-core/nanobot/templates/memory/MEMORY.md similarity index 100% rename from nanobot/nanobot/templates/memory/MEMORY.md rename to agent-core/nanobot/templates/memory/MEMORY.md diff --git a/backend/app/agent/__init__.py b/agent-core/nanobot/templates/memory/__init__.py similarity index 100% rename from backend/app/agent/__init__.py rename to agent-core/nanobot/templates/memory/__init__.py diff --git a/nanobot/nanobot/utils/__init__.py b/agent-core/nanobot/utils/__init__.py similarity index 100% rename from nanobot/nanobot/utils/__init__.py rename to agent-core/nanobot/utils/__init__.py diff --git a/nanobot/nanobot/utils/evaluator.py b/agent-core/nanobot/utils/evaluator.py similarity index 100% rename from nanobot/nanobot/utils/evaluator.py rename to agent-core/nanobot/utils/evaluator.py diff --git a/nanobot/nanobot/utils/helpers.py b/agent-core/nanobot/utils/helpers.py similarity index 100% rename from nanobot/nanobot/utils/helpers.py rename to agent-core/nanobot/utils/helpers.py diff --git a/nanobot/nanobot_arch.png b/agent-core/nanobot_arch.png similarity index 100% rename from nanobot/nanobot_arch.png rename to agent-core/nanobot_arch.png diff --git a/nanobot/nanobot_logo.png b/agent-core/nanobot_logo.png similarity index 100% rename from nanobot/nanobot_logo.png rename to agent-core/nanobot_logo.png diff --git a/nanobot/pyproject.toml b/agent-core/pyproject.toml similarity index 100% rename from nanobot/pyproject.toml rename to agent-core/pyproject.toml diff --git a/nanobot/tests/agent/test_consolidate_offset.py b/agent-core/tests/agent/test_consolidate_offset.py similarity index 100% rename from nanobot/tests/agent/test_consolidate_offset.py rename to agent-core/tests/agent/test_consolidate_offset.py diff --git a/nanobot/tests/agent/test_context_prompt_cache.py b/agent-core/tests/agent/test_context_prompt_cache.py similarity index 100% rename from nanobot/tests/agent/test_context_prompt_cache.py rename to agent-core/tests/agent/test_context_prompt_cache.py diff --git a/nanobot/tests/agent/test_evaluator.py b/agent-core/tests/agent/test_evaluator.py similarity index 100% rename from nanobot/tests/agent/test_evaluator.py rename to agent-core/tests/agent/test_evaluator.py diff --git a/nanobot/tests/agent/test_gemini_thought_signature.py b/agent-core/tests/agent/test_gemini_thought_signature.py similarity index 100% rename from nanobot/tests/agent/test_gemini_thought_signature.py rename to agent-core/tests/agent/test_gemini_thought_signature.py diff --git a/nanobot/tests/agent/test_heartbeat_service.py b/agent-core/tests/agent/test_heartbeat_service.py similarity index 100% rename from nanobot/tests/agent/test_heartbeat_service.py rename to agent-core/tests/agent/test_heartbeat_service.py diff --git a/nanobot/tests/agent/test_loop_consolidation_tokens.py b/agent-core/tests/agent/test_loop_consolidation_tokens.py similarity index 100% rename from nanobot/tests/agent/test_loop_consolidation_tokens.py rename to agent-core/tests/agent/test_loop_consolidation_tokens.py diff --git a/nanobot/tests/agent/test_loop_cron_timezone.py b/agent-core/tests/agent/test_loop_cron_timezone.py similarity index 100% rename from nanobot/tests/agent/test_loop_cron_timezone.py rename to agent-core/tests/agent/test_loop_cron_timezone.py diff --git a/nanobot/tests/agent/test_loop_save_turn.py b/agent-core/tests/agent/test_loop_save_turn.py similarity index 100% rename from nanobot/tests/agent/test_loop_save_turn.py rename to agent-core/tests/agent/test_loop_save_turn.py diff --git a/nanobot/tests/agent/test_memory_consolidation_types.py b/agent-core/tests/agent/test_memory_consolidation_types.py similarity index 100% rename from nanobot/tests/agent/test_memory_consolidation_types.py rename to agent-core/tests/agent/test_memory_consolidation_types.py diff --git a/nanobot/tests/agent/test_onboard_logic.py b/agent-core/tests/agent/test_onboard_logic.py similarity index 100% rename from nanobot/tests/agent/test_onboard_logic.py rename to agent-core/tests/agent/test_onboard_logic.py diff --git a/nanobot/tests/agent/test_runner.py b/agent-core/tests/agent/test_runner.py similarity index 100% rename from nanobot/tests/agent/test_runner.py rename to agent-core/tests/agent/test_runner.py diff --git a/nanobot/tests/agent/test_session_manager_history.py b/agent-core/tests/agent/test_session_manager_history.py similarity index 100% rename from nanobot/tests/agent/test_session_manager_history.py rename to agent-core/tests/agent/test_session_manager_history.py diff --git a/nanobot/tests/agent/test_skill_creator_scripts.py b/agent-core/tests/agent/test_skill_creator_scripts.py similarity index 100% rename from nanobot/tests/agent/test_skill_creator_scripts.py rename to agent-core/tests/agent/test_skill_creator_scripts.py diff --git a/nanobot/tests/agent/test_task_cancel.py b/agent-core/tests/agent/test_task_cancel.py similarity index 100% rename from nanobot/tests/agent/test_task_cancel.py rename to agent-core/tests/agent/test_task_cancel.py diff --git a/nanobot/tests/channels/test_base_channel.py b/agent-core/tests/channels/test_base_channel.py similarity index 100% rename from nanobot/tests/channels/test_base_channel.py rename to agent-core/tests/channels/test_base_channel.py diff --git a/nanobot/tests/channels/test_channel_manager_delta_coalescing.py b/agent-core/tests/channels/test_channel_manager_delta_coalescing.py similarity index 100% rename from nanobot/tests/channels/test_channel_manager_delta_coalescing.py rename to agent-core/tests/channels/test_channel_manager_delta_coalescing.py diff --git a/nanobot/tests/channels/test_channel_plugins.py b/agent-core/tests/channels/test_channel_plugins.py similarity index 100% rename from nanobot/tests/channels/test_channel_plugins.py rename to agent-core/tests/channels/test_channel_plugins.py diff --git a/nanobot/tests/channels/test_dingtalk_channel.py b/agent-core/tests/channels/test_dingtalk_channel.py similarity index 100% rename from nanobot/tests/channels/test_dingtalk_channel.py rename to agent-core/tests/channels/test_dingtalk_channel.py diff --git a/nanobot/tests/channels/test_email_channel.py b/agent-core/tests/channels/test_email_channel.py similarity index 100% rename from nanobot/tests/channels/test_email_channel.py rename to agent-core/tests/channels/test_email_channel.py diff --git a/nanobot/tests/channels/test_feishu_markdown_rendering.py b/agent-core/tests/channels/test_feishu_markdown_rendering.py similarity index 100% rename from nanobot/tests/channels/test_feishu_markdown_rendering.py rename to agent-core/tests/channels/test_feishu_markdown_rendering.py diff --git a/nanobot/tests/channels/test_feishu_post_content.py b/agent-core/tests/channels/test_feishu_post_content.py similarity index 100% rename from nanobot/tests/channels/test_feishu_post_content.py rename to agent-core/tests/channels/test_feishu_post_content.py diff --git a/nanobot/tests/channels/test_feishu_reply.py b/agent-core/tests/channels/test_feishu_reply.py similarity index 100% rename from nanobot/tests/channels/test_feishu_reply.py rename to agent-core/tests/channels/test_feishu_reply.py diff --git a/nanobot/tests/channels/test_feishu_streaming.py b/agent-core/tests/channels/test_feishu_streaming.py similarity index 100% rename from nanobot/tests/channels/test_feishu_streaming.py rename to agent-core/tests/channels/test_feishu_streaming.py diff --git a/nanobot/tests/channels/test_feishu_table_split.py b/agent-core/tests/channels/test_feishu_table_split.py similarity index 100% rename from nanobot/tests/channels/test_feishu_table_split.py rename to agent-core/tests/channels/test_feishu_table_split.py diff --git a/nanobot/tests/channels/test_feishu_tool_hint_code_block.py b/agent-core/tests/channels/test_feishu_tool_hint_code_block.py similarity index 100% rename from nanobot/tests/channels/test_feishu_tool_hint_code_block.py rename to agent-core/tests/channels/test_feishu_tool_hint_code_block.py diff --git a/nanobot/tests/channels/test_matrix_channel.py b/agent-core/tests/channels/test_matrix_channel.py similarity index 100% rename from nanobot/tests/channels/test_matrix_channel.py rename to agent-core/tests/channels/test_matrix_channel.py diff --git a/nanobot/tests/channels/test_qq_channel.py b/agent-core/tests/channels/test_qq_channel.py similarity index 100% rename from nanobot/tests/channels/test_qq_channel.py rename to agent-core/tests/channels/test_qq_channel.py diff --git a/nanobot/tests/channels/test_slack_channel.py b/agent-core/tests/channels/test_slack_channel.py similarity index 100% rename from nanobot/tests/channels/test_slack_channel.py rename to agent-core/tests/channels/test_slack_channel.py diff --git a/nanobot/tests/channels/test_telegram_channel.py b/agent-core/tests/channels/test_telegram_channel.py similarity index 100% rename from nanobot/tests/channels/test_telegram_channel.py rename to agent-core/tests/channels/test_telegram_channel.py diff --git a/nanobot/tests/channels/test_weixin_channel.py b/agent-core/tests/channels/test_weixin_channel.py similarity index 100% rename from nanobot/tests/channels/test_weixin_channel.py rename to agent-core/tests/channels/test_weixin_channel.py diff --git a/nanobot/tests/channels/test_whatsapp_channel.py b/agent-core/tests/channels/test_whatsapp_channel.py similarity index 100% rename from nanobot/tests/channels/test_whatsapp_channel.py rename to agent-core/tests/channels/test_whatsapp_channel.py diff --git a/nanobot/tests/cli/test_cli_input.py b/agent-core/tests/cli/test_cli_input.py similarity index 100% rename from nanobot/tests/cli/test_cli_input.py rename to agent-core/tests/cli/test_cli_input.py diff --git a/nanobot/tests/cli/test_commands.py b/agent-core/tests/cli/test_commands.py similarity index 100% rename from nanobot/tests/cli/test_commands.py rename to agent-core/tests/cli/test_commands.py diff --git a/nanobot/tests/cli/test_restart_command.py b/agent-core/tests/cli/test_restart_command.py similarity index 100% rename from nanobot/tests/cli/test_restart_command.py rename to agent-core/tests/cli/test_restart_command.py diff --git a/nanobot/tests/config/test_config_migration.py b/agent-core/tests/config/test_config_migration.py similarity index 100% rename from nanobot/tests/config/test_config_migration.py rename to agent-core/tests/config/test_config_migration.py diff --git a/nanobot/tests/config/test_config_paths.py b/agent-core/tests/config/test_config_paths.py similarity index 100% rename from nanobot/tests/config/test_config_paths.py rename to agent-core/tests/config/test_config_paths.py diff --git a/nanobot/tests/cron/test_cron_service.py b/agent-core/tests/cron/test_cron_service.py similarity index 100% rename from nanobot/tests/cron/test_cron_service.py rename to agent-core/tests/cron/test_cron_service.py diff --git a/nanobot/tests/cron/test_cron_tool_list.py b/agent-core/tests/cron/test_cron_tool_list.py similarity index 100% rename from nanobot/tests/cron/test_cron_tool_list.py rename to agent-core/tests/cron/test_cron_tool_list.py diff --git a/nanobot/tests/providers/test_azure_openai_provider.py b/agent-core/tests/providers/test_azure_openai_provider.py similarity index 100% rename from nanobot/tests/providers/test_azure_openai_provider.py rename to agent-core/tests/providers/test_azure_openai_provider.py diff --git a/nanobot/tests/providers/test_custom_provider.py b/agent-core/tests/providers/test_custom_provider.py similarity index 100% rename from nanobot/tests/providers/test_custom_provider.py rename to agent-core/tests/providers/test_custom_provider.py diff --git a/nanobot/tests/providers/test_litellm_kwargs.py b/agent-core/tests/providers/test_litellm_kwargs.py similarity index 100% rename from nanobot/tests/providers/test_litellm_kwargs.py rename to agent-core/tests/providers/test_litellm_kwargs.py diff --git a/nanobot/tests/providers/test_mistral_provider.py b/agent-core/tests/providers/test_mistral_provider.py similarity index 100% rename from nanobot/tests/providers/test_mistral_provider.py rename to agent-core/tests/providers/test_mistral_provider.py diff --git a/nanobot/tests/providers/test_provider_retry.py b/agent-core/tests/providers/test_provider_retry.py similarity index 100% rename from nanobot/tests/providers/test_provider_retry.py rename to agent-core/tests/providers/test_provider_retry.py diff --git a/nanobot/tests/providers/test_providers_init.py b/agent-core/tests/providers/test_providers_init.py similarity index 100% rename from nanobot/tests/providers/test_providers_init.py rename to agent-core/tests/providers/test_providers_init.py diff --git a/nanobot/tests/security/test_security_network.py b/agent-core/tests/security/test_security_network.py similarity index 100% rename from nanobot/tests/security/test_security_network.py rename to agent-core/tests/security/test_security_network.py diff --git a/nanobot/tests/test_docker.sh b/agent-core/tests/test_docker.sh similarity index 100% rename from nanobot/tests/test_docker.sh rename to agent-core/tests/test_docker.sh diff --git a/nanobot/tests/tools/test_exec_security.py b/agent-core/tests/tools/test_exec_security.py similarity index 100% rename from nanobot/tests/tools/test_exec_security.py rename to agent-core/tests/tools/test_exec_security.py diff --git a/nanobot/tests/tools/test_filesystem_tools.py b/agent-core/tests/tools/test_filesystem_tools.py similarity index 100% rename from nanobot/tests/tools/test_filesystem_tools.py rename to agent-core/tests/tools/test_filesystem_tools.py diff --git a/nanobot/tests/tools/test_mcp_tool.py b/agent-core/tests/tools/test_mcp_tool.py similarity index 100% rename from nanobot/tests/tools/test_mcp_tool.py rename to agent-core/tests/tools/test_mcp_tool.py diff --git a/nanobot/tests/tools/test_message_tool.py b/agent-core/tests/tools/test_message_tool.py similarity index 100% rename from nanobot/tests/tools/test_message_tool.py rename to agent-core/tests/tools/test_message_tool.py diff --git a/nanobot/tests/tools/test_message_tool_suppress.py b/agent-core/tests/tools/test_message_tool_suppress.py similarity index 100% rename from nanobot/tests/tools/test_message_tool_suppress.py rename to agent-core/tests/tools/test_message_tool_suppress.py diff --git a/nanobot/tests/tools/test_tool_validation.py b/agent-core/tests/tools/test_tool_validation.py similarity index 100% rename from nanobot/tests/tools/test_tool_validation.py rename to agent-core/tests/tools/test_tool_validation.py diff --git a/nanobot/tests/tools/test_web_fetch_security.py b/agent-core/tests/tools/test_web_fetch_security.py similarity index 100% rename from nanobot/tests/tools/test_web_fetch_security.py rename to agent-core/tests/tools/test_web_fetch_security.py diff --git a/nanobot/tests/tools/test_web_search_tool.py b/agent-core/tests/tools/test_web_search_tool.py similarity index 100% rename from nanobot/tests/tools/test_web_search_tool.py rename to agent-core/tests/tools/test_web_search_tool.py diff --git a/backend/.gitignore b/dataclaw-api/.gitignore similarity index 100% rename from backend/.gitignore rename to dataclaw-api/.gitignore diff --git a/dataclaw-api/Dockerfile b/dataclaw-api/Dockerfile new file mode 100644 index 0000000..57b861a --- /dev/null +++ b/dataclaw-api/Dockerfile @@ -0,0 +1,55 @@ +# Backend Dockerfile (Context is root) +# 固定 Debian bookworm,避免 python:3.11-slim 随上游切到 trixie/testing 导致 apt 源 404 +FROM python:3.11-slim-bookworm + +WORKDIR /app + +# 构建时若访问 deb.debian.org 不稳定:docker compose build --build-arg APT_CN_MIRROR=true +ARG APT_CN_MIRROR=false + +# Install system dependencies(重试 + 超时;可选国内镜像) +RUN set -eux; \ + printf 'Acquire::Retries "5";\nAcquire::http::Timeout "120";\nAcquire::https::Timeout "120";\n' \ + > /etc/apt/apt.conf.d/99dataclaw; \ + if [ "$APT_CN_MIRROR" = "true" ] || [ "$APT_CN_MIRROR" = "1" ] || [ "$APT_CN_MIRROR" = "yes" ]; then \ + for f in /etc/apt/sources.list.d/*.sources /etc/apt/sources.list; do \ + [ -f "$f" ] || continue; \ + sed -i 's|http://deb.debian.org/debian|https://mirrors.tuna.tsinghua.edu.cn/debian|g' "$f"; \ + sed -i 's|https://deb.debian.org/debian|https://mirrors.tuna.tsinghua.edu.cn/debian|g' "$f"; \ + sed -i 's|http://security.debian.org/debian-security|https://mirrors.tuna.tsinghua.edu.cn/debian-security|g' "$f"; \ + sed -i 's|https://security.debian.org/debian-security|https://mirrors.tuna.tsinghua.edu.cn/debian-security|g' "$f"; \ + done; \ + fi; \ + i=1; \ + while [ "$i" -le 8 ]; do \ + apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + build-essential curl libpq-dev && \ + rm -rf /var/lib/apt/lists/* && exit 0; \ + echo "apt attempt $i failed, retry in 25s..."; \ + i=$((i+1)); sleep 25; \ + done; \ + exit 1 + +# Install uv +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +# Copy build-related files from dataclaw-api directory +COPY dataclaw-api/pyproject.toml dataclaw-api/uv.lock dataclaw-api/hatch_build.py dataclaw-api/README.md ./ + +# Install dependencies +RUN uv sync --frozen --no-dev + +# Copy dataclaw-api source +COPY dataclaw-api/ . + +# Copy the actual agent-core package from the nested directory +COPY agent-core/nanobot/ ./nanobot/ +# Copy dataclaw-voice source +COPY dataclaw-voice/ ./whisper/ + +# Expose port +EXPOSE 8000 + +# Run the application with uvicorn +CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/backend/README.md b/dataclaw-api/README.md similarity index 100% rename from backend/README.md rename to dataclaw-api/README.md diff --git a/backend/app/api/__init__.py b/dataclaw-api/app/__init__.py similarity index 100% rename from backend/app/api/__init__.py rename to dataclaw-api/app/__init__.py diff --git a/backend/app/connectors/__init__.py b/dataclaw-api/app/agent/__init__.py similarity index 100% rename from backend/app/connectors/__init__.py rename to dataclaw-api/app/agent/__init__.py diff --git a/backend/app/agent/chart.py b/dataclaw-api/app/agent/chart.py similarity index 99% rename from backend/app/agent/chart.py rename to dataclaw-api/app/agent/chart.py index 0bfb83f..4df3574 100644 --- a/backend/app/agent/chart.py +++ b/dataclaw-api/app/agent/chart.py @@ -7,7 +7,7 @@ from pathlib import Path # Add project root to sys.path PROJECT_ROOT = Path(__file__).resolve().parents[3] -NANOBOT_ROOT = PROJECT_ROOT / "nanobot" +NANOBOT_ROOT = PROJECT_ROOT / "agent-core" if str(NANOBOT_ROOT) not in sys.path: sys.path.append(str(NANOBOT_ROOT)) diff --git a/backend/app/agent/nl2sql.py b/dataclaw-api/app/agent/nl2sql.py similarity index 81% rename from backend/app/agent/nl2sql.py rename to dataclaw-api/app/agent/nl2sql.py index fe245f1..b61234e 100644 --- a/backend/app/agent/nl2sql.py +++ b/dataclaw-api/app/agent/nl2sql.py @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) # Add project root to sys.path to allow importing nanobot PROJECT_ROOT = Path(__file__).resolve().parents[3] -NANOBOT_ROOT = PROJECT_ROOT / "nanobot" +NANOBOT_ROOT = PROJECT_ROOT / "agent-core" if str(NANOBOT_ROOT) not in sys.path: sys.path.append(str(NANOBOT_ROOT)) @@ -95,7 +95,20 @@ DEFAULT_TEXT_TO_SQL_RULES = """ - For the ranking problem, you must add the ranking column to the final SELECT clause. """ -SQL_GENERATION_SYSTEM_PROMPT = f""" +TABLE_SELECTOR_SYSTEM_PROMPT = """ +You are a helpful assistant that identifies relevant database tables for a given natural language query. + +Given the list of available tables and the user's question, return a list of table names that are likely to contain the information needed to answer the question. + +### FINAL ANSWER FORMAT ### +The final answer must be a JSON array of strings: +[ + "table_name1", + "table_name2" +] +""" + +SQL_GENERATION_SYSTEM_PROMPT = """ You are a helpful assistant that converts natural language queries into ANSI SQL queries. Given user's question and database schema, generate accurate ANSI SQL directly and concisely. @@ -105,15 +118,25 @@ Given user's question and database schema, generate accurate ANSI SQL directly a 1. YOU MUST FOLLOW the instructions strictly to generate the SQL query if the section of USER INSTRUCTIONS is available in user's input. 2. YOU MUST FOLLOW SQL Rules if they are not contradicted with instructions. -{DEFAULT_TEXT_TO_SQL_RULES} +""" + DEFAULT_TEXT_TO_SQL_RULES + """ + +### FEW-SHOT EXAMPLES ### +Example 1: +User's Question: 谁是去年前五个销售额最高的客户? +Database Schema: {"customers": [{"name": "customer_id", "type": "INT"}, {"name": "customer_name", "type": "TEXT"}], "orders": [{"name": "order_id", "type": "INT"}, {"name": "customer_id", "type": "INT"}, {"name": "amount", "type": "DECIMAL"}, {"name": "order_date", "type": "DATE"}]} +Final Answer: +{ + "reasoning": "I need to join customers and orders, filter for last year (2025 if current is 2026), group by customer, sum the amount, and limit to top 5.", + "sql": "SELECT \\"customers\\".\\"customer_name\\", SUM(\\"orders\\".\\"amount\\") AS \\"total_sales\\" FROM \\"customers\\" JOIN \\"orders\\" ON \\"customers\\".\\"customer_id\\" = \\"orders\\".\\"customer_id\\" WHERE \\"orders\\".\\"order_date\" BETWEEN '2025-01-01' AND '2025-12-31' GROUP BY \\"customers\\".\\"customer_name\\" ORDER BY \\"total_sales\\" DESC LIMIT 5;" +} ### FINAL ANSWER FORMAT ### The final answer must be a ANSI SQL query in JSON format: -{{ +{ "reasoning": , "sql": -}} +} """ def _resolve_upload_file_path(file_url: Optional[str]) -> Path: @@ -239,6 +262,94 @@ async def _check_connection_with_cache(source: str, connector: Any) -> bool: _connection_cache[cache_key] = {"ok": ok, "expires_at": now + CONNECTION_CACHE_TTL_SECONDS} return ok +async def _select_relevant_tables( + query: str, + schema: Dict[str, Any], + provider: Any, + on_progress: Callable[[str], Awaitable[None]] | None = None +) -> List[str]: + """Use LLM to select relevant tables from the schema to reduce context size.""" + table_names = list(schema.keys()) + if len(table_names) <= 5: + return table_names + + if on_progress: + await on_progress("正在进行语义表搜索") + + user_prompt = f"User's Question: {query}\nAvailable Tables: {', '.join(table_names)}" + messages = [ + {"role": "system", "content": TABLE_SELECTOR_SYSTEM_PROMPT}, + {"role": "user", "content": user_prompt} + ] + + try: + response = await asyncio.wait_for( + provider.chat( + messages=messages, + max_tokens=200, + temperature=0.0, + ), + timeout=30.0 + ) + content = (response.content or "").strip() + if "```json" in content: + content = content.split("```json")[1].split("```")[0] + elif "```" in content: + content = content.split("```")[1].split("```")[0] + + selected = json.loads(content.strip()) + if isinstance(selected, list): + # Filter valid table names + valid_selected = [t for t in selected if t in table_names] + if valid_selected: + return valid_selected + except Exception as e: + logger.warning(f"Table selection failed: {e}") + + return table_names + +async def _fetch_sample_data( + connector: Any, + table_names: List[str], + on_progress: Callable[[str], Awaitable[None]] | None = None +) -> Dict[str, List[Dict[str, Any]]]: + """Fetch sample rows for selected tables to help LLM understand data.""" + samples = {} + if not connector or not hasattr(connector, "execute_query"): + return samples + + if on_progress: + await on_progress(f"正在抓取 {len(table_names)} 張表的樣本數據") + + for table in table_names: + try: + # We use a very small limit + query = f"SELECT * FROM \"{table}\" LIMIT 3" + results = await asyncio.wait_for( + asyncio.to_thread(connector.execute_query, query), + timeout=10.0 + ) + + rows = [] + if isinstance(results, list): + if results and isinstance(results[0], dict): + rows = results + elif results and isinstance(results[0], (list, tuple)): + rows = [list(row) for row in results] + else: + rows = results + elif isinstance(results, tuple) and len(results) == 2: + rows_raw, cols = results + col_names = [c[0] for c in cols] + rows = [dict(zip(col_names, row)) for row in rows_raw] + + if rows: + samples[table] = rows + except Exception as e: + logger.warning(f"Failed to fetch sample for {table}: {e}") + + return samples + async def process_nl2sql( request: NL2SQLRequest, on_progress: Callable[[str], Awaitable[None]] | None = None, @@ -323,39 +434,6 @@ async def process_nl2sql( _set_cached_schema(request.source, connector, schema) await emit_progress(f"Schema 拉取完成,共 {len(schema)} 张表 ({time.perf_counter() - schema_started:.2f}s)") - - schema_str = json.dumps(schema, ensure_ascii=False, separators=(",", ":")) - - # Try to load MDL context - mdl_context = "" - if request.source.startswith("ds:"): - try: - ds_id = int(request.source.split(":")[1]) - mdl = await asyncio.to_thread(MDLService.get_mdl, ds_id) - if mdl: - mdl_lines = ["\n### SEMANTIC MODEL (WrenMDL) ###"] - - mdl_lines.append("MODELS:") - for model in mdl.models: - table_ref = model.tableReference.table if model.tableReference else model.name - desc = f" - Description: {model.properties.get('description', '')}" if model.properties.get('description') else "" - mdl_lines.append(f"- Model: {model.name} (Table: {table_ref}){desc}") - - if model.columns: - mdl_lines.append(" Columns:") - for col in model.columns: - col_desc = f" ({col.properties.get('description')})" if col.properties.get('description') else "" - expr = f" [Calculated: {col.expression}]" if col.isCalculated else "" - mdl_lines.append(f" - {col.name} ({col.type}){col_desc}{expr}") - - if mdl.relationships: - mdl_lines.append("\nRELATIONSHIPS:") - for rel in mdl.relationships: - mdl_lines.append(f"- {rel.name}: {rel.joinType} between {rel.models} ON {rel.condition}") - - mdl_context = "\n".join(mdl_lines) - except Exception as e: - print(f"Failed to load MDL: {e}") # 2. Get the active LLM config active_config = get_active_llm_config() @@ -375,12 +453,64 @@ async def process_nl2sql( except Exception as e: return NL2SQLResponse(sql="", result=[], error=f"Failed to initialize LLM provider: {e}") - # 4. Construct Prompt + # 4. Table Selection and Sample Data (Optimization) + relevant_tables = await _select_relevant_tables(request.query, schema, provider, emit_progress) + pruned_schema = {t: schema[t] for t in relevant_tables if t in schema} + + samples = {} + if request.source != "upload": # For upload, df is already in memory and small + samples = await _fetch_sample_data(connector, relevant_tables, emit_progress) + + schema_str = json.dumps(pruned_schema, ensure_ascii=False, separators=(",", ":")) + samples_str = json.dumps(samples, ensure_ascii=False, separators=(",", ":")) if samples else "" + + # Try to load MDL context + mdl_context = "" + if request.source.startswith("ds:"): + try: + ds_id = int(request.source.split(":")[1]) + mdl = await asyncio.to_thread(MDLService.get_mdl, ds_id) + if mdl: + mdl_lines = ["\n### SEMANTIC MODEL (WrenMDL) ###"] + + mdl_lines.append("MODELS:") + for model in mdl.models: + # Only include relevant models + if model.name not in relevant_tables and (model.tableReference and model.tableReference.table not in relevant_tables): + continue + + table_ref = model.tableReference.table if model.tableReference else model.name + desc = f" - Description: {model.properties.get('description', '')}" if model.properties.get('description') else "" + mdl_lines.append(f"- Model: {model.name} (Table: {table_ref}){desc}") + + if model.columns: + mdl_lines.append(" Columns:") + for col in model.columns: + col_desc = f" ({col.properties.get('description')})" if col.properties.get('description') else "" + expr = f" [Calculated: {col.expression}]" if col.isCalculated else "" + mdl_lines.append(f" - {col.name} ({col.type}){col_desc}{expr}") + + if mdl.relationships: + mdl_lines.append("\nRELATIONSHIPS:") + for rel in mdl.relationships: + # Only include relevant relationships + rel_models = rel.models if isinstance(rel.models, list) else [] + if any(m in relevant_tables for m in rel_models): + mdl_lines.append(f"- {rel.name}: {rel.joinType} between {rel.models} ON {rel.condition}") + + mdl_context = "\n".join(mdl_lines) + except Exception as e: + print(f"Failed to load MDL: {e}") + + # 5. Construct Prompt user_prompt = f""" ### DATABASE SCHEMA ### {schema_str} {mdl_context} +### SAMPLE DATA ### +{samples_str} + ### INPUTS ### User's Question: {request.query} Language: Chinese (Simplified) diff --git a/backend/app/core/__init__.py b/dataclaw-api/app/api/__init__.py similarity index 100% rename from backend/app/core/__init__.py rename to dataclaw-api/app/api/__init__.py diff --git a/backend/app/api/a2a.py b/dataclaw-api/app/api/a2a.py similarity index 99% rename from backend/app/api/a2a.py rename to dataclaw-api/app/api/a2a.py index 556326c..99aaaae 100644 --- a/backend/app/api/a2a.py +++ b/dataclaw-api/app/api/a2a.py @@ -167,8 +167,8 @@ AGENT_SKILLS = [ ] AGENT_PROVIDER = AgentProvider( - organization="DataClaw", - url="https://dataclaw.io", + organization="全源灵动", + url="", ) AGENT_SUPPORTED_INTERFACES = [ @@ -216,7 +216,7 @@ AGENT_SECURITY_SCHEMES = { def _build_public_agent_card() -> AgentCardPublicSchema: return AgentCardPublicSchema( - name="DataClaw A2A Gateway", + name="全源灵动 A2A 网关", protocol_version=SUPPORTED_PROTOCOL_VERSION, capabilities=SUPPORTED_CAPABILITIES, endpoints={ @@ -234,8 +234,8 @@ def _build_public_agent_card() -> AgentCardPublicSchema: supportedInterfaces=AGENT_SUPPORTED_INTERFACES, defaultInputModes=["text", "data"], defaultOutputModes=["text", "artifact", "stream"], - iconUrl="https://dataclaw.io/icon.png", - documentationUrl="https://docs.dataclaw.io/a2a", + iconUrl="", + documentationUrl="", ) @@ -940,7 +940,10 @@ async def health_check_remote_agent( return {"healthy": False, "failure_count": item.failure_count} +# NOTE: UI 侧走 `/api/v1/a2a/message:send`,而 A2A 规范/Agent Card 也可能使用根路径 `/message:send`。 +# 这里提供双路由,保持兼容。 @router.post("/message:send") +@router.post(f"{A2A_API_PREFIX}/message:send") async def send_message( request: SendMessageRequest, response: Response, @@ -1088,6 +1091,7 @@ async def send_message( @router.post("/message:stream") +@router.post(f"{A2A_API_PREFIX}/message:stream") async def send_streaming_message( request: SendStreamingMessageRequest, response: Response, diff --git a/backend/app/api/datasources.py b/dataclaw-api/app/api/datasources.py similarity index 100% rename from backend/app/api/datasources.py rename to dataclaw-api/app/api/datasources.py diff --git a/backend/app/api/embedding_models.py b/dataclaw-api/app/api/embedding_models.py similarity index 100% rename from backend/app/api/embedding_models.py rename to dataclaw-api/app/api/embedding_models.py diff --git a/backend/app/api/knowledge.py b/dataclaw-api/app/api/knowledge.py similarity index 100% rename from backend/app/api/knowledge.py rename to dataclaw-api/app/api/knowledge.py diff --git a/backend/app/api/llm.py b/dataclaw-api/app/api/llm.py similarity index 100% rename from backend/app/api/llm.py rename to dataclaw-api/app/api/llm.py diff --git a/backend/app/api/mcp.py b/dataclaw-api/app/api/mcp.py similarity index 100% rename from backend/app/api/mcp.py rename to dataclaw-api/app/api/mcp.py diff --git a/backend/app/api/projects.py b/dataclaw-api/app/api/projects.py similarity index 100% rename from backend/app/api/projects.py rename to dataclaw-api/app/api/projects.py diff --git a/backend/app/api/semantic.py b/dataclaw-api/app/api/semantic.py similarity index 100% rename from backend/app/api/semantic.py rename to dataclaw-api/app/api/semantic.py diff --git a/backend/app/api/skills.py b/dataclaw-api/app/api/skills.py similarity index 100% rename from backend/app/api/skills.py rename to dataclaw-api/app/api/skills.py diff --git a/backend/app/api/subagents.py b/dataclaw-api/app/api/subagents.py similarity index 100% rename from backend/app/api/subagents.py rename to dataclaw-api/app/api/subagents.py diff --git a/backend/app/api/upload.py b/dataclaw-api/app/api/upload.py similarity index 100% rename from backend/app/api/upload.py rename to dataclaw-api/app/api/upload.py diff --git a/backend/app/api/users.py b/dataclaw-api/app/api/users.py similarity index 100% rename from backend/app/api/users.py rename to dataclaw-api/app/api/users.py diff --git a/backend/app/api/web_search.py b/dataclaw-api/app/api/web_search.py similarity index 100% rename from backend/app/api/web_search.py rename to dataclaw-api/app/api/web_search.py diff --git a/backend/app/cli.py b/dataclaw-api/app/cli.py similarity index 99% rename from backend/app/cli.py rename to dataclaw-api/app/cli.py index 5ea74b4..602bc18 100644 --- a/backend/app/cli.py +++ b/dataclaw-api/app/cli.py @@ -16,7 +16,7 @@ from app.core.data_root import get_data_root app = typer.Typer( name="dataclaw", context_settings={"help_option_names": ["-h", "--help"]}, - help="DataClaw WebUI 服务控制命令", + help="全源灵动 WebUI 服务控制命令", no_args_is_help=True, ) console = Console() diff --git a/nanobot/nanobot/templates/__init__.py b/dataclaw-api/app/connectors/__init__.py similarity index 100% rename from nanobot/nanobot/templates/__init__.py rename to dataclaw-api/app/connectors/__init__.py diff --git a/backend/app/connectors/clickhouse.py b/dataclaw-api/app/connectors/clickhouse.py similarity index 100% rename from backend/app/connectors/clickhouse.py rename to dataclaw-api/app/connectors/clickhouse.py diff --git a/backend/app/connectors/csv.py b/dataclaw-api/app/connectors/csv.py similarity index 100% rename from backend/app/connectors/csv.py rename to dataclaw-api/app/connectors/csv.py diff --git a/dataclaw-api/app/connectors/duckdb.py b/dataclaw-api/app/connectors/duckdb.py new file mode 100644 index 0000000..34d52dd --- /dev/null +++ b/dataclaw-api/app/connectors/duckdb.py @@ -0,0 +1,48 @@ +import duckdb +import pandas as pd +from typing import List, Dict, Any, Optional +import os + +class DuckDBConnector: + def __init__(self, db_path: str = ":memory:"): + self.db_path = db_path + + def execute_query(self, query: str) -> List[Dict[str, Any]]: + conn = duckdb.connect(self.db_path) + try: + df = conn.execute(query).df() + return df.to_dict(orient="records") + finally: + conn.close() + + def get_schema(self) -> Dict[str, Any]: + conn = duckdb.connect(self.db_path) + try: + schema = {} + tables = conn.execute("SHOW TABLES").fetchall() + for (table_name,) in tables: + columns_info = conn.execute(f"DESCRIBE {table_name}").fetchall() + columns = [] + for col in columns_info: + columns.append({ + "name": col[0], + "type": col[1] + }) + schema[table_name] = { + "columns": columns, + "primary_keys": [], # DuckDB describe doesn't easily show PKs in this format + "foreign_keys": [] + } + return schema + finally: + conn.close() + + def test_connection(self) -> bool: + try: + conn = duckdb.connect(self.db_path) + conn.execute("SELECT 1") + conn.close() + return True + except Exception as e: + print(f"DuckDB Connection Error: {e}") + return False diff --git a/backend/app/connectors/factory.py b/dataclaw-api/app/connectors/factory.py similarity index 78% rename from backend/app/connectors/factory.py rename to dataclaw-api/app/connectors/factory.py index 53a199b..8240a80 100644 --- a/backend/app/connectors/factory.py +++ b/dataclaw-api/app/connectors/factory.py @@ -5,6 +5,7 @@ from app.connectors.postgres import PostgresConnector from app.connectors.clickhouse import ClickHouseConnector from app.connectors.parquet import ParquetConnector from app.connectors.csv import CSVConnector +from app.connectors.duckdb import DuckDBConnector from app.models.datasource import DataSource from app.core.files import resolve_upload_file_path @@ -26,6 +27,15 @@ def _get_cached_connector(ds_type: str, config_json: str): return PostgresConnector(db_url=db_url) + elif ds_type == "mysql": + db_url = config.get("connection_string") + if not db_url: + port = config.get("port") or 3306 + db_url = f"mysql+pymysql://{config.get('user')}:{config.get('password')}@{config.get('host')}:{port}/{config.get('database')}" + elif not db_url.startswith("mysql+pymysql://"): + db_url = db_url.replace("mysql://", "mysql+pymysql://") + return PostgresConnector(db_url=db_url) + elif ds_type == "sqlite": # SQLite uses connection string usually file path db_url = config.get("connection_string") @@ -43,6 +53,12 @@ def _get_cached_connector(ds_type: str, config_json: str): database=config.get("database", "default") ) + elif ds_type == "duckdb": + db_path = config.get("database") or config.get("file_path") or ":memory:" + if db_path != ":memory:": + db_path = str(resolve_upload_file_path(db_path)) + return DuckDBConnector(db_path=db_path) + elif ds_type == "parquet": file_path = str(resolve_upload_file_path(config.get("file_path"))) return ParquetConnector(file_path=file_path) @@ -62,8 +78,5 @@ def get_connector(datasource: DataSource): def get_connector_from_config(ds_type: str, config: Dict[str, Any]): # Helper for testing connection without saving to DB - # We can use the cached function too, or bypass if we want fresh check - # Usually for testing we want fresh check, so let's bypass cache or clear it if needed. - # But reusing cache is fine if config is same. config_str = json.dumps(config, sort_keys=True) return _get_cached_connector(ds_type.lower(), config_str) diff --git a/backend/app/connectors/parquet.py b/dataclaw-api/app/connectors/parquet.py similarity index 100% rename from backend/app/connectors/parquet.py rename to dataclaw-api/app/connectors/parquet.py diff --git a/backend/app/connectors/postgres.py b/dataclaw-api/app/connectors/postgres.py similarity index 100% rename from backend/app/connectors/postgres.py rename to dataclaw-api/app/connectors/postgres.py diff --git a/backend/app/context.py b/dataclaw-api/app/context.py similarity index 100% rename from backend/app/context.py rename to dataclaw-api/app/context.py diff --git a/nanobot/nanobot/templates/memory/__init__.py b/dataclaw-api/app/core/__init__.py similarity index 100% rename from nanobot/nanobot/templates/memory/__init__.py rename to dataclaw-api/app/core/__init__.py diff --git a/backend/app/core/artifacts.py b/dataclaw-api/app/core/artifacts.py similarity index 100% rename from backend/app/core/artifacts.py rename to dataclaw-api/app/core/artifacts.py diff --git a/backend/app/core/data_root.py b/dataclaw-api/app/core/data_root.py similarity index 100% rename from backend/app/core/data_root.py rename to dataclaw-api/app/core/data_root.py diff --git a/backend/app/core/email.py b/dataclaw-api/app/core/email.py similarity index 81% rename from backend/app/core/email.py rename to dataclaw-api/app/core/email.py index eeb3903..2ca00d8 100644 --- a/backend/app/core/email.py +++ b/dataclaw-api/app/core/email.py @@ -17,16 +17,16 @@ def send_verification_email(to_email: str, token: str): msg = MIMEMultipart() msg['From'] = smtp_user msg['To'] = to_email - msg['Subject'] = "Please verify your email address" + msg['Subject'] = "请验证你的邮箱地址" verify_link = f"{frontend_url}/verify-email?token={token}" body = f""" -

Welcome to DataClaw!

-

Please click the link below to verify your email address and activate your account:

+

欢迎使用全源灵动!

+

请点击下方链接验证邮箱并激活账号:

{verify_link}

-

If you did not request this, please ignore this email.

+

如果你没有发起该请求,请忽略此邮件。

""" diff --git a/backend/app/core/files.py b/dataclaw-api/app/core/files.py similarity index 100% rename from backend/app/core/files.py rename to dataclaw-api/app/core/files.py diff --git a/backend/app/core/llm_provider.py b/dataclaw-api/app/core/llm_provider.py similarity index 63% rename from backend/app/core/llm_provider.py rename to dataclaw-api/app/core/llm_provider.py index 9ab343c..96a21cc 100644 --- a/backend/app/core/llm_provider.py +++ b/dataclaw-api/app/core/llm_provider.py @@ -1,3 +1,4 @@ +import os from typing import Optional, Dict from nanobot.providers.azure_openai_provider import AzureOpenAIProvider @@ -18,6 +19,29 @@ def normalize_provider_name(provider: Optional[str]) -> Optional[str]: return alias_map.get(normalized, normalized) +def _running_in_docker() -> bool: + # Best-effort, cross-platform detection. + if os.environ.get("DATACLAW_RUNNING_IN_DOCKER", "").strip().lower() in ("1", "true", "yes", "y"): + return True + return os.path.exists("/.dockerenv") + + +def _rewrite_localhost_api_base(api_base: Optional[str]) -> Optional[str]: + """ + When running inside Docker, `localhost` points to the container itself. + For host-local LLMs (Ollama/vLLM), users often configure `http://localhost:...`, + which breaks in containers. We rewrite it to `host.docker.internal`. + """ + if not api_base: + return api_base + base = api_base.strip() + if base.startswith("http://localhost") or base.startswith("https://localhost"): + return base.replace("://localhost", "://host.docker.internal", 1) + if base.startswith("http://127.0.0.1") or base.startswith("https://127.0.0.1"): + return base.replace("://127.0.0.1", "://host.docker.internal", 1) + return api_base + + def build_llm_provider( *, model: str, @@ -29,6 +53,8 @@ def build_llm_provider( provider_name = normalize_provider_name(provider) spec = find_by_name(provider_name) if provider_name else None backend = spec.backend if spec else "openai_compat" + if _running_in_docker(): + api_base = _rewrite_localhost_api_base(api_base) if backend == "openai_codex" or model.startswith("openai-codex/"): return OpenAICodexProvider(default_model=model) diff --git a/backend/app/core/nanobot.py b/dataclaw-api/app/core/nanobot.py similarity index 98% rename from backend/app/core/nanobot.py rename to dataclaw-api/app/core/nanobot.py index 61599ce..01fd8d9 100644 --- a/backend/app/core/nanobot.py +++ b/dataclaw-api/app/core/nanobot.py @@ -11,8 +11,8 @@ from typing import List, Callable, Awaitable, Any, Dict # BUT nanobot package is in ../nanobot relative to backend/ # So we need to go up one more level to reach the parent of backend/ PROJECT_ROOT = Path(__file__).resolve().parents[3] -if str(PROJECT_ROOT / "nanobot") not in sys.path: - sys.path.append(str(PROJECT_ROOT / "nanobot")) +if str(PROJECT_ROOT / "agent-core") not in sys.path: + sys.path.append(str(PROJECT_ROOT / "agent-core")) from nanobot.agent.loop import AgentLoop from nanobot.bus.events import OutboundMessage @@ -32,6 +32,7 @@ from nanobot.config.schema import Config # Given the structure, importing here should be fine as long as skills.py doesn't import nanobot.py. from app.api.skills import load_skills from app.core.patched_openai_compat_provider import PatchedOpenAICompatProvider +from app.core.llm_provider import _rewrite_localhost_api_base, _running_in_docker from app.services.llm_cache import get_llm_configs, get_active_llm_config from app.services.web_search_config_store import get_web_search_config @@ -203,7 +204,7 @@ class NanobotIntegration: workspace_skills_root = workspace_path / "skills" workspace_skills_root.mkdir(parents=True, exist_ok=True) - for skill_name in ("nl2sql", "visualization"): + for skill_name in ("nl2sql", "visualization", "knowledge-base"): source_dir = builtin_root / skill_name source_skill_file = source_dir / "SKILL.md" if not source_skill_file.exists(): @@ -235,6 +236,8 @@ class NanobotIntegration: ): spec = find_by_name(provider_name) if provider_name else None backend = spec.backend if spec else "openai_compat" + if _running_in_docker(): + api_base = _rewrite_localhost_api_base(api_base) if backend == "openai_codex" or model.startswith("openai-codex/"): return OpenAICodexProvider(default_model=model) diff --git a/backend/app/core/patched_openai_compat_provider.py b/dataclaw-api/app/core/patched_openai_compat_provider.py similarity index 100% rename from backend/app/core/patched_openai_compat_provider.py rename to dataclaw-api/app/core/patched_openai_compat_provider.py diff --git a/backend/app/core/security.py b/dataclaw-api/app/core/security.py similarity index 100% rename from backend/app/core/security.py rename to dataclaw-api/app/core/security.py diff --git a/backend/app/core/session_alias_store.py b/dataclaw-api/app/core/session_alias_store.py similarity index 100% rename from backend/app/core/session_alias_store.py rename to dataclaw-api/app/core/session_alias_store.py diff --git a/backend/app/database.py b/dataclaw-api/app/database.py similarity index 100% rename from backend/app/database.py rename to dataclaw-api/app/database.py diff --git a/backend/app/models/a2a.py b/dataclaw-api/app/models/a2a.py similarity index 100% rename from backend/app/models/a2a.py rename to dataclaw-api/app/models/a2a.py diff --git a/backend/app/models/datasource.py b/dataclaw-api/app/models/datasource.py similarity index 100% rename from backend/app/models/datasource.py rename to dataclaw-api/app/models/datasource.py diff --git a/backend/app/models/project.py b/dataclaw-api/app/models/project.py similarity index 100% rename from backend/app/models/project.py rename to dataclaw-api/app/models/project.py diff --git a/backend/app/models/subagent.py b/dataclaw-api/app/models/subagent.py similarity index 100% rename from backend/app/models/subagent.py rename to dataclaw-api/app/models/subagent.py diff --git a/backend/app/models/user.py b/dataclaw-api/app/models/user.py similarity index 100% rename from backend/app/models/user.py rename to dataclaw-api/app/models/user.py diff --git a/backend/app/schemas/a2a.py b/dataclaw-api/app/schemas/a2a.py similarity index 100% rename from backend/app/schemas/a2a.py rename to dataclaw-api/app/schemas/a2a.py diff --git a/backend/app/schemas/chart.py b/dataclaw-api/app/schemas/chart.py similarity index 100% rename from backend/app/schemas/chart.py rename to dataclaw-api/app/schemas/chart.py diff --git a/backend/app/schemas/datasource.py b/dataclaw-api/app/schemas/datasource.py similarity index 100% rename from backend/app/schemas/datasource.py rename to dataclaw-api/app/schemas/datasource.py diff --git a/backend/app/schemas/embedding_model.py b/dataclaw-api/app/schemas/embedding_model.py similarity index 100% rename from backend/app/schemas/embedding_model.py rename to dataclaw-api/app/schemas/embedding_model.py diff --git a/backend/app/schemas/knowledge.py b/dataclaw-api/app/schemas/knowledge.py similarity index 100% rename from backend/app/schemas/knowledge.py rename to dataclaw-api/app/schemas/knowledge.py diff --git a/backend/app/schemas/mcp.py b/dataclaw-api/app/schemas/mcp.py similarity index 100% rename from backend/app/schemas/mcp.py rename to dataclaw-api/app/schemas/mcp.py diff --git a/backend/app/schemas/mdl.py b/dataclaw-api/app/schemas/mdl.py similarity index 100% rename from backend/app/schemas/mdl.py rename to dataclaw-api/app/schemas/mdl.py diff --git a/backend/app/schemas/project.py b/dataclaw-api/app/schemas/project.py similarity index 100% rename from backend/app/schemas/project.py rename to dataclaw-api/app/schemas/project.py diff --git a/backend/app/schemas/subagent.py b/dataclaw-api/app/schemas/subagent.py similarity index 100% rename from backend/app/schemas/subagent.py rename to dataclaw-api/app/schemas/subagent.py diff --git a/backend/app/schemas/user.py b/dataclaw-api/app/schemas/user.py similarity index 100% rename from backend/app/schemas/user.py rename to dataclaw-api/app/schemas/user.py diff --git a/backend/app/services/a2a_service.py b/dataclaw-api/app/services/a2a_service.py similarity index 100% rename from backend/app/services/a2a_service.py rename to dataclaw-api/app/services/a2a_service.py diff --git a/backend/app/services/embedding_model_store.py b/dataclaw-api/app/services/embedding_model_store.py similarity index 100% rename from backend/app/services/embedding_model_store.py rename to dataclaw-api/app/services/embedding_model_store.py diff --git a/backend/app/services/knowledge_base_store.py b/dataclaw-api/app/services/knowledge_base_store.py similarity index 100% rename from backend/app/services/knowledge_base_store.py rename to dataclaw-api/app/services/knowledge_base_store.py diff --git a/backend/app/services/knowledge_global_config_store.py b/dataclaw-api/app/services/knowledge_global_config_store.py similarity index 100% rename from backend/app/services/knowledge_global_config_store.py rename to dataclaw-api/app/services/knowledge_global_config_store.py diff --git a/backend/app/services/knowledge_index.py b/dataclaw-api/app/services/knowledge_index.py similarity index 100% rename from backend/app/services/knowledge_index.py rename to dataclaw-api/app/services/knowledge_index.py diff --git a/backend/app/services/llm_cache.py b/dataclaw-api/app/services/llm_cache.py similarity index 100% rename from backend/app/services/llm_cache.py rename to dataclaw-api/app/services/llm_cache.py diff --git a/backend/app/services/mdl.py b/dataclaw-api/app/services/mdl.py similarity index 100% rename from backend/app/services/mdl.py rename to dataclaw-api/app/services/mdl.py diff --git a/backend/app/services/openai_compat.py b/dataclaw-api/app/services/openai_compat.py similarity index 100% rename from backend/app/services/openai_compat.py rename to dataclaw-api/app/services/openai_compat.py diff --git a/backend/app/services/web_search_config_store.py b/dataclaw-api/app/services/web_search_config_store.py similarity index 100% rename from backend/app/services/web_search_config_store.py rename to dataclaw-api/app/services/web_search_config_store.py diff --git a/backend/app/skills_builtin/frontend-design/LICENSE.txt b/dataclaw-api/app/skills_builtin/frontend-design/LICENSE.txt similarity index 100% rename from backend/app/skills_builtin/frontend-design/LICENSE.txt rename to dataclaw-api/app/skills_builtin/frontend-design/LICENSE.txt diff --git a/backend/app/skills_builtin/frontend-design/SKILL.md b/dataclaw-api/app/skills_builtin/frontend-design/SKILL.md similarity index 100% rename from backend/app/skills_builtin/frontend-design/SKILL.md rename to dataclaw-api/app/skills_builtin/frontend-design/SKILL.md diff --git a/dataclaw-api/app/skills_builtin/knowledge-base/SKILL.md b/dataclaw-api/app/skills_builtin/knowledge-base/SKILL.md new file mode 100644 index 0000000..ceadc64 --- /dev/null +++ b/dataclaw-api/app/skills_builtin/knowledge-base/SKILL.md @@ -0,0 +1,24 @@ +--- +description: Retrieve information from the knowledge base (RAG) +metadata: + nanobot: + always: true +--- + +# Knowledge Base Skill + +You are an expert assistant with access to a knowledge base. You can use the `knowledge_retrieve` tool to find relevant information from documents to answer user questions. + +## When to use this skill +- When the user asks questions about specific documents, company policies, technical manuals, or any uploaded knowledge. +- When the user's question seems to require domain-specific knowledge that is not in your general training data but likely in the knowledge base. + +## How to use this skill +- Call the `knowledge_retrieve` tool with the user's query. +- You can adjust `top_k` (default is 5, max 20) if you need more or less context. + +## After using the tool +- The tool will return a list of relevant "hits" (document chunks) and optionally an AI-generated answer based on those hits. +- Synthesize the information from the hits to provide a comprehensive and accurate answer. +- Always cite the sources if provided in the metadata of the hits. +- If no relevant information is found, inform the user clearly. diff --git a/backend/app/skills_builtin/minimax-docx/.gitignore b/dataclaw-api/app/skills_builtin/minimax-docx/.gitignore similarity index 100% rename from backend/app/skills_builtin/minimax-docx/.gitignore rename to dataclaw-api/app/skills_builtin/minimax-docx/.gitignore diff --git a/backend/app/skills_builtin/minimax-docx/LICENSE b/dataclaw-api/app/skills_builtin/minimax-docx/LICENSE similarity index 100% rename from backend/app/skills_builtin/minimax-docx/LICENSE rename to dataclaw-api/app/skills_builtin/minimax-docx/LICENSE diff --git a/backend/app/skills_builtin/minimax-docx/SKILL.md b/dataclaw-api/app/skills_builtin/minimax-docx/SKILL.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/SKILL.md rename to dataclaw-api/app/skills_builtin/minimax-docx/SKILL.md diff --git a/backend/app/skills_builtin/minimax-docx/assets/styles/academic_styles.xml b/dataclaw-api/app/skills_builtin/minimax-docx/assets/styles/academic_styles.xml similarity index 100% rename from backend/app/skills_builtin/minimax-docx/assets/styles/academic_styles.xml rename to dataclaw-api/app/skills_builtin/minimax-docx/assets/styles/academic_styles.xml diff --git a/backend/app/skills_builtin/minimax-docx/assets/styles/corporate_styles.xml b/dataclaw-api/app/skills_builtin/minimax-docx/assets/styles/corporate_styles.xml similarity index 100% rename from backend/app/skills_builtin/minimax-docx/assets/styles/corporate_styles.xml rename to dataclaw-api/app/skills_builtin/minimax-docx/assets/styles/corporate_styles.xml diff --git a/backend/app/skills_builtin/minimax-docx/assets/styles/default_styles.xml b/dataclaw-api/app/skills_builtin/minimax-docx/assets/styles/default_styles.xml similarity index 100% rename from backend/app/skills_builtin/minimax-docx/assets/styles/default_styles.xml rename to dataclaw-api/app/skills_builtin/minimax-docx/assets/styles/default_styles.xml diff --git a/backend/app/skills_builtin/minimax-docx/assets/xsd/aesthetic-rules.xsd b/dataclaw-api/app/skills_builtin/minimax-docx/assets/xsd/aesthetic-rules.xsd similarity index 100% rename from backend/app/skills_builtin/minimax-docx/assets/xsd/aesthetic-rules.xsd rename to dataclaw-api/app/skills_builtin/minimax-docx/assets/xsd/aesthetic-rules.xsd diff --git a/backend/app/skills_builtin/minimax-docx/assets/xsd/business-rules.xsd b/dataclaw-api/app/skills_builtin/minimax-docx/assets/xsd/business-rules.xsd similarity index 100% rename from backend/app/skills_builtin/minimax-docx/assets/xsd/business-rules.xsd rename to dataclaw-api/app/skills_builtin/minimax-docx/assets/xsd/business-rules.xsd diff --git a/backend/app/skills_builtin/minimax-docx/assets/xsd/common-types.xsd b/dataclaw-api/app/skills_builtin/minimax-docx/assets/xsd/common-types.xsd similarity index 100% rename from backend/app/skills_builtin/minimax-docx/assets/xsd/common-types.xsd rename to dataclaw-api/app/skills_builtin/minimax-docx/assets/xsd/common-types.xsd diff --git a/backend/app/skills_builtin/minimax-docx/assets/xsd/wml-subset.xsd b/dataclaw-api/app/skills_builtin/minimax-docx/assets/xsd/wml-subset.xsd similarity index 100% rename from backend/app/skills_builtin/minimax-docx/assets/xsd/wml-subset.xsd rename to dataclaw-api/app/skills_builtin/minimax-docx/assets/xsd/wml-subset.xsd diff --git a/backend/app/skills_builtin/minimax-docx/references/cjk_typography.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/cjk_typography.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/cjk_typography.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/cjk_typography.md diff --git a/backend/app/skills_builtin/minimax-docx/references/cjk_university_template_guide.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/cjk_university_template_guide.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/cjk_university_template_guide.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/cjk_university_template_guide.md diff --git a/backend/app/skills_builtin/minimax-docx/references/comments_guide.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/comments_guide.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/comments_guide.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/comments_guide.md diff --git a/backend/app/skills_builtin/minimax-docx/references/design_good_bad_examples.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/design_good_bad_examples.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/design_good_bad_examples.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/design_good_bad_examples.md diff --git a/backend/app/skills_builtin/minimax-docx/references/design_principles.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/design_principles.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/design_principles.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/design_principles.md diff --git a/backend/app/skills_builtin/minimax-docx/references/openxml_element_order.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_element_order.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/openxml_element_order.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_element_order.md diff --git a/backend/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part1.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part1.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part1.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part1.md diff --git a/backend/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part2.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part2.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part2.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part2.md diff --git a/backend/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part3.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part3.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part3.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_encyclopedia_part3.md diff --git a/backend/app/skills_builtin/minimax-docx/references/openxml_namespaces.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_namespaces.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/openxml_namespaces.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_namespaces.md diff --git a/backend/app/skills_builtin/minimax-docx/references/openxml_units.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_units.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/openxml_units.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/openxml_units.md diff --git a/backend/app/skills_builtin/minimax-docx/references/scenario_a_create.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/scenario_a_create.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/scenario_a_create.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/scenario_a_create.md diff --git a/backend/app/skills_builtin/minimax-docx/references/scenario_b_edit_content.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/scenario_b_edit_content.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/scenario_b_edit_content.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/scenario_b_edit_content.md diff --git a/backend/app/skills_builtin/minimax-docx/references/scenario_c_apply_template.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/scenario_c_apply_template.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/scenario_c_apply_template.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/scenario_c_apply_template.md diff --git a/backend/app/skills_builtin/minimax-docx/references/track_changes_guide.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/track_changes_guide.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/track_changes_guide.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/track_changes_guide.md diff --git a/backend/app/skills_builtin/minimax-docx/references/troubleshooting.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/troubleshooting.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/troubleshooting.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/troubleshooting.md diff --git a/backend/app/skills_builtin/minimax-docx/references/typography_guide.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/typography_guide.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/typography_guide.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/typography_guide.md diff --git a/backend/app/skills_builtin/minimax-docx/references/xsd_validation_guide.md b/dataclaw-api/app/skills_builtin/minimax-docx/references/xsd_validation_guide.md similarity index 100% rename from backend/app/skills_builtin/minimax-docx/references/xsd_validation_guide.md rename to dataclaw-api/app/skills_builtin/minimax-docx/references/xsd_validation_guide.md diff --git a/backend/app/skills_builtin/minimax-docx/scripts/doc_to_docx.sh b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/doc_to_docx.sh old mode 100755 new mode 100644 similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/doc_to_docx.sh rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/doc_to_docx.sh diff --git a/backend/app/skills_builtin/minimax-docx/scripts/docx_preview.sh b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/docx_preview.sh old mode 100755 new mode 100644 similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/docx_preview.sh rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/docx_preview.sh diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/MiniMaxAIDocx.Cli.csproj diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/Program.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/Program.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/Program.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Cli/Program.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/AnalyzeCommand.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/AnalyzeCommand.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/AnalyzeCommand.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/AnalyzeCommand.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ApplyTemplateCommand.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ApplyTemplateCommand.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ApplyTemplateCommand.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ApplyTemplateCommand.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/CreateCommand.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/CreateCommand.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/CreateCommand.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/CreateCommand.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/DiffCommand.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/DiffCommand.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/DiffCommand.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/DiffCommand.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/EditContentCommand.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/EditContentCommand.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/EditContentCommand.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/EditContentCommand.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/FixOrderCommand.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/FixOrderCommand.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/FixOrderCommand.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/FixOrderCommand.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/MergeRunsCommand.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/MergeRunsCommand.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/MergeRunsCommand.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/MergeRunsCommand.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ValidateCommand.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ValidateCommand.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ValidateCommand.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Commands/ValidateCommand.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/MiniMaxAIDocx.Core.csproj diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/CommentSynchronizer.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/CommentSynchronizer.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/CommentSynchronizer.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/CommentSynchronizer.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/ElementOrder.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/ElementOrder.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/ElementOrder.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/ElementOrder.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/NamespaceConstants.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/NamespaceConstants.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/NamespaceConstants.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/NamespaceConstants.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/RunMerger.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/RunMerger.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/RunMerger.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/RunMerger.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/StyleAnalyzer.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/StyleAnalyzer.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/StyleAnalyzer.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/StyleAnalyzer.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/TrackChangesHelper.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/UnitConverter.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/UnitConverter.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/UnitConverter.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/OpenXml/UnitConverter.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch1.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch1.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch1.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch1.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch2.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch2.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch2.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch2.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch3.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch3.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch3.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch3.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch4.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch4.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch4.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/AestheticRecipeSamples_Batch4.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/CharacterFormattingSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/CharacterFormattingSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/CharacterFormattingSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/CharacterFormattingSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/DocumentCreationSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/DocumentCreationSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/DocumentCreationSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/DocumentCreationSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FieldAndTocSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FieldAndTocSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FieldAndTocSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FieldAndTocSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FootnoteAndCommentSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FootnoteAndCommentSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FootnoteAndCommentSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/FootnoteAndCommentSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/HeaderFooterSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/HeaderFooterSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/HeaderFooterSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/HeaderFooterSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ImageSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ImageSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ImageSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ImageSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ListAndNumberingSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ListAndNumberingSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ListAndNumberingSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ListAndNumberingSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ParagraphFormattingSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ParagraphFormattingSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ParagraphFormattingSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/ParagraphFormattingSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/StyleSystemSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/StyleSystemSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/StyleSystemSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/StyleSystemSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TableSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TableSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TableSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TableSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TrackChangesSamples.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TrackChangesSamples.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TrackChangesSamples.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Samples/TrackChangesSamples.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/CjkHelper.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/CjkHelper.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/CjkHelper.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/CjkHelper.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/FontDefaults.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/FontDefaults.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/FontDefaults.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/FontDefaults.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/PageSizes.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/PageSizes.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/PageSizes.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Typography/PageSizes.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/BusinessRuleValidator.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/BusinessRuleValidator.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/BusinessRuleValidator.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/BusinessRuleValidator.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/GateCheckValidator.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/GateCheckValidator.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/GateCheckValidator.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/GateCheckValidator.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/ValidationResult.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/ValidationResult.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/ValidationResult.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/ValidationResult.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/XsdValidator.cs b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/XsdValidator.cs similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/XsdValidator.cs rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.Core/Validation/XsdValidator.cs diff --git a/backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.slnx b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.slnx similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.slnx rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/dotnet/MiniMaxAIDocx.slnx diff --git a/backend/app/skills_builtin/minimax-docx/scripts/env_check.sh b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/env_check.sh old mode 100755 new mode 100644 similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/env_check.sh rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/env_check.sh diff --git a/backend/app/skills_builtin/minimax-docx/scripts/setup.ps1 b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/setup.ps1 similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/setup.ps1 rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/setup.ps1 diff --git a/backend/app/skills_builtin/minimax-docx/scripts/setup.sh b/dataclaw-api/app/skills_builtin/minimax-docx/scripts/setup.sh old mode 100755 new mode 100644 similarity index 100% rename from backend/app/skills_builtin/minimax-docx/scripts/setup.sh rename to dataclaw-api/app/skills_builtin/minimax-docx/scripts/setup.sh diff --git a/backend/app/skills_builtin/minimax-pdf/README.md b/dataclaw-api/app/skills_builtin/minimax-pdf/README.md similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/README.md rename to dataclaw-api/app/skills_builtin/minimax-pdf/README.md diff --git a/backend/app/skills_builtin/minimax-pdf/SKILL.md b/dataclaw-api/app/skills_builtin/minimax-pdf/SKILL.md similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/SKILL.md rename to dataclaw-api/app/skills_builtin/minimax-pdf/SKILL.md diff --git a/backend/app/skills_builtin/minimax-pdf/design/design.md b/dataclaw-api/app/skills_builtin/minimax-pdf/design/design.md similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/design/design.md rename to dataclaw-api/app/skills_builtin/minimax-pdf/design/design.md diff --git a/backend/app/skills_builtin/minimax-pdf/scripts/cover.py b/dataclaw-api/app/skills_builtin/minimax-pdf/scripts/cover.py similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/scripts/cover.py rename to dataclaw-api/app/skills_builtin/minimax-pdf/scripts/cover.py diff --git a/backend/app/skills_builtin/minimax-pdf/scripts/fill_inspect.py b/dataclaw-api/app/skills_builtin/minimax-pdf/scripts/fill_inspect.py similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/scripts/fill_inspect.py rename to dataclaw-api/app/skills_builtin/minimax-pdf/scripts/fill_inspect.py diff --git a/backend/app/skills_builtin/minimax-pdf/scripts/fill_write.py b/dataclaw-api/app/skills_builtin/minimax-pdf/scripts/fill_write.py similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/scripts/fill_write.py rename to dataclaw-api/app/skills_builtin/minimax-pdf/scripts/fill_write.py diff --git a/backend/app/skills_builtin/minimax-pdf/scripts/make.sh b/dataclaw-api/app/skills_builtin/minimax-pdf/scripts/make.sh similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/scripts/make.sh rename to dataclaw-api/app/skills_builtin/minimax-pdf/scripts/make.sh diff --git a/backend/app/skills_builtin/minimax-pdf/scripts/merge.py b/dataclaw-api/app/skills_builtin/minimax-pdf/scripts/merge.py similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/scripts/merge.py rename to dataclaw-api/app/skills_builtin/minimax-pdf/scripts/merge.py diff --git a/backend/app/skills_builtin/minimax-pdf/scripts/palette.py b/dataclaw-api/app/skills_builtin/minimax-pdf/scripts/palette.py similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/scripts/palette.py rename to dataclaw-api/app/skills_builtin/minimax-pdf/scripts/palette.py diff --git a/backend/app/skills_builtin/minimax-pdf/scripts/reformat_parse.py b/dataclaw-api/app/skills_builtin/minimax-pdf/scripts/reformat_parse.py similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/scripts/reformat_parse.py rename to dataclaw-api/app/skills_builtin/minimax-pdf/scripts/reformat_parse.py diff --git a/backend/app/skills_builtin/minimax-pdf/scripts/render_body.py b/dataclaw-api/app/skills_builtin/minimax-pdf/scripts/render_body.py similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/scripts/render_body.py rename to dataclaw-api/app/skills_builtin/minimax-pdf/scripts/render_body.py diff --git a/backend/app/skills_builtin/minimax-pdf/scripts/render_cover.js b/dataclaw-api/app/skills_builtin/minimax-pdf/scripts/render_cover.js similarity index 100% rename from backend/app/skills_builtin/minimax-pdf/scripts/render_cover.js rename to dataclaw-api/app/skills_builtin/minimax-pdf/scripts/render_cover.js diff --git a/backend/app/skills_builtin/minimax-xlsx/SKILL.md b/dataclaw-api/app/skills_builtin/minimax-xlsx/SKILL.md similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/SKILL.md rename to dataclaw-api/app/skills_builtin/minimax-xlsx/SKILL.md diff --git a/backend/app/skills_builtin/minimax-xlsx/references/create.md b/dataclaw-api/app/skills_builtin/minimax-xlsx/references/create.md similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/references/create.md rename to dataclaw-api/app/skills_builtin/minimax-xlsx/references/create.md diff --git a/backend/app/skills_builtin/minimax-xlsx/references/edit.md b/dataclaw-api/app/skills_builtin/minimax-xlsx/references/edit.md similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/references/edit.md rename to dataclaw-api/app/skills_builtin/minimax-xlsx/references/edit.md diff --git a/backend/app/skills_builtin/minimax-xlsx/references/fix.md b/dataclaw-api/app/skills_builtin/minimax-xlsx/references/fix.md similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/references/fix.md rename to dataclaw-api/app/skills_builtin/minimax-xlsx/references/fix.md diff --git a/backend/app/skills_builtin/minimax-xlsx/references/format.md b/dataclaw-api/app/skills_builtin/minimax-xlsx/references/format.md similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/references/format.md rename to dataclaw-api/app/skills_builtin/minimax-xlsx/references/format.md diff --git a/backend/app/skills_builtin/minimax-xlsx/references/ooxml-cheatsheet.md b/dataclaw-api/app/skills_builtin/minimax-xlsx/references/ooxml-cheatsheet.md similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/references/ooxml-cheatsheet.md rename to dataclaw-api/app/skills_builtin/minimax-xlsx/references/ooxml-cheatsheet.md diff --git a/backend/app/skills_builtin/minimax-xlsx/references/read-analyze.md b/dataclaw-api/app/skills_builtin/minimax-xlsx/references/read-analyze.md similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/references/read-analyze.md rename to dataclaw-api/app/skills_builtin/minimax-xlsx/references/read-analyze.md diff --git a/backend/app/skills_builtin/minimax-xlsx/references/validate.md b/dataclaw-api/app/skills_builtin/minimax-xlsx/references/validate.md similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/references/validate.md rename to dataclaw-api/app/skills_builtin/minimax-xlsx/references/validate.md diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/formula_check.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/formula_check.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/formula_check.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/formula_check.py diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/libreoffice_recalc.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/libreoffice_recalc.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/libreoffice_recalc.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/libreoffice_recalc.py diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/shared_strings_builder.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/shared_strings_builder.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/shared_strings_builder.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/shared_strings_builder.py diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/style_audit.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/style_audit.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/style_audit.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/style_audit.py diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_add_column.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_add_column.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_add_column.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_add_column.py diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_insert_row.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_insert_row.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_insert_row.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_insert_row.py diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_pack.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_pack.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_pack.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_pack.py diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_reader.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_reader.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_reader.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_reader.py diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_shift_rows.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_shift_rows.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_shift_rows.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_shift_rows.py diff --git a/backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_unpack.py b/dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_unpack.py similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/scripts/xlsx_unpack.py rename to dataclaw-api/app/skills_builtin/minimax-xlsx/scripts/xlsx_unpack.py diff --git a/backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/[Content_Types].xml b/dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/[Content_Types].xml similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/[Content_Types].xml rename to dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/[Content_Types].xml diff --git a/backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/_rels/.rels b/dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/_rels/.rels similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/_rels/.rels rename to dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/_rels/.rels diff --git a/backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels b/dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels rename to dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/_rels/workbook.xml.rels diff --git a/backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/sharedStrings.xml b/dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/sharedStrings.xml similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/sharedStrings.xml rename to dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/sharedStrings.xml diff --git a/backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/styles.xml b/dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/styles.xml similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/styles.xml rename to dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/styles.xml diff --git a/backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/workbook.xml b/dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/workbook.xml similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/workbook.xml rename to dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/workbook.xml diff --git a/backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml b/dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml similarity index 100% rename from backend/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml rename to dataclaw-api/app/skills_builtin/minimax-xlsx/templates/minimal_xlsx/xl/worksheets/sheet1.xml diff --git a/backend/app/skills_builtin/nl2sql/SKILL.md b/dataclaw-api/app/skills_builtin/nl2sql/SKILL.md similarity index 100% rename from backend/app/skills_builtin/nl2sql/SKILL.md rename to dataclaw-api/app/skills_builtin/nl2sql/SKILL.md diff --git a/backend/app/skills_builtin/pptx-generator/SKILL.md b/dataclaw-api/app/skills_builtin/pptx-generator/SKILL.md similarity index 100% rename from backend/app/skills_builtin/pptx-generator/SKILL.md rename to dataclaw-api/app/skills_builtin/pptx-generator/SKILL.md diff --git a/backend/app/skills_builtin/pptx-generator/references/design-system.md b/dataclaw-api/app/skills_builtin/pptx-generator/references/design-system.md similarity index 100% rename from backend/app/skills_builtin/pptx-generator/references/design-system.md rename to dataclaw-api/app/skills_builtin/pptx-generator/references/design-system.md diff --git a/backend/app/skills_builtin/pptx-generator/references/editing.md b/dataclaw-api/app/skills_builtin/pptx-generator/references/editing.md similarity index 100% rename from backend/app/skills_builtin/pptx-generator/references/editing.md rename to dataclaw-api/app/skills_builtin/pptx-generator/references/editing.md diff --git a/backend/app/skills_builtin/pptx-generator/references/pitfalls.md b/dataclaw-api/app/skills_builtin/pptx-generator/references/pitfalls.md similarity index 100% rename from backend/app/skills_builtin/pptx-generator/references/pitfalls.md rename to dataclaw-api/app/skills_builtin/pptx-generator/references/pitfalls.md diff --git a/backend/app/skills_builtin/pptx-generator/references/pptxgenjs.md b/dataclaw-api/app/skills_builtin/pptx-generator/references/pptxgenjs.md similarity index 100% rename from backend/app/skills_builtin/pptx-generator/references/pptxgenjs.md rename to dataclaw-api/app/skills_builtin/pptx-generator/references/pptxgenjs.md diff --git a/backend/app/skills_builtin/pptx-generator/references/slide-types.md b/dataclaw-api/app/skills_builtin/pptx-generator/references/slide-types.md similarity index 100% rename from backend/app/skills_builtin/pptx-generator/references/slide-types.md rename to dataclaw-api/app/skills_builtin/pptx-generator/references/slide-types.md diff --git a/backend/app/skills_builtin/visualization/SKILL.md b/dataclaw-api/app/skills_builtin/visualization/SKILL.md similarity index 100% rename from backend/app/skills_builtin/visualization/SKILL.md rename to dataclaw-api/app/skills_builtin/visualization/SKILL.md diff --git a/backend/app/tools/get_schema.py b/dataclaw-api/app/tools/get_schema.py similarity index 100% rename from backend/app/tools/get_schema.py rename to dataclaw-api/app/tools/get_schema.py diff --git a/backend/app/tools/knowledge_base.py b/dataclaw-api/app/tools/knowledge_base.py similarity index 100% rename from backend/app/tools/knowledge_base.py rename to dataclaw-api/app/tools/knowledge_base.py diff --git a/backend/app/tools/nl2sql.py b/dataclaw-api/app/tools/nl2sql.py similarity index 100% rename from backend/app/tools/nl2sql.py rename to dataclaw-api/app/tools/nl2sql.py diff --git a/backend/app/tools/subagent.py b/dataclaw-api/app/tools/subagent.py similarity index 100% rename from backend/app/tools/subagent.py rename to dataclaw-api/app/tools/subagent.py diff --git a/backend/app/tools/visualization.py b/dataclaw-api/app/tools/visualization.py similarity index 100% rename from backend/app/tools/visualization.py rename to dataclaw-api/app/tools/visualization.py diff --git a/backend/app/trace/__init__.py b/dataclaw-api/app/trace/__init__.py similarity index 100% rename from backend/app/trace/__init__.py rename to dataclaw-api/app/trace/__init__.py diff --git a/backend/app/trace/attributes.py b/dataclaw-api/app/trace/attributes.py similarity index 100% rename from backend/app/trace/attributes.py rename to dataclaw-api/app/trace/attributes.py diff --git a/backend/app/trace/service.py b/dataclaw-api/app/trace/service.py similarity index 100% rename from backend/app/trace/service.py rename to dataclaw-api/app/trace/service.py diff --git a/backend/hatch_build.py b/dataclaw-api/hatch_build.py similarity index 100% rename from backend/hatch_build.py rename to dataclaw-api/hatch_build.py diff --git a/backend/main.py b/dataclaw-api/main.py similarity index 97% rename from backend/main.py rename to dataclaw-api/main.py index d2578d1..3f81dd9 100644 --- a/backend/main.py +++ b/dataclaw-api/main.py @@ -122,7 +122,7 @@ def _resolve_webui_directory() -> Optional[Path]: source_webui = Path(__file__).resolve().parent / "app" / "webui" if source_webui.is_dir(): return source_webui - source_dist = Path(__file__).resolve().parent.parent / "frontend" / "dist" + source_dist = Path(__file__).resolve().parent.parent / "dataclaw-ui" / "dist" if source_dist.is_dir(): return source_dist return None @@ -156,7 +156,7 @@ async def shutdown_event(): async def read_root(): if _WEBUI_INDEX and _WEBUI_INDEX.is_file(): return FileResponse(path=str(_WEBUI_INDEX), media_type="text/html") - return {"Hello": "DataClaw Backend"} + return {"Hello": "全源灵动 Backend"} async def serve_webui_path(full_path: str, request: Request): @@ -193,7 +193,7 @@ def test_clickhouse(): return {"status": "success", "message": "Connected to ClickHouse"} raise HTTPException(status_code=500, detail="Failed to connect to ClickHouse") -@app.get("/nanobot/status") +@app.get("/agent-core/status") def nanobot_status(): if nanobot_service.agent: return {"status": "running", "model": nanobot_service.agent.model} @@ -239,7 +239,7 @@ def _decode_web_root(token: str) -> Path: return Path(decoded) -@app.get("/nanobot/artifacts/download") +@app.get("/agent-core/artifacts/download") def download_artifact(target: str = Query(...)): resolved = _resolve_checked_target(target) return FileResponse( @@ -249,7 +249,7 @@ def download_artifact(target: str = Query(...)): ) -@app.get("/nanobot/artifacts/preview") +@app.get("/agent-core/artifacts/preview") def preview_artifact(target: str = Query(...)): resolved = _resolve_checked_target(target) mime_type = _guess_mime_type(resolved) @@ -259,7 +259,7 @@ def preview_artifact(target: str = Query(...)): if suffix in {".html", ".htm"}: root_token = _encode_web_root(Path(resolved).parent) entry = Path(resolved).name - return RedirectResponse(url=f"/nanobot/artifacts/web/{root_token}/{entry}", status_code=307) + return RedirectResponse(url=f"/agent-core/artifacts/web/{root_token}/{entry}", status_code=307) return FileResponse( path=str(resolved), media_type=mime_type, @@ -268,7 +268,7 @@ def preview_artifact(target: str = Query(...)): ) -@app.get("/nanobot/artifacts/web/{root_token}/{resource_path:path}") +@app.get("/agent-core/artifacts/web/{root_token}/{resource_path:path}") def preview_web_artifact_resource(root_token: str, resource_path: str): root_dir = _decode_web_root(root_token) try: @@ -459,7 +459,7 @@ def _extract_reasoning_content(session_messages: List[Dict[str, Any]]) -> str: break return "" -@app.post("/nanobot/chat") +@app.post("/agent-core/chat") async def nanobot_chat(request: ChatRequest): try: _sync_session_project(request.session_id, request.project_id) @@ -539,7 +539,7 @@ async def nanobot_chat(request: ChatRequest): except Exception as e: raise HTTPException(status_code=500, detail=str(e)) -@app.post("/nanobot/chat/stream") +@app.post("/agent-core/chat/stream") async def nanobot_chat_stream(request: ChatRequest): async def event_generator(): current_task = None @@ -735,14 +735,14 @@ async def nanobot_chat_stream(request: ChatRequest): }, ) -@app.get("/nanobot/sessions") +@app.get("/agent-core/sessions") def get_sessions(project_id: Optional[int] = None): if not nanobot_service.agent: return session_alias_store.list_cached_sessions(project_id=project_id) sessions = nanobot_service.agent.sessions.list_sessions() return session_alias_store.sync_and_list(sessions, project_id=project_id) -@app.get("/nanobot/sessions/{session_id}") +@app.get("/agent-core/sessions/{session_id}") def get_session(session_id: str): if not nanobot_service.agent: raise HTTPException(status_code=400, detail="Nanobot not running") @@ -760,7 +760,7 @@ def get_session(session_id: str): class EnsureSessionRequest(BaseModel): project_id: Optional[int] = None -@app.post("/nanobot/sessions/{session_id}/ensure") +@app.post("/agent-core/sessions/{session_id}/ensure") def ensure_session(session_id: str, request: EnsureSessionRequest = EnsureSessionRequest()): if not nanobot_service.agent: raise HTTPException(status_code=400, detail="Nanobot not running") @@ -784,7 +784,7 @@ def ensure_session(session_id: str, request: EnsureSessionRequest = EnsureSessio "project_id": request.project_id } -@app.delete("/nanobot/sessions/{session_id}") +@app.delete("/agent-core/sessions/{session_id}") def delete_session(session_id: str): if not nanobot_service.agent: raise HTTPException(status_code=400, detail="Nanobot not running") @@ -801,7 +801,7 @@ def delete_session(session_id: str): raise HTTPException(status_code=404, detail="Session not found") -@app.post("/nanobot/sessions/batch-delete") +@app.post("/agent-core/sessions/batch-delete") def batch_delete_sessions(request: BatchDeleteRequest): if not nanobot_service.agent: raise HTTPException(status_code=400, detail="Nanobot not running") @@ -824,7 +824,7 @@ def batch_delete_sessions(request: BatchDeleteRequest): return {"status": "success", "deleted_count": len(deleted_ids), "deleted_ids": deleted_ids} -@app.put("/nanobot/sessions/{session_id}") +@app.put("/agent-core/sessions/{session_id}") def update_session(session_id: str, payload: SessionAliasUpdateRequest): updated = session_alias_store.update_alias_meta( session_key=session_id, @@ -836,7 +836,7 @@ def update_session(session_id: str, payload: SessionAliasUpdateRequest): return {"status": "success", **updated} -@app.put("/nanobot/sessions/{session_id}/context-file") +@app.put("/agent-core/sessions/{session_id}/context-file") def update_session_context_file(session_id: str, payload: SessionFileContextUpdateRequest): if not nanobot_service.agent: raise HTTPException(status_code=400, detail="Nanobot not running") diff --git a/backend/pyproject.toml b/dataclaw-api/pyproject.toml similarity index 92% rename from backend/pyproject.toml rename to dataclaw-api/pyproject.toml index a217b22..f2eb2b8 100644 --- a/backend/pyproject.toml +++ b/dataclaw-api/pyproject.toml @@ -32,6 +32,8 @@ dependencies = [ "passlib>=1.7.4", "prompt-toolkit>=3.0.50,<4.0.0", "psycopg2-binary>=2.9.11", + "pymysql>=1.1.3", + "cryptography>=42.0.0", "pydantic>=2.12.0,<3.0.0", "pydantic-settings>=2.12.0,<3.0.0", "pypdf2>=3.0.0", @@ -61,7 +63,7 @@ dependencies = [ [project.scripts] dataclaw = "app.cli:app" -nanobot = "nanobot.cli.commands:app" +nanobot = "agent-core.nanobot.cli.commands:app" [build-system] requires = ["hatchling"] @@ -80,3 +82,9 @@ packages = ["app"] [tool.hatch.build.hooks.custom] path = "hatch_build.py" +[dependency-groups] +dev = [ + "pytest>=9.0.3", + "pytest-asyncio>=1.3.0", +] + diff --git a/backend/tests/test_a2a_backend.py b/dataclaw-api/tests/test_a2a_backend.py similarity index 100% rename from backend/tests/test_a2a_backend.py rename to dataclaw-api/tests/test_a2a_backend.py diff --git a/backend/tests/test_artifact_endpoints.py b/dataclaw-api/tests/test_artifact_endpoints.py similarity index 100% rename from backend/tests/test_artifact_endpoints.py rename to dataclaw-api/tests/test_artifact_endpoints.py diff --git a/backend/tests/test_artifacts.py b/dataclaw-api/tests/test_artifacts.py similarity index 100% rename from backend/tests/test_artifacts.py rename to dataclaw-api/tests/test_artifacts.py diff --git a/backend/tests/test_chat_project_id.py b/dataclaw-api/tests/test_chat_project_id.py similarity index 100% rename from backend/tests/test_chat_project_id.py rename to dataclaw-api/tests/test_chat_project_id.py diff --git a/backend/tests/test_data_root.py b/dataclaw-api/tests/test_data_root.py similarity index 100% rename from backend/tests/test_data_root.py rename to dataclaw-api/tests/test_data_root.py diff --git a/backend/tests/test_dataclaw_cli.py b/dataclaw-api/tests/test_dataclaw_cli.py similarity index 100% rename from backend/tests/test_dataclaw_cli.py rename to dataclaw-api/tests/test_dataclaw_cli.py diff --git a/backend/tests/test_knowledge_base_backend.py b/dataclaw-api/tests/test_knowledge_base_backend.py similarity index 100% rename from backend/tests/test_knowledge_base_backend.py rename to dataclaw-api/tests/test_knowledge_base_backend.py diff --git a/backend/tests/test_nanobot_project_resolution.py b/dataclaw-api/tests/test_nanobot_project_resolution.py similarity index 100% rename from backend/tests/test_nanobot_project_resolution.py rename to dataclaw-api/tests/test_nanobot_project_resolution.py diff --git a/backend/tests/test_nl2sql.py b/dataclaw-api/tests/test_nl2sql.py similarity index 100% rename from backend/tests/test_nl2sql.py rename to dataclaw-api/tests/test_nl2sql.py diff --git a/dataclaw-api/tests/test_nl2sql_optimized.py b/dataclaw-api/tests/test_nl2sql_optimized.py new file mode 100644 index 0000000..fea7e11 --- /dev/null +++ b/dataclaw-api/tests/test_nl2sql_optimized.py @@ -0,0 +1,68 @@ +import pytest +import json +from unittest.mock import AsyncMock, MagicMock, patch +from app.agent.nl2sql import process_nl2sql, NL2SQLRequest, NL2SQLResponse + +@pytest.mark.asyncio +async def test_nl2sql_optimized_flow(): + # Mock parameters + query = "Show me the top 5 sales" + source = "ds:1" + + # Mock connector and schema + mock_connector = MagicMock() + mock_connector.get_schema.return_value = { + "sales": {"columns": [{"name": "id", "type": "INT"}, {"name": "amount", "type": "DECIMAL"}]}, + "users": {"columns": [{"name": "id", "type": "INT"}, {"name": "name", "type": "TEXT"}]}, + "logs": {"columns": [{"name": "id", "type": "INT"}, {"name": "event", "type": "TEXT"}]}, + "products": {"columns": [{"name": "id", "type": "INT"}]}, + "categories": {"columns": [{"name": "id", "type": "INT"}]}, + "inventory": {"columns": [{"name": "id", "type": "INT"}]} + } + mock_connector.test_connection.return_value = True + mock_connector.execute_query.return_value = [{"id": 1, "amount": 100}] + + # Mock LLM provider + mock_provider = AsyncMock() + + # First response for Table Selector + mock_resp_tables = MagicMock() + mock_resp_tables.content = '["sales"]' + mock_resp_tables.finish_reason = "stop" + + # Second response for SQL Generation + mock_resp_sql = MagicMock() + mock_resp_sql.content = '{"reasoning": "Plan...", "sql": "SELECT * FROM sales LIMIT 5"}' + mock_resp_sql.finish_reason = "stop" + + mock_provider.chat.side_effect = [mock_resp_tables, mock_resp_sql] + + # Patch dependencies + with patch("app.agent.nl2sql.get_active_llm_config", return_value={"model": "gpt-4"}), \ + patch("app.agent.nl2sql.build_llm_provider", return_value=mock_provider), \ + patch("app.agent.nl2sql.get_connector", return_value=mock_connector), \ + patch("app.agent.nl2sql.SessionLocal"), \ + patch("app.agent.nl2sql.DataSource"), \ + patch("app.agent.nl2sql.postgres_connector", mock_connector), \ + patch("app.agent.nl2sql._check_connection_with_cache", return_value=True): + + request = NL2SQLRequest(query=query, source=source) + response = await process_nl2sql(request) + + print(f"DEBUG: Response SQL: '{response.sql}'") + print(f"DEBUG: Response Error: '{response.error}'") + + assert response.sql == "SELECT * FROM sales LIMIT 5" + assert len(response.result) == 1 + assert response.error is None + + # Verify provider was called twice + assert mock_provider.chat.call_count == 2 + + # Verify first call was for table selection + args, kwargs = mock_provider.chat.call_args_list[0] + assert "TABLE_SELECTOR_SYSTEM_PROMPT" in str(args) or "Identifying relevant tables" in str(args) or any("system" in m["role"] for m in kwargs["messages"]) + +if __name__ == "__main__": + import asyncio + asyncio.run(test_nl2sql_optimized_flow()) diff --git a/backend/tests/test_patched_openai_compat_provider.py b/dataclaw-api/tests/test_patched_openai_compat_provider.py similarity index 100% rename from backend/tests/test_patched_openai_compat_provider.py rename to dataclaw-api/tests/test_patched_openai_compat_provider.py diff --git a/backend/tests/test_subagent_detail_route.py b/dataclaw-api/tests/test_subagent_detail_route.py similarity index 100% rename from backend/tests/test_subagent_detail_route.py rename to dataclaw-api/tests/test_subagent_detail_route.py diff --git a/backend/tests/test_subagent_tools_e2e_regression.py b/dataclaw-api/tests/test_subagent_tools_e2e_regression.py similarity index 100% rename from backend/tests/test_subagent_tools_e2e_regression.py rename to dataclaw-api/tests/test_subagent_tools_e2e_regression.py diff --git a/backend/tests/test_trace_integration.py b/dataclaw-api/tests/test_trace_integration.py similarity index 100% rename from backend/tests/test_trace_integration.py rename to dataclaw-api/tests/test_trace_integration.py diff --git a/backend/tests/test_webui_static_hosting.py b/dataclaw-api/tests/test_webui_static_hosting.py similarity index 100% rename from backend/tests/test_webui_static_hosting.py rename to dataclaw-api/tests/test_webui_static_hosting.py diff --git a/backend/uv.lock b/dataclaw-api/uv.lock similarity index 99% rename from backend/uv.lock rename to dataclaw-api/uv.lock index 3246f84..12e471e 100644 --- a/backend/uv.lock +++ b/dataclaw-api/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.11" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -669,6 +669,7 @@ dependencies = [ { name = "chardet" }, { name = "clickhouse-driver" }, { name = "croniter" }, + { name = "cryptography" }, { name = "ddgs" }, { name = "dingtalk-stream" }, { name = "duckdb" }, @@ -693,6 +694,7 @@ dependencies = [ { name = "psycopg2-binary" }, { name = "pydantic" }, { name = "pydantic-settings" }, + { name = "pymysql" }, { name = "pypdf2" }, { name = "python-docx" }, { name = "python-dotenv" }, @@ -718,6 +720,12 @@ dependencies = [ { name = "websockets" }, ] +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-asyncio" }, +] + [package.metadata] requires-dist = [ { name = "anthropic", specifier = ">=0.45.0,<1.0.0" }, @@ -725,6 +733,7 @@ requires-dist = [ { name = "chardet", specifier = ">=3.0.2,<6.0.0" }, { name = "clickhouse-driver", specifier = ">=0.2.10" }, { name = "croniter", specifier = ">=6.0.0,<7.0.0" }, + { name = "cryptography", specifier = ">=42.0.0" }, { name = "ddgs", specifier = ">=9.5.5,<10.0.0" }, { name = "dingtalk-stream", specifier = ">=0.24.0,<1.0.0" }, { name = "duckdb", specifier = ">=1.5.0" }, @@ -749,6 +758,7 @@ requires-dist = [ { name = "psycopg2-binary", specifier = ">=2.9.11" }, { name = "pydantic", specifier = ">=2.12.0,<3.0.0" }, { name = "pydantic-settings", specifier = ">=2.12.0,<3.0.0" }, + { name = "pymysql", specifier = ">=1.1.3" }, { name = "pypdf2", specifier = ">=3.0.0" }, { name = "python-docx", specifier = ">=1.2.0" }, { name = "python-dotenv", specifier = ">=1.0.1" }, @@ -774,6 +784,12 @@ requires-dist = [ { name = "websockets", specifier = ">=16.0,<17.0" }, ] +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=9.0.3" }, + { name = "pytest-asyncio", specifier = ">=1.3.0" }, +] + [[package]] name = "ddgs" version = "9.12.0" @@ -1207,6 +1223,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, ] +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + [[package]] name = "jinja2" version = "3.1.6" @@ -2370,6 +2395,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, ] +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + [[package]] name = "primp" version = "1.2.0" @@ -2783,6 +2817,15 @@ crypto = [ { name = "cryptography" }, ] +[[package]] +name = "pymysql" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/ec/8d45c920e90445f0b75c590b32851853ed319763b0d8dff8d283052da8cf/pymysql-1.1.3.tar.gz", hash = "sha256:e70ebf2047a4edf6138cf79c68ad418ef620af65900aa585c5e8bfc95044d43a", size = 48207, upload-time = "2026-05-01T09:09:54.532Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/dc/9085f3d6f497e9b25fb40d6e8ecef3ddbb5cf977a949b933624a299f5c16/pymysql-1.1.3-py3-none-any.whl", hash = "sha256:8164ba62c552f6105f3b11753352d0f16b90d1703ba67d81923d5a8a5d1c5289", size = 45356, upload-time = "2026-05-01T09:09:53.316Z" }, +] + [[package]] name = "pypdf2" version = "3.0.1" @@ -2792,6 +2835,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/5e/c86a5643653825d3c913719e788e41386bee415c2b87b4f955432f2de6b2/pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928", size = 232572, upload-time = "2022-12-31T10:36:10.327Z" }, ] +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" diff --git a/frontend/.gitignore b/dataclaw-ui/.gitignore similarity index 100% rename from frontend/.gitignore rename to dataclaw-ui/.gitignore diff --git a/dataclaw-ui/Dockerfile b/dataclaw-ui/Dockerfile new file mode 100644 index 0000000..d4138cc --- /dev/null +++ b/dataclaw-ui/Dockerfile @@ -0,0 +1,16 @@ +# Frontend Dockerfile +FROM node:20-slim AS build + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . +RUN npm run build + +FROM nginx:stable-alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/README.md b/dataclaw-ui/README.md similarity index 100% rename from frontend/README.md rename to dataclaw-ui/README.md diff --git a/frontend/components.json b/dataclaw-ui/components.json similarity index 100% rename from frontend/components.json rename to dataclaw-ui/components.json diff --git a/frontend/eslint.config.js b/dataclaw-ui/eslint.config.js similarity index 100% rename from frontend/eslint.config.js rename to dataclaw-ui/eslint.config.js diff --git a/frontend/index.html b/dataclaw-ui/index.html similarity index 96% rename from frontend/index.html rename to dataclaw-ui/index.html index 1c30fb6..ed3afb7 100644 --- a/frontend/index.html +++ b/dataclaw-ui/index.html @@ -4,7 +4,7 @@ - DataClaw + 全源灵动