diff --git a/.env.example b/.env.example index b875008..a7a15bd 100644 --- a/.env.example +++ b/.env.example @@ -17,6 +17,21 @@ DEEPL_AUTH_KEY= # API Service Configuration API_HOST=0.0.0.0 API_PORT=6002 +INDEXER_HOST=0.0.0.0 +INDEXER_PORT=6004 + +# Optional service ports +EMBEDDING_PORT=6005 +TRANSLATION_PORT=6006 +RERANKER_PORT=6007 +EMBEDDING_SERVICE_URL=http://127.0.0.1:6005 +TRANSLATION_SERVICE_URL=http://127.0.0.1:6006 +RERANKER_SERVICE_URL=http://127.0.0.1:6007/rerank + +# Optional startup switches (run.sh / scripts/service_ctl.sh) +START_EMBEDDING=0 +START_TRANSLATOR=0 +START_RERANKER=0 # Embedding Models TEXT_MODEL_DIR=/data/tw/models/bge-m3 # 已经改为web请求了,不使用本地模型 diff --git a/README.md b/README.md index 573f9d7..ec760ba 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ python scripts/recreate_and_import.py \ | 步骤 | 去哪里看 | 摘要 | |------|---------|------| | 1. 准备环境 | `docs/环境配置说明.md` / `Usage-Guide.md` | Conda、`activate.sh`、依赖、ES/MySQL、`.env` | -| 2. 构造测试数据 | `测试数据指南.md` | Tenant1 Mock、Tenant2 CSV、`mock_data.sh` / `ingest.sh` | +| 2. 构造测试数据 | `测试数据指南.md` | Tenant1 Mock、Tenant2 CSV、`mock_data.sh` | | 3. 启动与验证 | `Usage-Guide.md` | `run.sh` 一键启动、分步脚本、日志与健康检查 | | 4. 理解架构 | `系统设计文档.md` | 数据流、配置系统、查询/搜索/索引模块 | | 5. 接入搜索 API | `搜索API对接指南.md` / `搜索API速查表.md` | REST 端点、参数、响应、最佳实践 | @@ -173,19 +173,21 @@ docker run -d --name es -p 9200:9200 elasticsearch:8.11.0 # 2. 构造测试数据并导入 MySQL ./scripts/mock_data.sh # 详见 TEST_DATA_GUIDE.md -# 3. 从 MySQL 注入到 Elasticsearch -./scripts/ingest.sh 1 true -./scripts/ingest.sh 2 true +# 3. 创建租户索引结构并导入数据(推荐) +./scripts/create_tenant_index.sh 162 +curl -X POST "http://localhost:6004/indexer/reindex" \ + -H "Content-Type: application/json" \ + -d '{"tenant_id":"162","batch_size":500}' -# 4. 启动服务 +# 4. 启动核心服务(backend/indexer/frontend) ./run.sh -# (可选)启动本地向量服务(BGE-M3 / CN-CLIP,本地模型推理) -# 提供: POST http://localhost:6005/embed/text -# POST http://localhost:6005/embed/image -./scripts/start_embedding_service.sh +# (可选)附加启动 embedding / translator / reranker +START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh # -# 详细说明见:`embeddings/README.md` +# 查看服务状态 / 停止 +./scripts/service_ctl.sh status +./scripts/stop.sh # 5. 调用文本搜索 API curl -X POST http://localhost:6002/search/ \ @@ -199,6 +201,7 @@ curl -X POST http://localhost:6002/search/ \ | 文档 | 内容提要 | 适用场景 | |------|----------|----------| | `docs/环境配置说明.md` | 系统要求、`activate.sh`、Conda/依赖、外部服务、CONDA_ROOT | 首次部署、新机器环境 | +| `docs/SERVICE_MATRIX.md` | 服务分层、端口、统一启动/停止入口 | 运维值守、联调启动 | | `Usage-Guide.md` | 环境准备、服务启动、配置、日志、验证手册 | 日常运维、调试 | | `基础配置指南.md` | 统一硬编码配置说明、索引结构、查询配置 | 了解系统配置、修改配置 | | `测试数据指南.md` | 两个租户的模拟/CSV 数据构造 & MySQL→ES 流程 | 数据准备、联调 | @@ -220,7 +223,7 @@ curl -X POST http://localhost:6002/search/ \ - **数据构建 → MySQL → Elasticsearch** - `scripts/mock_data.sh`:Tenant1 Mock + Tenant2 CSV 一条龙 - - `scripts/ingest.sh [recreate]`:驱动 `indexer/` 模块写入 `search_products` + - `scripts/create_tenant_index.sh ` + `POST /indexer/reindex`:推荐导入链路 - 详解:`测试数据指南.md` - **索引富化 & Java 对接** diff --git a/config/env_config.py b/config/env_config.py index 398a767..3b5b722 100644 --- a/config/env_config.py +++ b/config/env_config.py @@ -55,10 +55,26 @@ DASHSCOPE_API_KEY = os.getenv('DASHSCOPE_API_KEY') # API Service Configuration API_HOST = os.getenv('API_HOST', '0.0.0.0') API_PORT = int(os.getenv('API_PORT', 6002)) +# Indexer service +INDEXER_HOST = os.getenv('INDEXER_HOST', '0.0.0.0') +INDEXER_PORT = int(os.getenv('INDEXER_PORT', 6004)) +# Optional dependent services +EMBEDDING_HOST = os.getenv('EMBEDDING_HOST', '127.0.0.1') +EMBEDDING_PORT = int(os.getenv('EMBEDDING_PORT', 6005)) +TRANSLATION_HOST = os.getenv('TRANSLATION_HOST', '127.0.0.1') +TRANSLATION_PORT = int(os.getenv('TRANSLATION_PORT', os.getenv('TRANSLATOR_PORT', 6006))) +RERANKER_HOST = os.getenv('RERANKER_HOST', '127.0.0.1') +RERANKER_PORT = int(os.getenv('RERANKER_PORT', 6007)) # API_BASE_URL: 如果未设置,根据API_HOST构建(0.0.0.0使用localhost) API_BASE_URL = os.getenv('API_BASE_URL') if not API_BASE_URL: API_BASE_URL = f'http://localhost:{API_PORT}' if API_HOST == '0.0.0.0' else f'http://{API_HOST}:{API_PORT}' +INDEXER_BASE_URL = os.getenv('INDEXER_BASE_URL') or ( + f'http://localhost:{INDEXER_PORT}' if INDEXER_HOST == '0.0.0.0' else f'http://{INDEXER_HOST}:{INDEXER_PORT}' +) +EMBEDDING_SERVICE_URL = os.getenv('EMBEDDING_SERVICE_URL') or f'http://{EMBEDDING_HOST}:{EMBEDDING_PORT}' +TRANSLATION_SERVICE_URL = os.getenv('TRANSLATION_SERVICE_URL') or f'http://{TRANSLATION_HOST}:{TRANSLATION_PORT}' +RERANKER_SERVICE_URL = os.getenv('RERANKER_SERVICE_URL') or f'http://{RERANKER_HOST}:{RERANKER_PORT}/rerank' # Model Directories TEXT_MODEL_DIR = os.getenv('TEXT_MODEL_DIR', '/data/tw/models/bge-m3') diff --git a/docs/ES/ES_9 b/docs/ES/ES_9 index 7a45ec0..829b32f 100644 --- a/docs/ES/ES_9 +++ b/docs/ES/ES_9 @@ -244,7 +244,7 @@ http://:5601 - `ES_HOST=http://127.0.0.1:9200` - 按你项目已有脚本执行索引初始化和数据导入(在 9.3 上推荐使用 bfloat16 版 mapping): - 创建索引:使用 `mappings/search_products.json`(bfloat16 版本) - - 执行你现有的数据导入脚本:`./scripts/ingest.sh true` 或 `python main.py ingest ...` + - 推荐导入流程:`./scripts/create_tenant_index.sh ` + `POST /indexer/reindex` --- diff --git a/docs/SERVICE_MATRIX.md b/docs/SERVICE_MATRIX.md new file mode 100644 index 0000000..a634298 --- /dev/null +++ b/docs/SERVICE_MATRIX.md @@ -0,0 +1,57 @@ +# 服务矩阵(Service Matrix) + +本文档定义当前项目的服务分层、默认启动策略与脚本入口。 + +## 1. 服务分层 + +| 服务 | 角色 | 默认端口 | 是否默认启动 | 启动脚本 | 停止方式 | +|---|---|---:|---|---|---| +| backend | 核心搜索 API | 6002 | 是 | `scripts/start_backend.sh` | `scripts/service_ctl.sh stop backend` | +| indexer | 核心索引 API | 6004 | 是 | `scripts/start_indexer.sh` | `scripts/service_ctl.sh stop indexer` | +| frontend | 调试 UI | 6003 | 是 | `scripts/start_frontend.sh` | `scripts/service_ctl.sh stop frontend` | +| embedding | 向量服务(文本/图片) | 6005 | 否(按需) | `scripts/start_embedding_service.sh` | `scripts/service_ctl.sh stop embedding` | +| translator | 翻译服务(qwen/deepl) | 6006 | 否(按需) | `scripts/start_translator.sh` | `scripts/service_ctl.sh stop translator` | +| reranker | 重排服务(BGE) | 6007 | 否(按需) | `scripts/start_reranker.sh` | `scripts/service_ctl.sh stop reranker` | +| clip | CLIP 替代服务(legacy/可选) | 51000 | 否(按需) | `scripts/start_clip_service.sh` | `scripts/service_ctl.sh stop clip` | +| cnclip | CN-CLIP gRPC 服务(legacy/可选) | 51000 | 否(按需) | `scripts/start_cnclip_service.sh` | `scripts/service_ctl.sh stop cnclip` | + +> 说明:`clip` 与 `cnclip` 都是 legacy 服务,脚本内部自带后台化与 PID 管理,`service_ctl.sh` 仅做编排与委托。 + +## 2. 统一控制入口 + +- 推荐统一入口:`scripts/service_ctl.sh` +- 支持命令:`start` / `stop` / `restart` / `status` + +示例: + +```bash +# 启动核心服务(backend/indexer/frontend) +./scripts/service_ctl.sh start + +# 启动指定服务 +./scripts/service_ctl.sh start backend indexer frontend translator reranker + +# 查看所有服务状态 +./scripts/service_ctl.sh status + +# 停止全部已知服务 +./scripts/service_ctl.sh stop +``` + +## 3. 默认与可选服务策略 + +- `./run.sh` 默认只启动核心服务:`backend/indexer/frontend` +- 如需启动可选能力,使用环境变量: + +```bash +START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh +``` + +## 4. 兼容入口 + +以下脚本仍保留,用于兼容旧习惯,但内部已委托到统一控制脚本: + +- `run.sh` +- `restart.sh` +- `scripts/start.sh` +- `scripts/stop.sh` diff --git a/docs/Usage-Guide.md b/docs/Usage-Guide.md index eb1671b..6e1d8a1 100644 --- a/docs/Usage-Guide.md +++ b/docs/Usage-Guide.md @@ -112,16 +112,42 @@ cd /data/saas-search 这个脚本会自动: 1. 创建日志目录 -2. 启动后端API服务(后台运行) -3. 启动前端Web界面(后台运行) -4. 等待服务就绪 +2. 启动核心服务(backend/indexer/frontend) +3. 写入 PID 到 `logs/*.pid` +4. 执行健康检查 启动完成后,访问: - **前端界面**: http://localhost:6003 - **后端API**: http://localhost:6002 - **API文档**: http://localhost:6002/docs +- **索引API**: http://localhost:6004/docs -### 方式2: 分步启动(单环境) +可选:全功能模式(同时启动 embedding/translator/reranker): + +```bash +START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh +``` + +### 方式2: 统一控制脚本(推荐) + +```bash +# 查看状态 +./scripts/service_ctl.sh status + +# 启动核心服务(默认) +./scripts/service_ctl.sh start + +# 启动指定服务 +./scripts/service_ctl.sh start backend indexer frontend translator reranker + +# 停止全部服务(含可选服务) +./scripts/service_ctl.sh stop + +# 重启 +./scripts/service_ctl.sh restart +``` + +### 方式3: 分步启动(单环境) #### 启动后端服务 @@ -139,7 +165,7 @@ cd /data/saas-search 前端界面会在 http://localhost:6003 启动 -### 方式3: 多环境示例(prod / uat) +### 方式4: 多环境示例(prod / uat) 假设有两套环境: @@ -182,7 +208,7 @@ cp .env.prod .env ./scripts/start_indexer.sh ``` -### 方式4: 手动启动 +### 方式5: 手动启动 #### 启动后端API服务 @@ -205,14 +231,11 @@ python -m http.server 6003 ### 停止服务 ```bash -# 停止后端 -kill $(cat logs/backend.pid) - -# 停止前端 -kill $(cat logs/frontend.pid) - -# 或使用停止脚本 +# 推荐:统一停止 ./scripts/stop.sh + +# 或使用统一控制脚本 +./scripts/service_ctl.sh stop ``` ### 服务端口 @@ -221,8 +244,12 @@ kill $(cat logs/frontend.pid) |------|------|-----| | Elasticsearch | 9200 | http://localhost:9200 | | Backend API | 6002 | http://localhost:6002 | +| Indexer API | 6004 | http://localhost:6004 | | Frontend Web | 6003 | http://localhost:6003 | -| API Docs | 6002 | http://localhost:6002/docs | +| Embedding (optional) | 6005 | http://localhost:6005 | +| Translation (optional) | 6006 | http://localhost:6006 | +| Reranker (optional) | 6007 | http://localhost:6007 | +| API Docs | 6002 / 6004 | http://localhost:6002/docs / http://localhost:6004/docs | --- @@ -256,6 +283,20 @@ DEEPL_AUTH_KEY=c9293ab4-ad25-479b-919f-ab4e63b429ed # API服务配置 API_HOST=0.0.0.0 API_PORT=6002 + +# Indexer服务配置 +INDEXER_HOST=0.0.0.0 +INDEXER_PORT=6004 + +# Optional service ports +EMBEDDING_PORT=6005 +TRANSLATION_PORT=6006 +RERANKER_PORT=6007 + +# Optional startup switches (for run.sh / service_ctl.sh) +START_EMBEDDING=0 +START_TRANSLATOR=0 +START_RERANKER=0 ``` ### 修改配置 @@ -272,7 +313,11 @@ API_PORT=6002 日志文件存储在 `logs/` 目录下: - `logs/backend.log` - 后端服务日志 +- `logs/indexer.log` - 索引服务日志 - `logs/frontend.log` - 前端服务日志 +- `logs/embedding.log` - 向量服务日志(可选) +- `logs/translator.log` - 翻译服务日志(可选) +- `logs/reranker.log` - 重排服务日志(可选) - `logs/search_engine.log` - 应用主日志(按天轮转) - `logs/errors.log` - 错误日志(按天轮转) diff --git a/docs/基础配置指南.md b/docs/基础配置指南.md index f9d1148..979569e 100644 --- a/docs/基础配置指南.md +++ b/docs/基础配置指南.md @@ -138,8 +138,8 @@ ### 修改索引结构 编辑 `mappings/search_products.json`,然后: -1. 删除旧索引: `scripts/recreate_and_import.py --recreate` -2. 重新导入数据: `scripts/ingest.sh true` +1. 重建租户索引结构: `./scripts/create_tenant_index.sh ` +2. 重新导入数据: `POST /indexer/reindex` ### 修改查询配置 diff --git a/docs/搜索API对接指南.md b/docs/搜索API对接指南.md index 9bcf1c4..a588cec 100644 --- a/docs/搜索API对接指南.md +++ b/docs/搜索API对接指南.md @@ -958,7 +958,7 @@ tail -f logs/api.log tail -f logs/*.log ``` -> ⚠️ **重要提示**:如需 **创建索引结构**,请参考 [5.0 为租户创建索引](#50-为租户创建索引) 章节,使用 `scripts/recreate_all_tenant_indices.py` 脚本。创建后需要调用 `/indexer/reindex` 导入数据。 +> ⚠️ **重要提示**:如需 **创建索引结构**,请参考 [5.0 为租户创建索引](#50-为租户创建索引) 章节,使用 `./scripts/create_tenant_index.sh `。创建后需要调用 `/indexer/reindex` 导入数据。 **查看索引日志**: diff --git a/docs/环境配置说明.md b/docs/环境配置说明.md index f96025d..5b3a6b2 100644 --- a/docs/环境配置说明.md +++ b/docs/环境配置说明.md @@ -170,8 +170,10 @@ saas-search 以 MySQL 中的店匠标准表为权威数据源: - **`activate.sh`**(项目根目录):激活 Conda 环境 `searchengine` 并加载 `.env`,**日常开发/部署以本脚本为准**。 - `scripts/mock_data.sh`:一次性生成 Tenant1 Mock + Tenant2 CSV 数据并导入 MySQL -- `scripts/ingest.sh [recreate]`:从 MySQL 写入 Elasticsearch -- `run.sh` / `restart.sh`:服务启动/重启(内部会调用 `start_backend.sh` 等,同样使用 `CONDA_ROOT`) +- `scripts/create_tenant_index.sh `:创建租户索引结构 +- `POST /indexer/reindex`:从 MySQL 导入到 Elasticsearch(推荐) +- `run.sh` / `restart.sh`:服务启动/重启(统一走 `scripts/service_ctl.sh`) +- `scripts/service_ctl.sh`:统一服务管理(start/stop/restart/status) **新机器部署**:若 Conda 未安装在默认路径(如 `/home/tw/miniconda3`),请在执行上述脚本前设置 `CONDA_ROOT`。例如你的 conda 是 `~/anaconda3/bin/conda`(即 `/home/ubuntu/anaconda3/bin/conda`),则设置:`export CONDA_ROOT=$HOME/anaconda3`。可将该行写入 `~/.bashrc` 或部署说明。 diff --git a/docs/翻译模块说明.md b/docs/翻译模块说明.md index 64e0732..3a3cebb 100644 --- a/docs/翻译模块说明.md +++ b/docs/翻译模块说明.md @@ -51,14 +51,14 @@ TRANSLATION_MODEL=qwen # 或 deepl 推荐(热更新): ```bash -cd /home/tw/saas-search +cd /data/saas-search uvicorn api.translator_app:app --host 0.0.0.0 --port 6006 --reload ``` 指定默认模型(不传请求 `model` 时生效): ```bash -cd /home/tw/saas-search +cd /data/saas-search export TRANSLATION_MODEL=qwen # 或 deepl uvicorn api.translator_app:app --host 0.0.0.0 --port 6006 --reload ``` diff --git a/embeddings/config.py b/embeddings/config.py index a5fc5f7..7e0bf3a 100644 --- a/embeddings/config.py +++ b/embeddings/config.py @@ -8,12 +8,13 @@ Edit values here to configure: """ from typing import Optional +import os class EmbeddingConfig(object): # Server - HOST = "0.0.0.0" - PORT = 6005 + HOST = os.getenv("EMBEDDING_HOST", "0.0.0.0") + PORT = int(os.getenv("EMBEDDING_PORT", 6005)) # Text embeddings (BGE-M3) TEXT_MODEL_DIR = "Xorbits/bge-m3" diff --git a/reranker/config.py b/reranker/config.py index 43b569d..29a4363 100644 --- a/reranker/config.py +++ b/reranker/config.py @@ -1,10 +1,12 @@ """Reranker service configuration (simple Python config).""" +import os + class RerankerConfig(object): # Server - HOST = "0.0.0.0" - PORT = 6007 + HOST = os.getenv("RERANKER_HOST", "0.0.0.0") + PORT = int(os.getenv("RERANKER_PORT", 6007)) # Model MODEL_NAME = "BAAI/bge-reranker-v2-m3" diff --git a/restart.sh b/restart.sh index 2def532..cf2d62a 100755 --- a/restart.sh +++ b/restart.sh @@ -1,50 +1,7 @@ #!/bin/bash -# Restart script for saas-search services -# This script stops all services first, then starts them again +# Unified restart script for saas-search services cd "$(dirname "$0")" -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' - -echo -e "${GREEN}========================================${NC}" -echo -e "${GREEN}saas-search服务重启脚本${NC}" -echo -e "${GREEN}========================================${NC}" - -# Step 1: Stop all services -echo -e "\n${YELLOW}Step 1/2: 停止现有服务${NC}" -if [ -f "./scripts/stop.sh" ]; then - ./scripts/stop.sh - if [ $? -eq 0 ]; then - echo -e "${GREEN}✓ 所有服务已成功停止${NC}" - else - echo -e "${YELLOW}⚠ 停止服务时出现警告,继续重启流程${NC}" - fi -else - echo -e "${RED}✗ 停止脚本不存在,无法安全重启${NC}" - exit 1 -fi - -# Wait a moment for services to fully stop -echo -e "\n${YELLOW}等待服务完全关闭...${NC}" -sleep 3 - -# Step 2: Start all services -echo -e "\n${YELLOW}Step 2/2: 重新启动服务${NC}" -if [ -f "./scripts/start.sh" ]; then - ./scripts/start.sh - if [ $? -eq 0 ]; then - echo -e "${GREEN}========================================${NC}" - echo -e "${GREEN}服务重启完成!${NC}" - echo -e "${GREEN}========================================${NC}" - else - echo -e "${RED}✗ 服务启动失败${NC}" - exit 1 - fi -else - echo -e "${RED}✗ 启动脚本不存在,无法完成重启${NC}" - exit 1 -fi \ No newline at end of file +./scripts/service_ctl.sh restart diff --git a/run.sh b/run.sh index 6f0d785..2afccb8 100755 --- a/run.sh +++ b/run.sh @@ -1,21 +1,7 @@ #!/bin/bash -# Production startup script for saas-search services -# This script starts frontend and backend services (no data ingestion) +# Unified startup script for saas-search services cd "$(dirname "$0")" -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' - -echo -e "${GREEN}========================================${NC}" -echo -e "${GREEN}saas-search服务启动脚本${NC}" -echo -e "${GREEN}========================================${NC}" - -# Create logs directory if it doesn't exist -mkdir -p logs - -# Call unified start script -./scripts/start.sh \ No newline at end of file +./scripts/service_ctl.sh start diff --git a/scripts/indexer__old_2025_11/ingest.sh b/scripts/indexer__old_2025_11/ingest.sh index 42db69a..572ab81 100755 --- a/scripts/indexer__old_2025_11/ingest.sh +++ b/scripts/indexer__old_2025_11/ingest.sh @@ -2,6 +2,11 @@ # Unified data ingestion script for saas-search # Ingests data from MySQL to Elasticsearch +# +# [LEGACY] 此脚本仅保留用于历史兼容,不建议新流程继续使用。 +# 推荐改用: +# 1) ./scripts/create_tenant_index.sh +# 2) POST /indexer/reindex cd "$(dirname "$0")/.." source /home/tw/miniconda3/etc/profile.d/conda.sh diff --git a/scripts/mock_data.sh b/scripts/mock_data.sh index c50bcaa..a719563 100755 --- a/scripts/mock_data.sh +++ b/scripts/mock_data.sh @@ -159,6 +159,6 @@ echo -e "${GREEN}数据导入完成!${NC}" echo -e "${GREEN}========================================${NC}" echo "" echo -e "下一步:" -echo -e " ${YELLOW}./scripts/ingest.sh 1 true${NC} - 从MySQL灌入tenant_id=1数据到ES" -echo -e " ${YELLOW}./scripts/ingest.sh 2 true${NC} - 从MySQL灌入tenant_id=2数据到ES" +echo -e " ${YELLOW}./scripts/create_tenant_index.sh ${NC} - 创建租户索引结构" +echo -e " ${YELLOW}curl -X POST http://localhost:6004/indexer/reindex ...${NC} - 从MySQL导入到ES" echo "" diff --git a/scripts/service_ctl.sh b/scripts/service_ctl.sh new file mode 100755 index 0000000..1236fe7 --- /dev/null +++ b/scripts/service_ctl.sh @@ -0,0 +1,326 @@ +#!/bin/bash +# +# Unified service lifecycle controller for saas-search. +# Supports: start / stop / restart / status +# + +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +LOG_DIR="${PROJECT_ROOT}/logs" + +mkdir -p "${LOG_DIR}" + +CORE_SERVICES=("backend" "indexer" "frontend") +OPTIONAL_SERVICES=("embedding" "translator" "reranker") +LEGACY_SERVICES=("clip" "cnclip") + +all_services() { + echo "${CORE_SERVICES[@]} ${OPTIONAL_SERVICES[@]} ${LEGACY_SERVICES[@]}" +} + +load_env_file() { + local env_file="${PROJECT_ROOT}/.env" + if [ -f "${env_file}" ]; then + set -a + # shellcheck disable=SC1090 + source "${env_file}" + set +a + fi +} + +get_port() { + local service="$1" + case "${service}" in + backend) echo "${API_PORT:-6002}" ;; + indexer) echo "${INDEXER_PORT:-6004}" ;; + frontend) echo "${FRONTEND_PORT:-6003}" ;; + embedding) echo "${EMBEDDING_PORT:-6005}" ;; + translator) echo "${TRANSLATION_PORT:-${TRANSLATOR_PORT:-6006}}" ;; + reranker) echo "${RERANKER_PORT:-6007}" ;; + clip) echo "${CLIP_PORT:-51000}" ;; + cnclip) echo "${CNCLIP_PORT:-51000}" ;; + *) echo "" ;; + esac +} + +pid_file() { + local service="$1" + case "${service}" in + clip) echo "${LOG_DIR}/clip_service.pid" ;; + cnclip) echo "${LOG_DIR}/cnclip_service.pid" ;; + *) echo "${LOG_DIR}/${service}.pid" ;; + esac +} + +log_file() { + local service="$1" + echo "${LOG_DIR}/${service}.log" +} + +service_start_cmd() { + local service="$1" + case "${service}" in + backend) echo "./scripts/start_backend.sh" ;; + indexer) echo "./scripts/start_indexer.sh" ;; + frontend) echo "./scripts/start_frontend.sh" ;; + embedding) echo "./scripts/start_embedding_service.sh" ;; + translator) echo "./scripts/start_translator.sh" ;; + reranker) echo "./scripts/start_reranker.sh" ;; + clip) echo "./scripts/start_clip_service.sh" ;; + cnclip) echo "./scripts/start_cnclip_service.sh" ;; + *) return 1 ;; + esac +} + +wait_for_health() { + local service="$1" + local max_retries="${2:-30}" + local interval_sec="${3:-1}" + local port + port="$(get_port "${service}")" + local path="/health" + + case "${service}" in + backend) path="/health" ;; + indexer) path="/health" ;; + frontend) path="/" ;; + embedding) path="/health" ;; + translator) path="/health" ;; + reranker) path="/health" ;; + *) return 0 ;; + esac + + local i=0 + while [ "${i}" -lt "${max_retries}" ]; do + if curl -sf "http://127.0.0.1:${port}${path}" >/dev/null 2>&1; then + return 0 + fi + i=$((i + 1)) + sleep "${interval_sec}" + done + return 1 +} + +is_running_by_pid() { + local service="$1" + local pf + pf="$(pid_file "${service}")" + if [ ! -f "${pf}" ]; then + return 1 + fi + local pid + pid="$(cat "${pf}" 2>/dev/null || true)" + [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null +} + +is_running_by_port() { + local service="$1" + local port + port="$(get_port "${service}")" + [ -n "${port}" ] && lsof -ti:"${port}" >/dev/null 2>&1 +} + +start_one() { + local service="$1" + cd "${PROJECT_ROOT}" + local cmd + cmd="$(service_start_cmd "${service}")" + local pf lf + pf="$(pid_file "${service}")" + lf="$(log_file "${service}")" + + if is_running_by_pid "${service}" || is_running_by_port "${service}"; then + echo "[skip] ${service} already running" + return 0 + fi + + case "${service}" in + clip|cnclip) + echo "[start] ${service} (managed by native script)" + bash -lc "${cmd}" >> "${lf}" 2>&1 || true + if is_running_by_pid "${service}" || is_running_by_port "${service}"; then + echo "[ok] ${service} started (log=${lf})" + else + echo "[warn] ${service} may not be running, inspect ${lf}" + fi + ;; + backend|indexer|frontend|embedding|translator|reranker) + echo "[start] ${service}" + nohup bash -lc "${cmd}" > "${lf}" 2>&1 & + local pid=$! + echo "${pid}" > "${pf}" + if wait_for_health "${service}"; then + echo "[ok] ${service} healthy (pid=${pid}, log=${lf})" + else + echo "[warn] ${service} health check timeout, inspect ${lf}" + fi + ;; + *) + echo "[warn] ${service} unsupported start path" + ;; + esac +} + +stop_one() { + local service="$1" + cd "${PROJECT_ROOT}" + if [ "${service}" = "clip" ]; then + echo "[stop] clip (managed by native script)" + bash -lc "./scripts/stop_clip_service.sh" || true + return 0 + fi + if [ "${service}" = "cnclip" ]; then + echo "[stop] cnclip (managed by native script)" + bash -lc "./scripts/stop_cnclip_service.sh" || true + return 0 + fi + + local pf + pf="$(pid_file "${service}")" + + if [ -f "${pf}" ]; then + local pid + pid="$(cat "${pf}" 2>/dev/null || true)" + if [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null; then + echo "[stop] ${service} pid=${pid}" + kill -TERM "${pid}" 2>/dev/null || true + sleep 1 + if kill -0 "${pid}" 2>/dev/null; then + kill -KILL "${pid}" 2>/dev/null || true + fi + fi + rm -f "${pf}" + fi + + local port + port="$(get_port "${service}")" + if [ -n "${port}" ]; then + local pids + pids="$(lsof -ti:${port} 2>/dev/null || true)" + if [ -n "${pids}" ]; then + echo "[stop] ${service} port=${port} pids=${pids}" + for pid in ${pids}; do + kill -TERM "${pid}" 2>/dev/null || true + done + sleep 1 + pids="$(lsof -ti:${port} 2>/dev/null || true)" + for pid in ${pids}; do + kill -KILL "${pid}" 2>/dev/null || true + done + fi + fi +} + +status_one() { + local service="$1" + local port + port="$(get_port "${service}")" + local running="no" + local pid_info="-" + + if is_running_by_pid "${service}"; then + running="yes" + pid_info="$(cat "$(pid_file "${service}")" 2>/dev/null || echo "-")" + elif is_running_by_port "${service}"; then + running="yes" + pid_info="$(lsof -ti:${port} 2>/dev/null | tr '\n' ',' | sed 's/,$//' || echo "-")" + fi + + printf "%-10s running=%-3s port=%-6s pid=%s\n" "${service}" "${running}" "${port:--}" "${pid_info}" +} + +resolve_targets() { + local scope="$1" + shift || true + + if [ "$#" -gt 0 ]; then + echo "$*" + return + fi + + case "${scope}" in + start) + local targets=("${CORE_SERVICES[@]}") + if [ "${START_EMBEDDING:-0}" = "1" ]; then targets+=("embedding"); fi + if [ "${START_TRANSLATOR:-0}" = "1" ]; then targets+=("translator"); fi + if [ "${START_RERANKER:-0}" = "1" ]; then targets+=("reranker"); fi + echo "${targets[@]}" + ;; + stop|restart|status) + echo "$(all_services)" + ;; + *) + echo "" + ;; + esac +} + +usage() { + cat <<'EOF' +Usage: + ./scripts/service_ctl.sh start [service...] + ./scripts/service_ctl.sh stop [service...] + ./scripts/service_ctl.sh restart [service...] + ./scripts/service_ctl.sh status [service...] + +Default target set (when no service provided): + start -> backend indexer frontend (+ optional by env flags) + stop -> all known services + restart -> all known services + status -> all known services + +Optional startup flags: + START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh +EOF +} + +main() { + if [ "$#" -lt 1 ]; then + usage + exit 1 + fi + + local action="$1" + shift || true + + load_env_file + local targets + targets="$(resolve_targets "${action}" "$@")" + if [ -z "${targets}" ]; then + usage + exit 1 + fi + + case "${action}" in + start) + for svc in ${targets}; do + start_one "${svc}" + done + ;; + stop) + for svc in ${targets}; do + stop_one "${svc}" + done + ;; + restart) + for svc in ${targets}; do + stop_one "${svc}" + done + for svc in ${targets}; do + start_one "${svc}" + done + ;; + status) + for svc in ${targets}; do + status_one "${svc}" + done + ;; + *) + usage + exit 1 + ;; + esac +} + +main "$@" diff --git a/scripts/start.sh b/scripts/start.sh index bfbb7c2..36eb4ad 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -1,138 +1,23 @@ #!/bin/bash -# Unified startup script for saas-search services -# This script starts both frontend and backend services +# Backward-compatible start entrypoint. +# Delegates to unified service controller. -cd "$(dirname "$0")/.." - -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' - -echo -e "${GREEN}========================================${NC}" -echo -e "${GREEN}saas-search服务启动脚本${NC}" -echo -e "${GREEN}========================================${NC}" - -# Create logs directory if it doesn't exist -mkdir -p logs - -# Step 1: Start backend in background (search API) -echo -e "\n${YELLOW}Step 1/3: 启动后端搜索服务${NC}" -echo -e "${YELLOW}后端搜索服务将在后台运行...${NC}" - -nohup ./scripts/start_backend.sh > logs/backend.log 2>&1 & -BACKEND_PID=$! -echo $BACKEND_PID > logs/backend.pid -echo -e "${GREEN}后端搜索服务已启动 (PID: $BACKEND_PID)${NC}" -echo -e "${GREEN}日志文件: logs/backend.log${NC}" - -# Wait for backend to start -echo -e "${YELLOW}等待后端搜索服务启动...${NC}" -MAX_RETRIES=30 -RETRY_COUNT=0 -BACKEND_READY=false - -while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do - sleep 2 - if curl -s http://localhost:6002/health > /dev/null 2>&1; then - BACKEND_READY=true - break - fi - RETRY_COUNT=$((RETRY_COUNT + 1)) - echo -e "${YELLOW} 等待中... ($RETRY_COUNT/$MAX_RETRIES)${NC}" -done - -# Check if backend is running -if [ "$BACKEND_READY" = true ]; then - echo -e "${GREEN}✓ 后端搜索服务运行正常${NC}" -else - echo -e "${RED}✗ 后端搜索服务启动失败,请检查日志: logs/backend.log${NC}" - echo -e "${YELLOW}提示: 后端服务可能需要更多时间启动,或者检查端口是否被占用${NC}" - exit 1 -fi +set -e -# Step 2: Start indexer in background -echo -e "\n${YELLOW}Step 2/3: 启动索引服务${NC}" -echo -e "${YELLOW}索引服务将在后台运行...${NC}" - -nohup ./scripts/start_indexer.sh > logs/indexer.log 2>&1 & -INDEXER_PID=$! -echo $INDEXER_PID > logs/indexer.pid -echo -e "${GREEN}索引服务已启动 (PID: $INDEXER_PID)${NC}" -echo -e "${GREEN}日志文件: logs/indexer.log${NC}" - -# Wait for indexer to start -echo -e "${YELLOW}等待索引服务启动...${NC}" -MAX_RETRIES=30 -RETRY_COUNT=0 -INDEXER_READY=false - -while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do - sleep 2 - if curl -s http://localhost:6004/health > /dev/null 2>&1; then - INDEXER_READY=true - break - fi - RETRY_COUNT=$((RETRY_COUNT + 1)) - echo -e "${YELLOW} 等待中... ($RETRY_COUNT/$MAX_RETRIES)${NC}" -done - -if [ "$INDEXER_READY" = true ]; then - echo -e "${GREEN}✓ 索引服务运行正常${NC}" -else - echo -e "${YELLOW}⚠ 索引服务可能还在启动中,请稍后访问 (日志: logs/indexer.log)${NC}" -fi - -# Step 3: Start frontend in background -echo -e "\n${YELLOW}Step 3/3: 启动前端服务${NC}" -echo -e "${YELLOW}前端服务将在后台运行...${NC}" - -nohup ./scripts/start_frontend.sh > logs/frontend.log 2>&1 & -FRONTEND_PID=$! -echo $FRONTEND_PID > logs/frontend.pid -echo -e "${GREEN}前端服务已启动 (PID: $FRONTEND_PID)${NC}" -echo -e "${GREEN}日志文件: logs/frontend.log${NC}" - -# Wait for frontend to start -echo -e "${YELLOW}等待前端服务启动...${NC}" -MAX_RETRIES=15 -RETRY_COUNT=0 -FRONTEND_READY=false +cd "$(dirname "$0")/.." -while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do - sleep 2 - if curl -s http://localhost:6003/ > /dev/null 2>&1; then - FRONTEND_READY=true - break - fi - RETRY_COUNT=$((RETRY_COUNT + 1)) - echo -e "${YELLOW} 等待中... ($RETRY_COUNT/$MAX_RETRIES)${NC}" -done +echo "========================================" +echo "saas-search 服务启动" +echo "========================================" +echo "默认启动核心服务: backend/indexer/frontend" +echo "可选服务通过环境变量开启:" +echo " START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh" +echo -# Check if frontend is running -if [ "$FRONTEND_READY" = true ]; then - echo -e "${GREEN}✓ 前端服务运行正常${NC}" -else - echo -e "${YELLOW}⚠ 前端服务可能还在启动中,请稍后访问${NC}" -fi +./scripts/service_ctl.sh start -echo -e "${GREEN}========================================${NC}" -echo -e "${GREEN}所有服务启动完成!${NC}" -echo -e "${GREEN}========================================${NC}" -echo "" -echo -e "访问地址:" -echo -e " ${GREEN}前端界面: http://localhost:6003${NC}" -echo -e " ${GREEN}后端API: http://localhost:6002${NC}" -echo -e " ${GREEN}API文档: http://localhost:6002/docs${NC}" -echo "" -echo -e "日志文件:" -echo -e " 后端: logs/backend.log" -echo -e " 前端: logs/frontend.log" -echo "" -echo -e "停止服务:" -echo -e " 所有服务: ./scripts/stop.sh" -echo -e " 单独停止后端: kill \$(cat logs/backend.pid)" -echo -e " 单独停止前端: kill \$(cat logs/frontend.pid)" -echo "" +echo +echo "当前服务状态:" +./scripts/service_ctl.sh status backend indexer frontend embedding translator reranker diff --git a/scripts/start_embedding_service.sh b/scripts/start_embedding_service.sh index ff30a19..a58bab7 100755 --- a/scripts/start_embedding_service.sh +++ b/scripts/start_embedding_service.sh @@ -14,8 +14,11 @@ cd "$(dirname "$0")/.." source ./activate.sh -EMBEDDING_SERVICE_HOST=$(python -c "from embeddings.config import CONFIG; print(CONFIG.HOST)") -EMBEDDING_SERVICE_PORT=$(python -c "from embeddings.config import CONFIG; print(CONFIG.PORT)") +DEFAULT_EMBEDDING_SERVICE_HOST=$(python -c "from embeddings.config import CONFIG; print(CONFIG.HOST)") +DEFAULT_EMBEDDING_SERVICE_PORT=$(python -c "from embeddings.config import CONFIG; print(CONFIG.PORT)") + +EMBEDDING_SERVICE_HOST="${EMBEDDING_HOST:-${DEFAULT_EMBEDDING_SERVICE_HOST}}" +EMBEDDING_SERVICE_PORT="${EMBEDDING_PORT:-${DEFAULT_EMBEDDING_SERVICE_PORT}}" echo "========================================" echo "Starting Local Embedding Service" diff --git a/scripts/start_reranker.sh b/scripts/start_reranker.sh new file mode 100755 index 0000000..14d8018 --- /dev/null +++ b/scripts/start_reranker.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Start Reranker Service +# + +set -e + +cd "$(dirname "$0")/.." +source ./activate.sh + +RERANKER_HOST="${RERANKER_HOST:-0.0.0.0}" +RERANKER_PORT="${RERANKER_PORT:-6007}" + +echo "========================================" +echo "Starting Reranker Service" +echo "========================================" +echo "Host: ${RERANKER_HOST}" +echo "Port: ${RERANKER_PORT}" +echo + +exec python -m uvicorn reranker.server:app \ + --host "${RERANKER_HOST}" \ + --port "${RERANKER_PORT}" \ + --workers 1 diff --git a/scripts/start_servers.py b/scripts/start_servers.py index 23cc16c..baa9a5b 100755 --- a/scripts/start_servers.py +++ b/scripts/start_servers.py @@ -1,6 +1,11 @@ #!/usr/bin/env python3 """ Production-ready server startup script with proper error handling and monitoring. + +[LEGACY] +This script is kept for historical compatibility. +Preferred entrypoint is: + ./scripts/service_ctl.sh start """ import os diff --git a/scripts/start_translator.sh b/scripts/start_translator.sh new file mode 100755 index 0000000..5f504c2 --- /dev/null +++ b/scripts/start_translator.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Start Translation Service +# + +set -e + +cd "$(dirname "$0")/.." +source ./activate.sh + +TRANSLATION_HOST="${TRANSLATION_HOST:-0.0.0.0}" +TRANSLATION_PORT="${TRANSLATION_PORT:-${TRANSLATOR_PORT:-6006}}" + +echo "========================================" +echo "Starting Translation Service" +echo "========================================" +echo "Host: ${TRANSLATION_HOST}" +echo "Port: ${TRANSLATION_PORT}" +echo "Default model: ${TRANSLATION_MODEL:-qwen}" +echo + +exec python -m uvicorn api.translator_app:app \ + --host "${TRANSLATION_HOST}" \ + --port "${TRANSLATION_PORT}" \ + --workers 1 diff --git a/scripts/stop.sh b/scripts/stop.sh index a85c1ee..3b96402 100755 --- a/scripts/stop.sh +++ b/scripts/stop.sh @@ -1,110 +1,16 @@ #!/bin/bash -# Stop script for Search Engine services -# This script stops both backend and frontend servers +# Backward-compatible stop entrypoint. +# Delegates to unified service controller. -echo "========================================" -echo "Stopping Search Engine Services" -echo "========================================" - -# Kill processes on port 6002 (backend - search API) -BACKEND_PIDS=$(lsof -ti:6002 2>/dev/null) -if [ ! -z "$BACKEND_PIDS" ]; then - echo "Stopping backend server(s) on port 6002..." - for PID in $BACKEND_PIDS; do - echo " Killing PID: $PID" - kill -TERM $PID 2>/dev/null || true - done - sleep 2 - # Force kill if still running - REMAINING_PIDS=$(lsof -ti:6002 2>/dev/null) - if [ ! -z "$REMAINING_PIDS" ]; then - echo " Force killing remaining processes..." - for PID in $REMAINING_PIDS; do - kill -KILL $PID 2>/dev/null || true - done - fi - echo "Backend server stopped." -else - echo "No backend server found running on port 6002." -fi - -# Kill processes on port 6004 (indexer API) -INDEXER_PIDS=$(lsof -ti:6004 2>/dev/null) -if [ ! -z "$INDEXER_PIDS" ]; then - echo "Stopping indexer server(s) on port 6004..." - for PID in $INDEXER_PIDS; do - echo " Killing PID: $PID" - kill -TERM $PID 2>/dev/null || true - done - sleep 2 - # Force kill if still running - REMAINING_PIDS=$(lsof -ti:6004 2>/dev/null) - if [ ! -z "$REMAINING_PIDS" ]; then - echo " Force killing remaining processes..." - for PID in $REMAINING_PIDS; do - kill -KILL $PID 2>/dev/null || true - done - fi - echo "Indexer server stopped." -else - echo "No indexer server found running on port 6004." -fi +set -e -# Kill processes on port 6003 (frontend) -FRONTEND_PIDS=$(lsof -ti:6003 2>/dev/null) -if [ ! -z "$FRONTEND_PIDS" ]; then - echo "Stopping frontend server(s) on port 6003..." - for PID in $FRONTEND_PIDS; do - echo " Killing PID: $PID" - kill -TERM $PID 2>/dev/null || true - done - sleep 2 - # Force kill if still running - REMAINING_PIDS=$(lsof -ti:6003 2>/dev/null) - if [ ! -z "$REMAINING_PIDS" ]; then - echo " Force killing remaining processes..." - for PID in $REMAINING_PIDS; do - kill -KILL $PID 2>/dev/null || true - done - fi - echo "Frontend server stopped." -else - echo "No frontend server found running on port 6003." -fi +cd "$(dirname "$0")/.." -# Also stop any processes using PID files -if [ -f "logs/backend.pid" ]; then - BACKEND_PID=$(cat logs/backend.pid 2>/dev/null) - if [ ! -z "$BACKEND_PID" ] && kill -0 $BACKEND_PID 2>/dev/null; then - echo "Stopping backend server via PID file (PID: $BACKEND_PID)..." - kill -TERM $BACKEND_PID 2>/dev/null || true - sleep 2 - kill -KILL $BACKEND_PID 2>/dev/null || true - fi - rm -f logs/backend.pid -fi - -[ -f "logs/indexer.pid" ] && INDEXER_PID=$(cat logs/indexer.pid 2>/dev/null) -if [ ! -z "$INDEXER_PID" ] && kill -0 $INDEXER_PID 2>/dev/null; then - echo "Stopping indexer server via PID file (PID: $INDEXER_PID)..." - kill -TERM $INDEXER_PID 2>/dev/null || true - sleep 2 - kill -KILL $INDEXER_PID 2>/dev/null || true -fi -rm -f logs/indexer.pid +echo "========================================" +echo "Stopping saas-search services" +echo "========================================" -if [ -f "logs/frontend.pid" ]; then - FRONTEND_PID=$(cat logs/frontend.pid 2>/dev/null) - if [ ! -z "$FRONTEND_PID" ] && kill -0 $FRONTEND_PID 2>/dev/null; then - echo "Stopping frontend server via PID file (PID: $FRONTEND_PID)..." - kill -TERM $FRONTEND_PID 2>/dev/null || true - sleep 2 - kill -KILL $FRONTEND_PID 2>/dev/null || true - fi - rm -f logs/frontend.pid -fi +./scripts/service_ctl.sh stop -echo "========================================" -echo "All services stopped successfully!" -echo "========================================" \ No newline at end of file +echo "Done." diff --git a/scripts/stop_clip_service.sh b/scripts/stop_clip_service.sh index 81deb3c..3110f99 100755 --- a/scripts/stop_clip_service.sh +++ b/scripts/stop_clip_service.sh @@ -4,7 +4,8 @@ # set -e -LOG_DIR="/home/tw/saas-search/logs" +PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +LOG_DIR="${PROJECT_ROOT}/logs" PID_FILE="${LOG_DIR}/clip_service.pid" echo "========================================" diff --git a/scripts/stop_cnclip_service.sh b/scripts/stop_cnclip_service.sh index 3cc3877..df933db 100755 --- a/scripts/stop_cnclip_service.sh +++ b/scripts/stop_cnclip_service.sh @@ -21,7 +21,7 @@ BLUE='\033[0;34m' NC='\033[0m' # No Color # 项目路径 -PROJECT_ROOT="/data/tw/saas-search" +PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" LOG_DIR="${PROJECT_ROOT}/logs" PID_FILE="${LOG_DIR}/cnclip_service.pid" diff --git a/scripts/stop_reranker.sh b/scripts/stop_reranker.sh new file mode 100755 index 0000000..3af37b6 --- /dev/null +++ b/scripts/stop_reranker.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# +# Stop Reranker Service +# + +set -e + +cd "$(dirname "$0")/.." + +PID_FILE="logs/reranker.pid" +RERANKER_PORT="${RERANKER_PORT:-6007}" + +echo "========================================" +echo "Stopping Reranker Service" +echo "========================================" + +if [ -f "${PID_FILE}" ]; then + PID="$(cat "${PID_FILE}" 2>/dev/null || true)" + if [ -n "${PID}" ] && kill -0 "${PID}" 2>/dev/null; then + echo "Stopping PID from file: ${PID}" + kill -TERM "${PID}" 2>/dev/null || true + sleep 1 + if kill -0 "${PID}" 2>/dev/null; then + kill -KILL "${PID}" 2>/dev/null || true + fi + fi + rm -f "${PID_FILE}" +fi + +PORT_PIDS="$(lsof -ti:${RERANKER_PORT} 2>/dev/null || true)" +if [ -n "${PORT_PIDS}" ]; then + echo "Stopping process on port ${RERANKER_PORT}: ${PORT_PIDS}" + for PID in ${PORT_PIDS}; do + kill -TERM "${PID}" 2>/dev/null || true + done + sleep 1 + PORT_PIDS="$(lsof -ti:${RERANKER_PORT} 2>/dev/null || true)" + for PID in ${PORT_PIDS}; do + kill -KILL "${PID}" 2>/dev/null || true + done +fi + +echo "Reranker service stopped." diff --git a/scripts/stop_translator.sh b/scripts/stop_translator.sh new file mode 100755 index 0000000..7966874 --- /dev/null +++ b/scripts/stop_translator.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# +# Stop Translation Service +# + +set -e + +cd "$(dirname "$0")/.." + +PID_FILE="logs/translator.pid" +TRANSLATION_PORT="${TRANSLATION_PORT:-${TRANSLATOR_PORT:-6006}}" + +echo "========================================" +echo "Stopping Translation Service" +echo "========================================" + +if [ -f "${PID_FILE}" ]; then + PID="$(cat "${PID_FILE}" 2>/dev/null || true)" + if [ -n "${PID}" ] && kill -0 "${PID}" 2>/dev/null; then + echo "Stopping PID from file: ${PID}" + kill -TERM "${PID}" 2>/dev/null || true + sleep 1 + if kill -0 "${PID}" 2>/dev/null; then + kill -KILL "${PID}" 2>/dev/null || true + fi + fi + rm -f "${PID_FILE}" +fi + +PORT_PIDS="$(lsof -ti:${TRANSLATION_PORT} 2>/dev/null || true)" +if [ -n "${PORT_PIDS}" ]; then + echo "Stopping process on port ${TRANSLATION_PORT}: ${PORT_PIDS}" + for PID in ${PORT_PIDS}; do + kill -TERM "${PID}" 2>/dev/null || true + done + sleep 1 + PORT_PIDS="$(lsof -ti:${TRANSLATION_PORT} 2>/dev/null || true)" + for PID in ${PORT_PIDS}; do + kill -KILL "${PID}" 2>/dev/null || true + done +fi + +echo "Translation service stopped." diff --git a/search/rerank_client.py b/search/rerank_client.py index 0962db0..aca671f 100644 --- a/search/rerank_client.py +++ b/search/rerank_client.py @@ -8,6 +8,7 @@ """ from typing import Dict, Any, List, Optional, Tuple +import os import logging logger = logging.getLogger(__name__) @@ -228,7 +229,7 @@ def run_rerank( except Exception: RERANKER_CONFIG = None - url = service_url + url = service_url or os.getenv("RERANKER_SERVICE_URL") if not url and RERANKER_CONFIG is not None: url = f"http://127.0.0.1:{RERANKER_CONFIG.PORT}/rerank" if not url: diff --git a/search/searcher.py b/search/searcher.py index 38beb1c..3fca1f9 100644 --- a/search/searcher.py +++ b/search/searcher.py @@ -5,6 +5,7 @@ Handles query parsing, boolean expressions, ranking, and result formatting. """ from typing import Dict, Any, List, Optional, Union +import os import time, json import logging @@ -400,11 +401,11 @@ class Searcher: ) if rerank_meta is not None: - try: - from reranker.config import CONFIG as RERANKER_CONFIG - rerank_url = f"http://127.0.0.1:{RERANKER_CONFIG.PORT}/rerank" - except Exception: - rerank_url = "http://127.0.0.1:6007/rerank" + rerank_url = ( + rc.service_url + or os.getenv("RERANKER_SERVICE_URL") + or "http://127.0.0.1:6007/rerank" + ) context.metadata.setdefault("rerank_info", {}) context.metadata["rerank_info"].update({ "service_url": rerank_url, -- libgit2 0.21.2