-
-
-
-## 🏗️ 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: