diff --git a/.gitignore b/.gitignore index 97d85df..8954a55 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,6 @@ frontend/.env scratch/ v1.2 - Copy/ inv dash/ -bot/ voice/ # ---- Machine-local compose overrides (LAN phone access, etc.) ---- diff --git a/ai procurement/.env.example b/bot/.env.example similarity index 100% rename from ai procurement/.env.example rename to bot/.env.example diff --git a/ai procurement/.gitignore b/bot/.gitignore similarity index 100% rename from ai procurement/.gitignore rename to bot/.gitignore diff --git a/ai procurement/Dockerfile b/bot/Dockerfile similarity index 100% rename from ai procurement/Dockerfile rename to bot/Dockerfile diff --git a/bot/INTEGRATION-REPORT.md b/bot/INTEGRATION-REPORT.md new file mode 100644 index 0000000..042d62a --- /dev/null +++ b/bot/INTEGRATION-REPORT.md @@ -0,0 +1,150 @@ +# گزارش ادغام ایجنت هوش مصنوعی با ریپوی ERP + +> این سند وضعیت نهایی یکپارچه‌سازی بات تلگرام/بله (procurement AI agent) با پروژه‌ی ERP را +> توضیح می‌دهد: مشکلی که پیش آمده بود، چه چیزی اصلاح شد، وضعیت فعلی گیت و سرور، +> نکات امنیتی و نحوه‌ی استفاده. تاریخ آخرین به‌روزرسانی: برنچ `prod-v7`. + +--- + +## ۱. مشکل چی بود؟ + +سرویس `bot` در `docker-compose.yml` این‌طور تعریف شده بود: + +```yaml +bot: + build: + context: ./bot # ← پوشه‌ای که در گیت وجود نداشت! + volumes: + - ./bot/app:/srv/bot/app +``` + +اما: + +1. **پوشه‌ی `bot/` هیچ‌وقت کامیت نشده بود** — نه روی `prod-v7` و نه روی هیچ برنچ دیگری. +2. کد ایجنت در پوشه‌ای به نام `ai procurement/` (با فاصله در نام) زندگی می‌کرد که روی `prod-v7` کامیت شده بود، ولی compose به آن اشاره نمی‌کرد. +3. روی دستگاه reza.g یک **Windows Junction** (میان‌بر سطح NTFS) به نام `bot` وجود داشت که به `ai procurement` اشاره می‌کرد — به همین دلیل `docker compose build bot` فقط روی ماشین او جواب می‌داد. +4. خود برنچ `prod-v7` هم `bot/` را در `.gitignore` (خط ۳۳) داشت. + +**نتیجه:** روی هر کلون تمیزی از مخزن، `docker compose build bot` شکست می‌خورد — +همان چیزی که تیم گزارش کرد: «bot/ was never committed — prod-v7's bot is un-buildable from the repo.» + +## ۲. راه‌حل اعمال‌شده (راه‌حل شماره ۲) + +1. Junction ویندوزی `bot` حذف شد (فقط میان‌بر؛ فایلی از دست نرفت). +2. پوشه‌ی `ai procurement/` به **`bot/`** تغییر نام یافت — با `git mv`، یعنی + **تاریخچه‌ی گیت هر ۴۱ فایل به‌صورت rename خالص حفظ شده** (`R` در گیت، نه حذف/افزودن). +3. خط `bot/` از `.gitignore` ریشه حذف شد تا پوشه‌ی بات دیگر ignore نشود. + (`voice/` سر جایش ماند — نت‌های صوتی رuntimes هستند و نباید کامیت شوند.) +4. اکنون `docker-compose.yml` **بدون هیچ تغییری** درست کار می‌کند: + `context: ./bot` و `./bot/app` دقیقاً به فایل‌های کامیت‌شده اشاره می‌کنند. +5. همه‌چیز روی برنچ **`prod-v7`** کامیت و پوش شده تا تیم به فایل‌ها دسترسی داشته باشد. + +### قواعد ignore فعلی (عمدی) + +| مسیر | وضعیت | دلیل | +|---|---|---| +| `bot/.env` | ignore (توسط `bot/.gitignore`) | کلیدها و توکن‌ها — هرگز کامیت نمی‌شوند | +| `bot/__pycache__/` و `*.pyc` | ignore | خروجی runtime پایتون | +| `voice/` | ignore (ریشه) | فایل‌های صوتی دریافتی از بله در زمان اجرا | +| `procurement_ai_agent.bundle` و `git_history_backup.zip` | **کامیت‌شده** | بکاپ تاریخچه‌ی اصلی ایجنت (~۲۰۰KB) | + +## ۳. وضعیت فعلی گیت (تاریخ این سند) + +- برنچ فعال: **`prod-v7`** (محلی و `origin/prod-v7` هم‌SHA). +- درخت `prod-v7` = کل اپلیکیشن ERP (backend/frontend/…) **به‌علاوه‌ی** `bot/` + (کد ایجنت + Dockerfile + docs + integration) + `backend/app/routers/drafts.py` + (اندپوینت‌های همگام‌سازی draft بات ↔ ویزارد) + اسکیمای `bot_drafts`. +- تاریخچه: از production (d69ec7f) یازده کامیت جلوتر — از «feat(drafts): bot_drafts schema» + تا «feat(bot,wizard): smart message filtering…». +- بقیه‌ی برنچ‌های تیم (`prod-v8`، `prod-v9`، `prod_v8`، `production`) **فایل‌های بات را ندارند** — + ادغام این برنچ با کار جدید تیم، قدم بعدی جداگانه است (با تأیید تیم). + +## ۴. ⚠️ نکته‌ی امنیتی (مهم — اقدام لازم) + +در همان `docker-compose.yml`، مقادیر **واقعی** به‌عنوان پیش‌فرض env کامیت و پوش شده‌اند: + +```yaml +PROCUREMENT_BOT_BOT_TOKEN: ${BALE_BOT_TOKEN:-1354444845:yhVMnKZBFuKaU3UW5fP1bHmknEHx99mipWw} +PROCUREMENT_BOT_AI_API_KEY: ${AI_API_KEY:-sk-a671eab15be846d494bf0839070a3b32} +``` + +یعنی توکن بات بله و کلید DeepSeek الان در تاریخچه‌ی مخزن مشترک است و **هر کسی که به ریپو +دسترسی دارد می‌تواند آن‌ها را ببیند** (`.env` کامیت نشده، ولی این پیش‌فرض‌ها عملاً همان لو رفتن راز است). + +اقدامات توصیه‌شده (هنوز انجام نشده — نیاز به تصمیم مالک توکن‌ها): + +1. **توکن بات بله را از @BotFather بله باطل و دوباره بسازید** و کلید DeepSeek را rotate کنید. +2. مقادیر پیش‌فرض را از compose حذف کنید و به‌جای `${VAR:-secret}` از `${VAR:?set VAR}` استفاده کنید + (مثل `TRAEFIK_ACME_EMAIL` در compose پروडاکشن) و مقادیر واقعی فقط در `.env` محلی/سرور بمانند. +3. فایل `bot/.env` را از روی `bot/.env.example` بسازید (نمونه در همان پوشه هست). + +## ۵. نحوه‌ی استفاده + +### ۵.۱ اجرای کامل استک (شامل بات) — روی هر ماشین + +```powershell +git clone https://git.bennu-api-bahrestan.ir/ali_mohaghegh/ERP.git +cd ERP +git checkout prod-v7 + +# ۱) متغیرهای محیطی بات را بسازید (بدون این، بات توکن ندارد): +copy bot\.env.example bot\.env # و مقادیر واقعی را داخلش بگذارید +# BALE_BOT_TOKEN=... توکن بات از بله +# AI_API_KEY=... کلید سرویس LLM +# BALE_ADMIN_CHAT_IDS=... چت‌آیدی مدیران + +# ۲) کل استک + بات: +docker compose up -d --build +``` + +سرویس `bot` (کانتینر `procurement-bot`) به‌صورت long-polling به `tapi.bale.ai` وصل می‌شود، +به `api:8000/api/v1` (همان ERP) و پایگاه‌داده‌ی `procurement` وصل است و +draft های گفتگو را در جدول `bot_drafts` می‌نویسد. + +### ۵.۲ اجرای فقط بات (وقتی استک از قبل بالا است) + +```powershell +docker compose build bot +docker compose up -d bot +docker compose logs -f bot +``` + +### ۵.۳ جریان کاری کاربر نهایی (بات ↔ ERP) + +1. کاربر در بله به بات پیام می‌دهد (متن یا وویس — وویس با STT به متن تبدیل می‌شود). +2. بات با NLU موجودی/تأمین‌کننده/کالا را استخراج می‌کند و یک **draft** می‌سازد + (وضعیت `active` در جدول `bot_drafts`) و کارت پیش‌نمایش با فیلدهای خالی می‌فرستد. +3. کاربر با دکمه‌ی ✅ تأیید می‌کند؛ بات draft را به ERP کامیت می‌کند + (از طریق REST: `POST /api/v1/drafts/{id}/commit` با توکن سرویس). +4. ویزارد فرانت‌اند همان draft را از `GET /api/v1/drafts` می‌خواند و پیش‌پر می‌کند — + همگام‌سازی دوطرفه با ذخیره‌ی خودکار (debounced) بین مرورگر و بات. +5. ویرایشِ پاسخِ کاربر به کارت قبلی بات هم پشتیبانی می‌شود (reply-based edit، بدون کارت تکراری). + +### ۵.۴ تست سریع سلامت + +```powershell +docker compose ps bot # باید Up باشد +docker compose logs bot | Select-Object -Last 20 +# داخل کانتینر: +docker compose exec bot python -m tools.smoke_test # اگر توکن ست باشد +``` + +### ۵.۵ مسیرهای مهم + +| مسیر | محتوا | +|---|---| +| `bot/app/` | کد بات (handlers برای quote/material/supplier/decisions، bale client، NLU، STT، sync) | +| `bot/Dockerfile` | ایمیج `python:3.13-slim` + ffmpeg (برای وویس) | +| `bot/docs/SETUP.md` | راه‌اندازی جزئی‌تر ایجنت | +| `bot/integration/` | طرح ادغام: نمونه سرویس compose + پچ `X-Actor-Id` | +| `backend/app/routers/drafts.py` | REST drafts (سمت ERP) | +| `bot/procurement_ai_agent.bundle` و `bot/git_history_backup.zip` | بکاپ تاریخچه‌ی مستقل قبلی ایجنت | + +## ۶. نکات باقی‌مانده برای تیم + +- **ادغام با برنچ‌های جدید تیم:** فایل‌های بات فقط روی `prod-v7` هستند؛ `prod-v8`/`prod-v9` + آن‌ها را ندارند. برای رسیدن به استقرار نهایی، این برنچ باید با کار تیم merge شود. +- **سرویس bot در `docker-compose.prod.yml` و `docker-compose.coolify.yml` نیست** — + برای استقرار سرور باید به overlay پروڈ اضافه شود (یا از همان base compose استفاده شود). +- نام قدیمی `ai procurement` در برخی مستندات (`bot/docs/SETUP.md`، `bot/integration/`) + ممکن است هنوز دیده شود — محتوایی است و عملکردی تحت تأثیر نیست. diff --git a/ai procurement/README.md b/bot/README.md similarity index 100% rename from ai procurement/README.md rename to bot/README.md diff --git a/ai procurement/app/__init__.py b/bot/app/__init__.py similarity index 100% rename from ai procurement/app/__init__.py rename to bot/app/__init__.py diff --git a/ai procurement/app/api/__init__.py b/bot/app/api/__init__.py similarity index 100% rename from ai procurement/app/api/__init__.py rename to bot/app/api/__init__.py diff --git a/ai procurement/app/api/client.py b/bot/app/api/client.py similarity index 100% rename from ai procurement/app/api/client.py rename to bot/app/api/client.py diff --git a/ai procurement/app/bale/__init__.py b/bot/app/bale/__init__.py similarity index 100% rename from ai procurement/app/bale/__init__.py rename to bot/app/bale/__init__.py diff --git a/ai procurement/app/bale/client.py b/bot/app/bale/client.py similarity index 100% rename from ai procurement/app/bale/client.py rename to bot/app/bale/client.py diff --git a/ai procurement/app/config.py b/bot/app/config.py similarity index 100% rename from ai procurement/app/config.py rename to bot/app/config.py diff --git a/ai procurement/app/db.py b/bot/app/db.py similarity index 100% rename from ai procurement/app/db.py rename to bot/app/db.py diff --git a/ai procurement/app/db_init.py b/bot/app/db_init.py similarity index 100% rename from ai procurement/app/db_init.py rename to bot/app/db_init.py diff --git a/ai procurement/app/drafts.py b/bot/app/drafts.py similarity index 100% rename from ai procurement/app/drafts.py rename to bot/app/drafts.py diff --git a/ai procurement/app/handlers/__init__.py b/bot/app/handlers/__init__.py similarity index 100% rename from ai procurement/app/handlers/__init__.py rename to bot/app/handlers/__init__.py diff --git a/ai procurement/app/handlers/callbacks.py b/bot/app/handlers/callbacks.py similarity index 100% rename from ai procurement/app/handlers/callbacks.py rename to bot/app/handlers/callbacks.py diff --git a/ai procurement/app/handlers/commands.py b/bot/app/handlers/commands.py similarity index 100% rename from ai procurement/app/handlers/commands.py rename to bot/app/handlers/commands.py diff --git a/ai procurement/app/handlers/decisions.py b/bot/app/handlers/decisions.py similarity index 100% rename from ai procurement/app/handlers/decisions.py rename to bot/app/handlers/decisions.py diff --git a/ai procurement/app/handlers/links.py b/bot/app/handlers/links.py similarity index 100% rename from ai procurement/app/handlers/links.py rename to bot/app/handlers/links.py diff --git a/ai procurement/app/handlers/material.py b/bot/app/handlers/material.py similarity index 100% rename from ai procurement/app/handlers/material.py rename to bot/app/handlers/material.py diff --git a/ai procurement/app/handlers/queries.py b/bot/app/handlers/queries.py similarity index 100% rename from ai procurement/app/handlers/queries.py rename to bot/app/handlers/queries.py diff --git a/ai procurement/app/handlers/quote.py b/bot/app/handlers/quote.py similarity index 100% rename from ai procurement/app/handlers/quote.py rename to bot/app/handlers/quote.py diff --git a/ai procurement/app/handlers/supplier.py b/bot/app/handlers/supplier.py similarity index 100% rename from ai procurement/app/handlers/supplier.py rename to bot/app/handlers/supplier.py diff --git a/ai procurement/app/keyboards.py b/bot/app/keyboards.py similarity index 100% rename from ai procurement/app/keyboards.py rename to bot/app/keyboards.py diff --git a/ai procurement/app/main.py b/bot/app/main.py similarity index 100% rename from ai procurement/app/main.py rename to bot/app/main.py diff --git a/ai procurement/app/models.py b/bot/app/models.py similarity index 100% rename from ai procurement/app/models.py rename to bot/app/models.py diff --git a/ai procurement/app/nlu.py b/bot/app/nlu.py similarity index 100% rename from ai procurement/app/nlu.py rename to bot/app/nlu.py diff --git a/ai procurement/app/resolve.py b/bot/app/resolve.py similarity index 100% rename from ai procurement/app/resolve.py rename to bot/app/resolve.py diff --git a/ai procurement/app/runtime.py b/bot/app/runtime.py similarity index 100% rename from ai procurement/app/runtime.py rename to bot/app/runtime.py diff --git a/ai procurement/app/security.py b/bot/app/security.py similarity index 100% rename from ai procurement/app/security.py rename to bot/app/security.py diff --git a/ai procurement/app/storage.py b/bot/app/storage.py similarity index 100% rename from ai procurement/app/storage.py rename to bot/app/storage.py diff --git a/ai procurement/app/stt.py b/bot/app/stt.py similarity index 100% rename from ai procurement/app/stt.py rename to bot/app/stt.py diff --git a/ai procurement/app/sync.py b/bot/app/sync.py similarity index 100% rename from ai procurement/app/sync.py rename to bot/app/sync.py diff --git a/ai procurement/app/texts.py b/bot/app/texts.py similarity index 100% rename from ai procurement/app/texts.py rename to bot/app/texts.py diff --git a/ai procurement/docs/SETUP.md b/bot/docs/SETUP.md similarity index 100% rename from ai procurement/docs/SETUP.md rename to bot/docs/SETUP.md diff --git a/ai procurement/git_history_backup.zip b/bot/git_history_backup.zip similarity index 100% rename from ai procurement/git_history_backup.zip rename to bot/git_history_backup.zip diff --git a/ai procurement/integration/README.md b/bot/integration/README.md similarity index 100% rename from ai procurement/integration/README.md rename to bot/integration/README.md diff --git a/ai procurement/integration/erp-compose-service.yml b/bot/integration/erp-compose-service.yml similarity index 100% rename from ai procurement/integration/erp-compose-service.yml rename to bot/integration/erp-compose-service.yml diff --git a/ai procurement/integration/x-actor-id.patch b/bot/integration/x-actor-id.patch similarity index 100% rename from ai procurement/integration/x-actor-id.patch rename to bot/integration/x-actor-id.patch diff --git a/ai procurement/procurement_ai_agent.bundle b/bot/procurement_ai_agent.bundle similarity index 100% rename from ai procurement/procurement_ai_agent.bundle rename to bot/procurement_ai_agent.bundle diff --git a/ai procurement/requirements.txt b/bot/requirements.txt similarity index 100% rename from ai procurement/requirements.txt rename to bot/requirements.txt diff --git a/ai procurement/tools/smoke_test.py b/bot/tools/smoke_test.py similarity index 100% rename from ai procurement/tools/smoke_test.py rename to bot/tools/smoke_test.py