Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea1b017f75 | |||
| de349a6d20 | |||
| 893f5e87b4 | |||
| c73bfa7e2a | |||
| ad4db736de | |||
| cfbd9e47ac |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -150,4 +150,5 @@ app/logs/*
|
||||
*.avi
|
||||
*.json
|
||||
*.env*
|
||||
config.backup.py
|
||||
config.backup.py
|
||||
*.pckl
|
||||
@@ -11,6 +11,7 @@ from app.api import api_precompute
|
||||
from app.api import api_prompt_generation
|
||||
from app.api import api_recommendation
|
||||
from app.api import api_test
|
||||
from app.api import api_sketch_to_garment
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -26,6 +27,7 @@ router.include_router(api_precompute.router, tags=['api_precompute'], prefix="/a
|
||||
router.include_router(api_mannequins_edit.router, tags=['api_mannequins_edit'], prefix="/api")
|
||||
router.include_router(api_pose_transform.router, tags=['api_pose_transform'], prefix="/api")
|
||||
router.include_router(api_clothing_seg.router, tags=['api_clothing_seg'], prefix="/api")
|
||||
router.include_router(api_sketch_to_garment.router, tags=['sketch_to_garment'], prefix="/api")
|
||||
|
||||
"""停用"""
|
||||
# from app.api import api_chat_robot
|
||||
|
||||
104
app/api/api_sketch_to_garment.py
Normal file
104
app/api/api_sketch_to_garment.py
Normal file
@@ -0,0 +1,104 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from app.schemas.response_template import ResponseModel
|
||||
from app.schemas.sketch_to_garment_schemas import SketchToGarmentModel
|
||||
from app.service.sketch2garment.server import submit_sketch_to_garment_task
|
||||
|
||||
logger = logging.getLogger()
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/sketch_to_garment")
|
||||
def sketch_to_garment_api(request_item: SketchToGarmentModel):
|
||||
"""
|
||||
### 接口说明:
|
||||
将图片转换为3D模型(异步处理)。接口接收请求后立即返回任务ID,后台通过 Celery 处理,处理完成后结果会通过 RabbitMQ 发送。
|
||||
|
||||
### 参数说明:
|
||||
- **input_image_path**: 输入图片路径
|
||||
- **bucket_name**: bucket name
|
||||
- **user_id**: 用户id
|
||||
- **callback_url**: 回调url
|
||||
- **task_id**: 任务id
|
||||
- **model**: 转换模式 文本和图片 ,默认只有图片
|
||||
|
||||
### 请求体示例:
|
||||
**单张图片模式:**
|
||||
```json
|
||||
{
|
||||
"input_image_path": "test/53d38bd5-f77b-4034-ada2-45f1e2ebe00c.png",
|
||||
"bucket_name": "test",
|
||||
"user_id": "string-456",
|
||||
"callback_url": "http://18.167.251.121:10015/api/image/webhook/img-to-3d",
|
||||
"task_id": "string12",
|
||||
"model": "picture"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 输出示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "OK!",
|
||||
"data": {
|
||||
"state": "success",
|
||||
"task_id": "string12",
|
||||
"message": "任务已成功提交,正在后台处理..."
|
||||
}
|
||||
}
|
||||
```
|
||||
### 错误输出
|
||||
参考文档: https://platform.tripo3d.ai/docs/error-handling
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"message": "You don’t have enough credit to create this task",
|
||||
"data": {
|
||||
"status": "fail",
|
||||
"task_id": "123",
|
||||
"message": "You don’t have enough credit to create this task",
|
||||
"error": str(e)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
回调请求参数例子:
|
||||
```json
|
||||
{
|
||||
"task_id": "string12",
|
||||
"status": "success",
|
||||
"result": {
|
||||
"pattern": "test/string-456/pattern_making/now_string-456_pattern.png",
|
||||
"texture": "test/string-456/pattern_making/now_string-456_texture.png",
|
||||
"glb": "test/string-456/pattern_making/now_string-456_sim.glb",
|
||||
"texture_fabric": "test/string-456/pattern_making/now_string-456_texture_fabric.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
logger.info(f"sketch_to_garment request item is : @@@@@@:{json.dumps(request_item.model_dump(), indent=4)}")
|
||||
result = submit_sketch_to_garment_task(
|
||||
task_id=request_item.task_id,
|
||||
callback_url=request_item.callback_url,
|
||||
bucket_name=request_item.bucket_name,
|
||||
input_image_path=request_item.input_image_path,
|
||||
user_id=request_item.user_id,
|
||||
model=request_item.model
|
||||
)
|
||||
result = {
|
||||
"state": "success",
|
||||
"task_id": request_item.task_id,
|
||||
"message": "任务已成功提交,正在后台处理...",
|
||||
}
|
||||
state_code = 200
|
||||
return ResponseModel(data=result, code=state_code)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"super_resolution Run Exception @@@@@@:{e}")
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
@@ -1,75 +1,91 @@
|
||||
import logging
|
||||
from typing import Dict, Any
|
||||
|
||||
import yaml
|
||||
from pydantic import Field
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from v2.nacos import ClientConfigBuilder, GRPCConfig, NacosConfigService, ConfigParam, NacosNamingService, RegisterInstanceParam, DeregisterInstanceParam
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ====================== Nacos 配置 ======================
|
||||
NACOS_SERVER_ADDRESSES = "18.167.251.121:28848"
|
||||
NACOS_NAMESPACE = "zcr"
|
||||
NACOS_USERNAME = "nacos"
|
||||
NACOS_PASSWORD = "Aidlab123123!"
|
||||
NACOS_GROUP = "LOCAL"
|
||||
NACOS_DATA_ID = "aida.python"
|
||||
SERVICE_NAME = "fastapi-service" # ←←← 必须修改!建议格式:项目名-环境,例如 ai-image-service-dev
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""
|
||||
应用配置类。Pydantic Settings 会自动从环境变量和 .env 文件中加载这些值。
|
||||
"""
|
||||
model_config = SettingsConfigDict(
|
||||
env_file='.env',
|
||||
env_file_encoding='utf-8',
|
||||
# extra='ignore' # 忽略环境变量中多余的键
|
||||
)
|
||||
|
||||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore") # 忽略环境变量中多余的键
|
||||
# --- 服务端口配置信息 ---
|
||||
PORT: int = Field(default=8001, description="")
|
||||
# --- 服务环境 配置信息 ---
|
||||
SERVE_ENV: str = Field(default='', description="")
|
||||
SERVE_ENV: str = Field(default="", description="")
|
||||
# --- 开发状态 配置信息 ---
|
||||
DEBUG: bool = Field(default=False, description="")
|
||||
# --- 千问api 配置信息 ---
|
||||
QWEN_API_KEY: str = Field(default="", description="")
|
||||
|
||||
# --- ComfyUI 配置信息 ---
|
||||
COMFYUI_SERVER_ADDRESS: str = Field(default='', description="")
|
||||
COMFYUI_SERVER_ADDRESS: str = Field(default="", description="")
|
||||
|
||||
# --- minio 配置信息 ---
|
||||
MINIO_URL: str = Field(default='', description="")
|
||||
MINIO_ACCESS: str = Field(default='', description="")
|
||||
MINIO_SECRET: str = Field(default='', description="")
|
||||
MINIO_URL: str = Field(default="", description="")
|
||||
MINIO_ACCESS: str = Field(default="", description="")
|
||||
MINIO_SECRET: str = Field(default="", description="")
|
||||
MINIO_SECURE: bool = Field(default=True, description="")
|
||||
|
||||
# --- redis 配置信息 ---
|
||||
REDIS_HOST: str = Field(default='', description="")
|
||||
REDIS_PORT: str = Field(default='', description="")
|
||||
REDIS_HOST: str = Field(default="", description="")
|
||||
REDIS_PORT: str = Field(default="", description="")
|
||||
REDIS_DB: int = Field(default=0, description="")
|
||||
|
||||
# --- mysql 配置信息 ---
|
||||
MYSQL_HOST: str = Field(default='', description="")
|
||||
MYSQL_HOST: str = Field(default="", description="")
|
||||
MYSQL_PORT: int = Field(default=3306, description="")
|
||||
MYSQL_USER: str = Field(default='', description="")
|
||||
MYSQL_PASSWORD: str = Field(default='', description="")
|
||||
MYSQL_DB: str = Field(default='', description="")
|
||||
MYSQL_CHARSET: str = Field(default='utf8mb4', description="")
|
||||
MYSQL_USER: str = Field(default="", description="")
|
||||
MYSQL_PASSWORD: str = Field(default="", description="")
|
||||
MYSQL_DB: str = Field(default="", description="")
|
||||
MYSQL_CHARSET: str = Field(default="utf8mb4", description="")
|
||||
|
||||
# --- rabbit-mq 配置信息 ---
|
||||
MQ_HOST: str = Field(default='', description="")
|
||||
MQ_PORT: str = Field(default='', description="")
|
||||
MQ_USERNAME: str = Field(default='', description="")
|
||||
MQ_PASSWORD: str = Field(default='', description="")
|
||||
MQ_VIRTUAL_HOST: str = Field(default='/', description="")
|
||||
MQ_ENV: str = Field(default='', description="")
|
||||
MQ_HOST: str = Field(default="", description="")
|
||||
MQ_PORT: str = Field(default="", description="")
|
||||
MQ_USERNAME: str = Field(default="", description="")
|
||||
MQ_PASSWORD: str = Field(default="", description="")
|
||||
MQ_VIRTUAL_HOST: str = Field(default="/", description="")
|
||||
MQ_ENV: str = Field(default="", description="")
|
||||
|
||||
# --- milvus 配置信息 ---
|
||||
MILVUS_URL: str = Field(default='', description="")
|
||||
MILVUS_TOKEN: str = Field(default='', description="")
|
||||
MILVUS_ALIAS: str = Field(default='', description="")
|
||||
MILVUS_URL: str = Field(default="", description="")
|
||||
MILVUS_TOKEN: str = Field(default="", description="")
|
||||
MILVUS_ALIAS: str = Field(default="", description="")
|
||||
|
||||
# --- ollama 配置信息 ---
|
||||
CHROMADB_PATH: str = Field(default='', description="")
|
||||
CHROMADB_PATH: str = Field(default="", description="")
|
||||
|
||||
# --- ollama 配置信息 ---
|
||||
OLLAMA_URL: str = Field(default='', description="")
|
||||
OLLAMA_URL: str = Field(default="", description="")
|
||||
|
||||
# --- Design Callback Java 接口 ---
|
||||
JAVA_STREAM_API_URL: str = Field(default='', description="")
|
||||
JAVA_STREAM_API_URL: str = Field(default="", description="")
|
||||
|
||||
# --- flux2 klein model url ---
|
||||
FLUX2_GEN_IMG_MODEL_URL: str = Field(default='', description="")
|
||||
FLUX2_GEN_IMG_MODEL_URL: str = Field(default="", description="")
|
||||
|
||||
# --- 服务器IP ---
|
||||
A6000_SERVICE_HOST: str = Field(default='', description="")
|
||||
B_4_X_4090_SERVICE_HOST: str = Field(default='', description="")
|
||||
A6000_SERVICE_HOST: str = Field(default="", description="")
|
||||
B_4_X_4090_SERVICE_HOST: str = Field(default="", description="")
|
||||
|
||||
# --- sketch to garment 模型url ---
|
||||
SKETCH_TO_GARMENT_URL: str = Field(default='', description="")
|
||||
|
||||
# --- 其他配置信息 以下均为Docker容器内配置---
|
||||
LOGS_PATH: str = Field(default="/logs/", description="")
|
||||
@@ -77,6 +93,7 @@ class Settings(BaseSettings):
|
||||
SEG_CACHE_PATH: str = Field(default="/seg_cache/", description="")
|
||||
RECOMMEND_PATH_PREFIX: str = Field(default="/app/service/recommend/", description="")
|
||||
SERVE_PORT: int = Field(default=2010, description="")
|
||||
sketch_to_garment_url: str = Field(default="", description="")
|
||||
|
||||
|
||||
settings = Settings()
|
||||
@@ -91,36 +108,49 @@ TABLE_CATEGORIES = {
|
||||
"female_blouse": "female/blouse",
|
||||
"male_tops": "male/tops",
|
||||
"male_bottoms": "male/bottoms",
|
||||
"male_outwear": "male/outwear"
|
||||
"male_outwear": "male/outwear",
|
||||
}
|
||||
# Design前后排优先级
|
||||
PRIORITY_DICT = {
|
||||
'earring_front': 99,
|
||||
'bag_front': 98,
|
||||
'hairstyle_front': 97,
|
||||
'outwear_front': 20,
|
||||
'tops_front': 19,
|
||||
'dress_front': 18,
|
||||
'blouse_front': 17,
|
||||
'skirt_front': 16,
|
||||
'trousers_front': 15,
|
||||
'bottoms_front': 14,
|
||||
'shoes_right': 1,
|
||||
'shoes_left': 1,
|
||||
'body': 0,
|
||||
'bottoms_back': -14,
|
||||
'trousers_back': -15,
|
||||
'skirt_back': -16,
|
||||
'blouse_back': -17,
|
||||
'dress_back': -18,
|
||||
'tops_back': -19,
|
||||
'outwear_back': -20,
|
||||
'hairstyle_back': -97,
|
||||
'bag_back': -98,
|
||||
'earring_back': -99,
|
||||
"earring_front": 99,
|
||||
"bag_front": 98,
|
||||
"hairstyle_front": 97,
|
||||
"outwear_front": 20,
|
||||
"tops_front": 19,
|
||||
"dress_front": 18,
|
||||
"blouse_front": 17,
|
||||
"skirt_front": 16,
|
||||
"trousers_front": 15,
|
||||
"bottoms_front": 14,
|
||||
"shoes_right": 1,
|
||||
"shoes_left": 1,
|
||||
"body": 0,
|
||||
"bottoms_back": -14,
|
||||
"trousers_back": -15,
|
||||
"skirt_back": -16,
|
||||
"blouse_back": -17,
|
||||
"dress_back": -18,
|
||||
"tops_back": -19,
|
||||
"outwear_back": -20,
|
||||
"hairstyle_back": -97,
|
||||
"bag_back": -98,
|
||||
"earring_back": -99,
|
||||
}
|
||||
# Design 关键点字段
|
||||
KEYPOINT_RESULT_TABLE_FIELD_SET = ('neckline_left', 'neckline_right', 'shoulder_left', 'shoulder_right', 'armpit_left', 'armpit_right', 'cuff_left_in', 'cuff_left_out', 'cuff_right_in', 'cuff_right_out', 'waistband_left', 'waistband_right')
|
||||
KEYPOINT_RESULT_TABLE_FIELD_SET = (
|
||||
"neckline_left",
|
||||
"neckline_right",
|
||||
"shoulder_left",
|
||||
"shoulder_right",
|
||||
"armpit_left",
|
||||
"armpit_right",
|
||||
"cuff_left_in",
|
||||
"cuff_left_out",
|
||||
"cuff_right_in",
|
||||
"cuff_right_out",
|
||||
"waistband_left",
|
||||
"waistband_right",
|
||||
)
|
||||
# milvus配置信息
|
||||
MILVUS_TABLE_KEYPOINT = "keypoint_cache_2"
|
||||
|
||||
@@ -129,37 +159,37 @@ OLLAMA_URL = f"http://{settings.A6000_SERVICE_HOST}:11434/api/embeddings"
|
||||
|
||||
"""Triton Server Config"""
|
||||
# Design
|
||||
DESIGN_MODEL_URL = f'{settings.A6000_SERVICE_HOST}:10000'
|
||||
DESIGN_MODEL_NAME = 'seg_knet'
|
||||
DESIGN_MODEL_URL = f"{settings.A6000_SERVICE_HOST}:10000"
|
||||
DESIGN_MODEL_NAME = "seg_knet"
|
||||
# Seg Product
|
||||
SEG_PRODUCT_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:30000'
|
||||
SEG_PRODUCT_MODEL_URL = f"{settings.B_4_X_4090_SERVICE_HOST}:30000"
|
||||
# Generate Image
|
||||
GI_MODEL_URL = f'{settings.A6000_SERVICE_HOST}:10061'
|
||||
GI_MODEL_NAME = 'flux'
|
||||
GI_MODEL_URL = f"{settings.A6000_SERVICE_HOST}:10061"
|
||||
GI_MODEL_NAME = "flux"
|
||||
# Generate Single Logo
|
||||
GSL_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10041'
|
||||
GSL_MODEL_NAME = 'stable_diffusion_xl_transparent'
|
||||
GSL_MODEL_URL = f"{settings.B_4_X_4090_SERVICE_HOST}:10041"
|
||||
GSL_MODEL_NAME = "stable_diffusion_xl_transparent"
|
||||
# Generate Product (整套和单品)
|
||||
GPI_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10051'
|
||||
GPI_MODEL_NAME_OVERALL = 'diffusion_ensemble_all'
|
||||
GPI_MODEL_NAME_SINGLE = 'stable_diffusion_1_5_cnet'
|
||||
GPI_MODEL_URL = f"{settings.B_4_X_4090_SERVICE_HOST}:10051"
|
||||
GPI_MODEL_NAME_OVERALL = "diffusion_ensemble_all"
|
||||
GPI_MODEL_NAME_SINGLE = "stable_diffusion_1_5_cnet"
|
||||
|
||||
# 以下停用中...*************
|
||||
# 多视角生成
|
||||
GMV_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10081'
|
||||
GMV_MODEL_NAME = 'multi_view'
|
||||
GMV_MODEL_URL = f"{settings.B_4_X_4090_SERVICE_HOST}:10081"
|
||||
GMV_MODEL_NAME = "multi_view"
|
||||
# 超分
|
||||
SR_MODEL_NAME = "super_resolution"
|
||||
SR_TRITON_URL = f"{settings.A6000_SERVICE_HOST}:10031"
|
||||
# 打光
|
||||
GRI_MODEL_URL = f'{settings.A6000_SERVICE_HOST}:10051'
|
||||
GRI_MODEL_NAME_OVERALL = 'diffusion_relight_ensemble'
|
||||
GRI_MODEL_NAME_SINGLE = 'stable_diffusion_1_5_relight'
|
||||
GRI_MODEL_URL = f"{settings.A6000_SERVICE_HOST}:10051"
|
||||
GRI_MODEL_NAME_OVERALL = "diffusion_relight_ensemble"
|
||||
GRI_MODEL_NAME_SINGLE = "stable_diffusion_1_5_relight"
|
||||
# agent 图片生成
|
||||
FAST_GI_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10011'
|
||||
FAST_GI_MODEL_NAME = 'stable_diffusion_xl'
|
||||
FAST_GI_MODEL_URL = f"{settings.B_4_X_4090_SERVICE_HOST}:10011"
|
||||
FAST_GI_MODEL_NAME = "stable_diffusion_xl"
|
||||
# 图转视频 triton版
|
||||
PT_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10061'
|
||||
PT_MODEL_URL = f"{settings.B_4_X_4090_SERVICE_HOST}:10061"
|
||||
|
||||
# *************
|
||||
|
||||
|
||||
@@ -1,343 +0,0 @@
|
||||
import logging
|
||||
import socket
|
||||
from typing import Dict, Any
|
||||
|
||||
import yaml
|
||||
from pydantic import Field
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from v2.nacos import ClientConfigBuilder, GRPCConfig, NacosConfigService, ConfigParam, NacosNamingService, RegisterInstanceParam, DeregisterInstanceParam
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ====================== Nacos 配置 ======================
|
||||
NACOS_SERVER_ADDRESSES = "18.167.251.121:28848"
|
||||
NACOS_NAMESPACE = "zcr"
|
||||
NACOS_USERNAME = "nacos"
|
||||
NACOS_PASSWORD = "Aidlab123123!"
|
||||
NACOS_GROUP = "LOCAL"
|
||||
NACOS_DATA_ID = "aida.python"
|
||||
SERVICE_NAME = "fastapi-service" # ←←← 必须修改!建议格式:项目名-环境,例如 ai-image-service-dev
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""
|
||||
应用配置类。Pydantic Settings 会自动从环境变量和 .env 文件中加载这些值。
|
||||
"""
|
||||
model_config = SettingsConfigDict(
|
||||
env_file='.env',
|
||||
env_file_encoding='utf-8',
|
||||
# extra='ignore' # 忽略环境变量中多余的键
|
||||
)
|
||||
# --- 服务端口配置信息 ---
|
||||
PORT: int = Field(default=8001, description="")
|
||||
# --- 服务环境 配置信息 ---
|
||||
SERVE_ENV: str = Field(default='', description="")
|
||||
# --- 开发状态 配置信息 ---
|
||||
DEBUG: bool = Field(default=False, description="")
|
||||
# --- 千问api 配置信息 ---
|
||||
QWEN_API_KEY: str = Field(default="", description="")
|
||||
|
||||
# --- ComfyUI 配置信息 ---
|
||||
COMFYUI_SERVER_ADDRESS: str = Field(default='', description="")
|
||||
|
||||
# --- minio 配置信息 ---
|
||||
MINIO_URL: str = Field(default='', description="")
|
||||
MINIO_ACCESS: str = Field(default='', description="")
|
||||
MINIO_SECRET: str = Field(default='', description="")
|
||||
MINIO_SECURE: bool = Field(default=True, description="")
|
||||
|
||||
# --- redis 配置信息 ---
|
||||
REDIS_HOST: str = Field(default='', description="")
|
||||
REDIS_PORT: str = Field(default='', description="")
|
||||
REDIS_DB: int = Field(default=0, description="")
|
||||
|
||||
# --- mysql 配置信息 ---
|
||||
MYSQL_HOST: str = Field(default='', description="")
|
||||
MYSQL_PORT: int = Field(default=3306, description="")
|
||||
MYSQL_USER: str = Field(default='', description="")
|
||||
MYSQL_PASSWORD: str = Field(default='', description="")
|
||||
MYSQL_DB: str = Field(default='', description="")
|
||||
MYSQL_CHARSET: str = Field(default='utf8mb4', description="")
|
||||
|
||||
# --- rabbit-mq 配置信息 ---
|
||||
MQ_HOST: str = Field(default='', description="")
|
||||
MQ_PORT: str = Field(default='', description="")
|
||||
MQ_USERNAME: str = Field(default='', description="")
|
||||
MQ_PASSWORD: str = Field(default='', description="")
|
||||
MQ_VIRTUAL_HOST: str = Field(default='/', description="")
|
||||
MQ_ENV: str = Field(default='', description="")
|
||||
|
||||
# --- milvus 配置信息 ---
|
||||
MILVUS_URL: str = Field(default='', description="")
|
||||
MILVUS_TOKEN: str = Field(default='', description="")
|
||||
MILVUS_ALIAS: str = Field(default='', description="")
|
||||
|
||||
# --- ollama 配置信息 ---
|
||||
CHROMADB_PATH: str = Field(default='', description="")
|
||||
|
||||
# --- ollama 配置信息 ---
|
||||
OLLAMA_URL: str = Field(default='', description="")
|
||||
|
||||
# --- Design Callback Java 接口 ---
|
||||
JAVA_STREAM_API_URL: str = Field(default='', description="")
|
||||
|
||||
# --- flux2 klein model url ---
|
||||
FLUX2_GEN_IMG_MODEL_URL: str = Field(default='', description="")
|
||||
|
||||
# --- 服务器IP ---
|
||||
A6000_SERVICE_HOST: str = Field(default='', description="")
|
||||
B_4_X_4090_SERVICE_HOST: str = Field(default='', description="")
|
||||
|
||||
# --- 其他配置信息 以下均为Docker容器内配置---
|
||||
LOGS_PATH: str = Field(default="/logs/", description="")
|
||||
CATEGORY_PATH: str = Field(default="/app/service/attribute/config/descriptor/category/category_dis.csv", description="")
|
||||
SEG_CACHE_PATH: str = Field(default="/seg_cache/", description="")
|
||||
RECOMMEND_PATH_PREFIX: str = Field(default="/app/service/recommend/", description="")
|
||||
SERVE_PORT: int = Field(default=2010, description="")
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
# ====================== Nacos 配置管理 ======================
|
||||
|
||||
client_config = (ClientConfigBuilder()
|
||||
.server_address(NACOS_SERVER_ADDRESSES)
|
||||
.username(NACOS_USERNAME)
|
||||
.password(NACOS_PASSWORD)
|
||||
.namespace_id(NACOS_NAMESPACE)
|
||||
.log_level('INFO')
|
||||
.grpc_config(GRPCConfig(grpc_timeout=5000))
|
||||
.build())
|
||||
|
||||
# ====================== Nacos 配置管理 ======================
|
||||
nacos_config_data: Dict[str, Any] = {}
|
||||
nacos_config_client = None
|
||||
async def load_nacos_config() -> None:
|
||||
"""初始化 Nacos 配置并监听变化"""
|
||||
global nacos_config_data, settings
|
||||
|
||||
try:
|
||||
client = await NacosConfigService.create_config_service(client_config)
|
||||
|
||||
# 1. 第一次获取配置
|
||||
content = await client.get_config(ConfigParam(
|
||||
data_id=NACOS_DATA_ID,
|
||||
group=NACOS_GROUP
|
||||
))
|
||||
|
||||
if content:
|
||||
loaded = yaml.safe_load(content) or {}
|
||||
nacos_config_data = loaded
|
||||
# 用 Nacos 配置覆盖 settings
|
||||
for key, value in loaded.items():
|
||||
if hasattr(settings, key):
|
||||
setattr(settings, key, value)
|
||||
logger.info(f"✅ Nacos 配置加载成功: {NACOS_DATA_ID} | 覆盖字段数量: {len(loaded)}")
|
||||
else:
|
||||
logger.warning("Nacos 返回配置为空,使用 .env + 默认值")
|
||||
|
||||
# 2. 注册动态监听器(配置变更自动刷新)
|
||||
async def listener(tenant: str, data_id: str, group: str, content: str):
|
||||
global nacos_config_data, settings
|
||||
try:
|
||||
new_config = yaml.safe_load(content) if content else {}
|
||||
nacos_config_data = new_config
|
||||
# 实时覆盖 settings
|
||||
for key, value in new_config.items():
|
||||
if hasattr(settings, key):
|
||||
old_val = getattr(settings, key)
|
||||
setattr(settings, key, value)
|
||||
if old_val != value:
|
||||
logger.info(f"🔄 配置更新 → {key}: {old_val} → {value}")
|
||||
logger.info(f"【Nacos 动态更新】{NACOS_DATA_ID}")
|
||||
except Exception as e:
|
||||
logger.error(f"Nacos 配置解析失败: {e}")
|
||||
|
||||
await client.add_listener(NACOS_DATA_ID, NACOS_GROUP, listener)
|
||||
logger.info("✅ Nacos 配置监听器已注册(支持热更新)")
|
||||
|
||||
await register_service_to_nacos()
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Nacos 初始化失败: {e},将仅使用 .env 配置")
|
||||
|
||||
|
||||
async def register_service_to_nacos():
|
||||
"""启动时把服务注册到 Nacos"""
|
||||
global nacos_config_client
|
||||
|
||||
nacos_config_client = await NacosConfigService.create_config_service(client_config)
|
||||
|
||||
if not nacos_config_client: # 如果配置客户端都没连上,就不注册
|
||||
logger.warning("Nacos 配置客户端未初始化,跳过服务注册")
|
||||
return
|
||||
|
||||
try:
|
||||
nacos_naming_client = await NacosNamingService.create_naming_service(client_config)
|
||||
|
||||
# 获取服务 IP(生产环境建议通过环境变量传入,避免 Docker/K8s 内获取错误)
|
||||
host_ip = socket.gethostbyname(socket.gethostname())
|
||||
if not host_ip or host_ip.startswith('127.'):
|
||||
host_ip = "127.0.0.1" # 本地测试用
|
||||
|
||||
param = RegisterInstanceParam(
|
||||
service_name="aida.python",
|
||||
group_name=NACOS_GROUP,
|
||||
ip=host_ip,
|
||||
port=settings.PORT, # 使用你 settings 中的 PORT
|
||||
cluster_name="DEFAULT",
|
||||
weight=1.0,
|
||||
metadata={
|
||||
"version": "1.0.0",
|
||||
"env": settings.SERVE_ENV,
|
||||
"framework": "fastapi",
|
||||
"debug": str(settings.DEBUG),
|
||||
},
|
||||
enabled=True,
|
||||
healthy=True,
|
||||
ephemeral=True, # 临时实例,推荐生产使用
|
||||
)
|
||||
|
||||
await nacos_naming_client.register_instance(request=param)
|
||||
logger.info(f"✅ 服务已成功注册到 Nacos! → {SERVICE_NAME} | {host_ip}:{settings.PORT} | env={settings.SERVE_ENV}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 服务注册到 Nacos 失败: {e}")
|
||||
|
||||
|
||||
async def deregister_service_from_nacos():
|
||||
"""服务关闭时优雅注销(防止 Nacos 长时间显示不健康实例)"""
|
||||
try:
|
||||
nacos_naming_client = await NacosNamingService.create_naming_service(client_config)
|
||||
|
||||
host_ip = socket.gethostbyname(socket.gethostname()) or "127.0.0.1"
|
||||
param = DeregisterInstanceParam(
|
||||
service_name=SERVICE_NAME,
|
||||
group_name=NACOS_GROUP,
|
||||
ip=host_ip,
|
||||
port=settings.PORT,
|
||||
cluster_name='c1',
|
||||
ephemeral=True,
|
||||
)
|
||||
await nacos_naming_client.deregister_instance(request=param)
|
||||
logger.info(f"✅ 服务已从 Nacos 注销 → {SERVICE_NAME}")
|
||||
except Exception as e:
|
||||
logger.warning(f"服务注销时出现异常(通常可忽略): {e}")
|
||||
|
||||
|
||||
|
||||
|
||||
# 提供给 FastAPI 的依赖
|
||||
def get_settings() -> Settings:
|
||||
return settings
|
||||
|
||||
|
||||
"""Design 服务"""
|
||||
# 推荐服装类别映射
|
||||
TABLE_CATEGORIES = {
|
||||
"female_dress": "female/dress",
|
||||
"female_outwear": "female/outwear",
|
||||
"female_trousers": "female/trousers",
|
||||
"female_skirt": "female/skirt",
|
||||
"female_blouse": "female/blouse",
|
||||
"male_tops": "male/tops",
|
||||
"male_bottoms": "male/bottoms",
|
||||
"male_outwear": "male/outwear"
|
||||
}
|
||||
# Design前后排优先级
|
||||
PRIORITY_DICT = {
|
||||
'earring_front': 99,
|
||||
'bag_front': 98,
|
||||
'hairstyle_front': 97,
|
||||
'outwear_front': 20,
|
||||
'tops_front': 19,
|
||||
'dress_front': 18,
|
||||
'blouse_front': 17,
|
||||
'skirt_front': 16,
|
||||
'trousers_front': 15,
|
||||
'bottoms_front': 14,
|
||||
'shoes_right': 1,
|
||||
'shoes_left': 1,
|
||||
'body': 0,
|
||||
'bottoms_back': -14,
|
||||
'trousers_back': -15,
|
||||
'skirt_back': -16,
|
||||
'blouse_back': -17,
|
||||
'dress_back': -18,
|
||||
'tops_back': -19,
|
||||
'outwear_back': -20,
|
||||
'hairstyle_back': -97,
|
||||
'bag_back': -98,
|
||||
'earring_back': -99,
|
||||
}
|
||||
# Design 关键点字段
|
||||
KEYPOINT_RESULT_TABLE_FIELD_SET = ('neckline_left', 'neckline_right', 'shoulder_left', 'shoulder_right', 'armpit_left', 'armpit_right', 'cuff_left_in', 'cuff_left_out', 'cuff_right_in', 'cuff_right_out', 'waistband_left', 'waistband_right')
|
||||
# milvus配置信息
|
||||
MILVUS_TABLE_KEYPOINT = "keypoint_cache_2"
|
||||
|
||||
# ollama 地址
|
||||
OLLAMA_URL = f"http://{settings.A6000_SERVICE_HOST}:11434/api/embeddings"
|
||||
|
||||
"""Triton Server Config"""
|
||||
# Design
|
||||
DESIGN_MODEL_URL = f'{settings.A6000_SERVICE_HOST}:10000'
|
||||
DESIGN_MODEL_NAME = 'seg_knet'
|
||||
# Seg Product
|
||||
SEG_PRODUCT_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:30000'
|
||||
# Generate Image
|
||||
GI_MODEL_URL = f'{settings.A6000_SERVICE_HOST}:10061'
|
||||
GI_MODEL_NAME = 'flux'
|
||||
# Generate Single Logo
|
||||
GSL_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10041'
|
||||
GSL_MODEL_NAME = 'stable_diffusion_xl_transparent'
|
||||
# Generate Product (整套和单品)
|
||||
GPI_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10051'
|
||||
GPI_MODEL_NAME_OVERALL = 'diffusion_ensemble_all'
|
||||
GPI_MODEL_NAME_SINGLE = 'stable_diffusion_1_5_cnet'
|
||||
|
||||
# 以下停用中...*************
|
||||
# 多视角生成
|
||||
GMV_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10081'
|
||||
GMV_MODEL_NAME = 'multi_view'
|
||||
# 超分
|
||||
SR_MODEL_NAME = "super_resolution"
|
||||
SR_TRITON_URL = f"{settings.A6000_SERVICE_HOST}:10031"
|
||||
# 打光
|
||||
GRI_MODEL_URL = f'{settings.A6000_SERVICE_HOST}:10051'
|
||||
GRI_MODEL_NAME_OVERALL = 'diffusion_relight_ensemble'
|
||||
GRI_MODEL_NAME_SINGLE = 'stable_diffusion_1_5_relight'
|
||||
# agent 图片生成
|
||||
FAST_GI_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10011'
|
||||
FAST_GI_MODEL_NAME = 'stable_diffusion_xl'
|
||||
# 图转视频 triton版
|
||||
PT_MODEL_URL = f'{settings.B_4_X_4090_SERVICE_HOST}:10061'
|
||||
|
||||
# *************
|
||||
|
||||
"""MQ 队列信息"""
|
||||
# 生成图片 moodboard printboard sketchboard
|
||||
GI_RABBITMQ_QUEUES = f"GenerateImage-{settings.SERVE_ENV}"
|
||||
# 生成slogan
|
||||
SLOGAN_RABBITMQ_QUEUES = f"Slogan-{settings.SERVE_ENV}"
|
||||
# 转产品图
|
||||
GPI_RABBITMQ_QUEUES = f"ToProductImage-{settings.SERVE_ENV}"
|
||||
# 产品图转视频
|
||||
PS_RABBITMQ_QUEUES = f"PoseTransform-{settings.SERVE_ENV}"
|
||||
|
||||
# 以下停用中...*************
|
||||
# 产品图打光
|
||||
GRI_RABBITMQ_QUEUES = f"Relight-{settings.SERVE_ENV}"
|
||||
# 超分
|
||||
SR_RABBITMQ_QUEUES = f"SuperResolution-{settings.SERVE_ENV}"
|
||||
# 生成多视图
|
||||
GMV_RABBITMQ_QUEUES = f"GenerateMultiView-{settings.SERVE_ENV}"
|
||||
# 批量转产品图
|
||||
BATCH_GPI_RABBITMQ_QUEUES = f"BatchToProductImage-{settings.SERVE_ENV}"
|
||||
# 批量打光
|
||||
BATCH_GRI_RABBITMQ_QUEUES = f"BatchRelight-{settings.SERVE_ENV}"
|
||||
# 批量图片转视频
|
||||
BATCH_PS_RABBITMQ_QUEUES = f"BatchPoseTransform-{settings.SERVE_ENV}"
|
||||
# 批量design
|
||||
BATCH_DESIGN_RABBITMQ_QUEUES = f"DesignBatch-{settings.SERVE_ENV}"
|
||||
# *************
|
||||
@@ -1,108 +0,0 @@
|
||||
import logging
|
||||
import yaml
|
||||
import nacos
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
logger = logging.getLogger("nacos")
|
||||
|
||||
# client config
|
||||
NACOS_SERVER_ADDRESSES = "18.167.251.121:28848"
|
||||
NACOS_NAMESPACE = "zcr"
|
||||
NACOS_USERNAME = "nacos"
|
||||
NACOS_PASSWORD = "Aidlab123123!"
|
||||
|
||||
# nacos config info
|
||||
NACOS_CONFIG_GROUP = "LOCAL"
|
||||
NACOS_CONFIG_DATA_ID = "aida.python"
|
||||
|
||||
# nacos server config
|
||||
NACOS_SERVICE_NAME = "AiDA-DEV" # ←←← 必须修改!建议格式:项目名-环境,例如 ai-image-service-dev
|
||||
NACOS_SERVICE_IP = "127.0.0.1"
|
||||
NACOS_SERVICE_PORT = 8445
|
||||
|
||||
# nacos client
|
||||
client = nacos.NacosClient(
|
||||
server_addresses=NACOS_SERVER_ADDRESSES,
|
||||
namespace=NACOS_NAMESPACE,
|
||||
username=NACOS_USERNAME,
|
||||
password=NACOS_PASSWORD
|
||||
)
|
||||
|
||||
|
||||
def listener_config_callback(args):
|
||||
data_id = args['data_id']
|
||||
namespace = args['namespace']
|
||||
group = args['group']
|
||||
content = args['content']
|
||||
logger.info("【Nacos】配置")
|
||||
try:
|
||||
logger.info(f"【Nacos】 动态更新 : data_id : {data_id} | namespace : {namespace} | group_name: {group}")
|
||||
new_config = yaml.safe_load(content) if content else {}
|
||||
for key, value in new_config.items():
|
||||
if hasattr(settings, key):
|
||||
old_val = getattr(settings, key)
|
||||
setattr(settings, key, value)
|
||||
if old_val != value:
|
||||
logger.info(f"🔄 配置更新 → {key}: {old_val} → {value}")
|
||||
except Exception as e:
|
||||
logger.error(f"【Nacos】 配置解析失败: {e}")
|
||||
|
||||
|
||||
def remove_config_callback(args):
|
||||
data_id = args['data_id']
|
||||
namespace = args['namespace']
|
||||
print(f" remove_config_callback : {data_id} | namespace : {namespace}")
|
||||
|
||||
|
||||
def load_nacos_config():
|
||||
"""初始化 Nacos 配置并监听变化"""
|
||||
logger.info(f"【Nacos】 配置订阅 - 初次获取配置信息")
|
||||
|
||||
try:
|
||||
# 1. 第一次获取配置
|
||||
content = client.get_config(data_id=NACOS_CONFIG_DATA_ID, group=NACOS_CONFIG_GROUP)
|
||||
if content:
|
||||
loaded = yaml.safe_load(content) or {}
|
||||
for key, value in loaded.items():
|
||||
if hasattr(settings, key):
|
||||
setattr(settings, key, value)
|
||||
logger.info(f"【Nacos】✅ 配置加载成功: {NACOS_CONFIG_DATA_ID} | 覆盖字段数量: {len(loaded)}")
|
||||
else:
|
||||
logger.warning("【Nacos】 返回配置为空,使用 .env + 默认值")
|
||||
|
||||
client.add_config_watcher(data_id=NACOS_CONFIG_DATA_ID, group=NACOS_CONFIG_GROUP, cb=listener_config_callback)
|
||||
logger.info("【Nacos】✅ 配置监听器已注册(支持热更新)")
|
||||
except Exception as e:
|
||||
logger.error(f"【Nacos】❌ 初始化失败: {e},将仅使用 .env 配置")
|
||||
finally:
|
||||
client.remove_config_watcher(
|
||||
data_id=NACOS_CONFIG_DATA_ID,
|
||||
group=NACOS_CONFIG_GROUP,
|
||||
cb=remove_config_callback
|
||||
)
|
||||
|
||||
|
||||
def register_server():
|
||||
logger.info(f"nacos 服务注册")
|
||||
try:
|
||||
client.add_naming_instance(
|
||||
service_name=NACOS_SERVICE_NAME,
|
||||
ip=NACOS_SERVICE_IP,
|
||||
port=NACOS_SERVICE_PORT,
|
||||
metadata={"status": "ok"},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"【Nacos】❌ 服务注册失败 : {e}")
|
||||
|
||||
|
||||
def deregister_server():
|
||||
logger.info(f"nacos 服务注册")
|
||||
try:
|
||||
client.remove_naming_instance(
|
||||
service_name=NACOS_SERVICE_NAME,
|
||||
ip=NACOS_SERVICE_IP,
|
||||
port=NACOS_SERVICE_PORT
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"【Nacos】❌ 服务注销失败 : {e}")
|
||||
86
app/design_batch/request_data/requests_data.json
Normal file
86
app/design_batch/request_data/requests_data.json
Normal file
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"objects": [
|
||||
{
|
||||
"basic": {
|
||||
"body_point_test": {
|
||||
"waistband_right": [
|
||||
201,
|
||||
242
|
||||
],
|
||||
"hand_point_right": [
|
||||
222,
|
||||
312
|
||||
],
|
||||
"waistband_left": [
|
||||
114,
|
||||
243
|
||||
],
|
||||
"hand_point_left": [
|
||||
94,
|
||||
310
|
||||
],
|
||||
"shoulder_left": [
|
||||
102,
|
||||
116
|
||||
],
|
||||
"shoulder_right": [
|
||||
211,
|
||||
115
|
||||
]
|
||||
},
|
||||
"layer_order": true,
|
||||
"scale_bag": 0.7,
|
||||
"scale_earrings": 0.16,
|
||||
"self_template": true,
|
||||
"single_overall": "overall",
|
||||
"switch_category": ""
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"businessId": 264931,
|
||||
"color": "145 220 232",
|
||||
"image_id": 96844,
|
||||
"offset": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"path": "aida-users/87/sketch/2aa7aad5-74bb-41fa-9cdf-f06611b3e89a-2-87.png",
|
||||
"print": {
|
||||
"element": {
|
||||
"element_angle_list": [],
|
||||
"element_path_list": [],
|
||||
"element_scale_list": [],
|
||||
"location": []
|
||||
},
|
||||
"overall": {
|
||||
"location": [],
|
||||
"print_angle_list": [],
|
||||
"print_path_list": [],
|
||||
"print_scale_list": []
|
||||
},
|
||||
"single": {
|
||||
"location": [],
|
||||
"print_angle_list": [],
|
||||
"print_path_list": [],
|
||||
"print_scale_list": []
|
||||
}
|
||||
},
|
||||
"priority": 10,
|
||||
"resize_scale": [
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"type": "Dress"
|
||||
},
|
||||
{
|
||||
"body_path": "aida-sys-image/models/female/79805ec3-3f01-466d-91e0-36028d079699.png",
|
||||
"image_id": 95444,
|
||||
"type": "Body"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"process_id": "87",
|
||||
"tasks_id": ""
|
||||
}
|
||||
|
||||
22
app/main.py
22
app/main.py
@@ -1,5 +1,8 @@
|
||||
# 1. 这里的顺序至关重要!必须在最顶端
|
||||
import sys
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
# from app.core.nacos_config import load_nacos_config, register_server, deregister_server
|
||||
|
||||
try:
|
||||
import asyncore
|
||||
@@ -30,8 +33,21 @@ logger = logging.getLogger(__name__)
|
||||
load_dotenv()
|
||||
|
||||
|
||||
# @asynccontextmanager
|
||||
# async def lifespan(app: FastAPI):
|
||||
# try:
|
||||
# load_nacos_config()
|
||||
# register_server()
|
||||
#
|
||||
# yield
|
||||
# finally:
|
||||
# deregister_server()
|
||||
# logger.info("lifespan down")
|
||||
|
||||
|
||||
def get_application() -> FastAPI:
|
||||
application = FastAPI(
|
||||
# lifespan=lifespan,
|
||||
docs_url="/docs",
|
||||
redoc_url='/re-docs',
|
||||
openapi_url=f"/openapi.json",
|
||||
@@ -64,5 +80,11 @@ async def http_exception_handler(exc: HTTPException):
|
||||
)
|
||||
|
||||
|
||||
@app.get("/health", operation_id="health")
|
||||
async def health():
|
||||
logger.info("health check")
|
||||
return {"ok": True, "env": settings.APP_ENV}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
uvicorn.run(app, host="0.0.0.0", port=settings.PORT)
|
||||
|
||||
12
app/schemas/sketch_to_garment_schemas.py
Normal file
12
app/schemas/sketch_to_garment_schemas.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class SketchToGarmentModel(BaseModel):
|
||||
input_image_path: str = Field(..., description="输入图片路径列表")
|
||||
bucket_name: str = Field(..., description="输入图片路径列表")
|
||||
user_id: str = Field(..., description="用户id")
|
||||
callback_url: str # 必填,客户端提供的回调地址
|
||||
task_id: str = Field()
|
||||
model: str = Field(default="single", description="模型类型: single 或 multi")
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,170 +0,0 @@
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
|
||||
import requests
|
||||
from dashscope import Generation
|
||||
from requests import RequestException
|
||||
from retry import retry
|
||||
|
||||
from app.core.config import settings
|
||||
from app.service.chat_robot.script.prompt import GET_LANGUAGE_PREFIX
|
||||
from app.service.prompt_generation.util import minio_util
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_assistant_response(messages):
|
||||
response = Generation.call(
|
||||
model='qwen-max',
|
||||
api_key=settings.QWEN_API_KEY,
|
||||
messages=messages,
|
||||
# seed=random.randint(1, 10000), # 设置随机数种子seed,如果没有设置,则随机数种子默认为1234
|
||||
result_format='message', # 将输出设置为message形式
|
||||
enable_search='false'
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
def get_language(message: str) -> str:
|
||||
messages = [
|
||||
{
|
||||
"content": GET_LANGUAGE_PREFIX, # ai message
|
||||
"role": "system"
|
||||
},
|
||||
{
|
||||
"content": "Tree", # 用户message
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"content": "English", # 用户message
|
||||
"role": "assistant"
|
||||
},
|
||||
{
|
||||
"content": "玩具", # 用户message
|
||||
"role": "user"
|
||||
},
|
||||
{
|
||||
"content": "Chinese", # 用户message
|
||||
"role": "assistant"
|
||||
},
|
||||
{
|
||||
"content": message, # 用户message
|
||||
"role": "user"
|
||||
}
|
||||
]
|
||||
|
||||
first_response = get_assistant_response(messages)
|
||||
assistant_output = first_response.output.choices[0].message.content
|
||||
logging.info(f"大模型输出信息:{first_response}\n判断用户输入的语言为:{assistant_output}")
|
||||
# print(f"大模型输出信息:{first_response}\n判断用户输入的语言为:{assistant_output}")
|
||||
return assistant_output
|
||||
|
||||
|
||||
@retry(exceptions=RequestException, tries=3, delay=1)
|
||||
def get_response(messages):
|
||||
response = Generation.call(
|
||||
model='qwen-turbo',
|
||||
api_key=settings.QWEN_API_KEY,
|
||||
messages=messages,
|
||||
# seed=random.randint(1, 10000), # 设置随机数种子seed,如果没有设置,则随机数种子默认为1234
|
||||
result_format='message', # 将输出设置为message形式
|
||||
enable_search='True'
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
def get_translation_from_llama3(text):
|
||||
start_time = time.time()
|
||||
url = f"http://{settings.A6000_SERVICE_HOST}:12434/api/generate"
|
||||
# 先获取用户输入文本的语言
|
||||
language = get_language(text)
|
||||
|
||||
if 'English' in language:
|
||||
return text
|
||||
|
||||
# 创建请求的负载 translator是自定义的翻译模型
|
||||
payload = {
|
||||
"model": "AiDA-translator:latest",
|
||||
"prompt": f"[{text}]",
|
||||
"stream": False
|
||||
}
|
||||
# 将负载转换为 JSON 格式
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
response = requests.post(url, data=json.dumps(payload), headers=headers)
|
||||
# 处理响应
|
||||
if response.status_code == 200:
|
||||
# print("Response from server:")
|
||||
# print(response.json())
|
||||
resp = json.loads(response.content).get("response")
|
||||
logger.info(f"translation server runtime is {time.time() - start_time} , response is {resp}")
|
||||
print("input : {}, translate result : {}".format(text, resp))
|
||||
return resp
|
||||
else:
|
||||
logger.info(f"translation server runtime is {time.time() - start_time} , response is {response.content}")
|
||||
print(f"Request failed with status code {response.status_code}")
|
||||
print(response.text)
|
||||
return ""
|
||||
|
||||
|
||||
# 在llama3中创建一个翻译模型
|
||||
# def create_model_with_llama(text):
|
||||
# url = "http://localhost:11434/api/create"
|
||||
# # url = "http://20.1.1.43:1143/api/generate"
|
||||
#
|
||||
# # prompt = f"System: {prefix_for_llama}\nUser:[{text}]"
|
||||
#
|
||||
# # 创建翻译器的配置文件
|
||||
# payload = {
|
||||
# "model": "translator",
|
||||
# "modelfile": "FROM llama3\nSYSTEM Translate everything within the brackets [] into English."
|
||||
# "Never translate or modify any English input."
|
||||
# "The input must be fully translated into coherent English sentences."
|
||||
# }
|
||||
#
|
||||
# # 将负载转换为 JSON 格式
|
||||
# headers = {'Content-Type': 'application/json'}
|
||||
# response = requests.post(url, data=json.dumps(payload), headers=headers)
|
||||
|
||||
|
||||
def get_prompt_from_image(image_path, text):
|
||||
start_time = time.time()
|
||||
# url = "http://localhost:11434/api/generate"
|
||||
url = f"http://{settings.B_4_X_4090_SERVICE_HOST}:11434/api/generate"
|
||||
|
||||
image_base64 = minio_util.minio_url_to_base64(image_path.img)
|
||||
# image_base64 = minio_url_to_base64(image_path)
|
||||
|
||||
# 创建请求的负载 translator是自定义的翻译模型
|
||||
payload = {
|
||||
"model": "llama3.2-vision",
|
||||
"images": [image_base64],
|
||||
"prompt": f"{text}",
|
||||
"stream": False
|
||||
}
|
||||
# 将负载转换为 JSON 格式
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
response = requests.post(url, data=json.dumps(payload), headers=headers)
|
||||
# 处理响应
|
||||
if response.status_code == 200:
|
||||
# print("Response from server:")
|
||||
# print(response.json())
|
||||
resp = json.loads(response.content).get("response")
|
||||
logger.info(f"sketch re-generate server runtime is {time.time() - start_time} \n, response is {resp}")
|
||||
# print("input : {}, sketch re-generate result : {}".format(text, resp))
|
||||
return resp
|
||||
else:
|
||||
logger.info(f"sketch re-generate server runtime is {time.time() - start_time} , response is {response.content}")
|
||||
print(f"Request failed with status code {response.status_code}")
|
||||
print(response.text)
|
||||
return ""
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function"""
|
||||
text = get_translation_from_llama3("[火焰]")
|
||||
print(text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
35
app/service/sketch2garment/callback.py
Normal file
35
app/service/sketch2garment/callback.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import logging
|
||||
|
||||
import httpx
|
||||
|
||||
logger = logging.getLogger("app")
|
||||
|
||||
|
||||
async def notify_callback(callback_url: str, task_id: str, status: str, result: dict, ):
|
||||
"""
|
||||
调用客户端提供的回调接口
|
||||
"""
|
||||
try:
|
||||
payload = {
|
||||
"task_id": task_id,
|
||||
"status": status,
|
||||
"result": result
|
||||
}
|
||||
logger.info(payload)
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
resp = await client.post(
|
||||
str(callback_url),
|
||||
json=payload,
|
||||
headers={"Content-Type": "application/json"}
|
||||
)
|
||||
|
||||
if 200 <= resp.status_code < 300:
|
||||
logger.info(f"回调成功 | task_id: {task_id} | status: {status} | url: {callback_url}")
|
||||
return True
|
||||
else:
|
||||
logger.warning(f"回调返回非2xx状态码 | task_id: {task_id} | status: {resp.status_code} | url: {callback_url}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"回调失败 | task_id: {task_id} | url: {callback_url} | error: {e}", exc_info=True)
|
||||
return False
|
||||
46
app/service/sketch2garment/celery_app.py
Normal file
46
app/service/sketch2garment/celery_app.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from celery import Celery
|
||||
from kombu import Queue, Exchange
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
celery_app = Celery(
|
||||
"sketch_to_garment",
|
||||
broker=f"redis://{settings.REDIS_HOST}:{settings.REDIS_PORT}/2",
|
||||
backend=f"redis://{settings.REDIS_HOST}:{settings.REDIS_PORT}/{settings.REDIS_DB}",
|
||||
include=["app.service.sketch2garment.tasks"]
|
||||
)
|
||||
print(f"redis://{settings.REDIS_HOST}:{settings.REDIS_PORT}/3")
|
||||
print(f"celery_app: {celery_app}")
|
||||
|
||||
celery_app.conf.update(
|
||||
task_serializer="json",
|
||||
accept_content=["json"],
|
||||
result_serializer="json",
|
||||
timezone="Asia/Hong_Kong",
|
||||
enable_utc=True,
|
||||
task_track_started=True,
|
||||
task_time_limit=300, # 单个任务最长 5 分钟
|
||||
task_soft_time_limit=280,
|
||||
# 定义队列
|
||||
task_queues=(
|
||||
Queue("sketch_to_garment_queue",
|
||||
exchange=Exchange("sketch_to_garment_exchange", type="direct"),
|
||||
durable=True),
|
||||
|
||||
),
|
||||
|
||||
task_routes={
|
||||
'app.service.sketch2garment.tasks.sketch_to_garment':
|
||||
{
|
||||
'queue': 'sketch_to_garment_queue',
|
||||
'exchange': 'sketch_to_garment_exchange', # ← 修改这里
|
||||
},
|
||||
},
|
||||
task_default_queue="sketch_to_garment_queue",
|
||||
|
||||
worker_concurrency=1,
|
||||
worker_prefetch_multiplier=1,
|
||||
worker_max_tasks_per_child=1,
|
||||
task_acks_late=True,
|
||||
task_reject_on_worker_lost=True,
|
||||
)
|
||||
44
app/service/sketch2garment/server.py
Normal file
44
app/service/sketch2garment/server.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import logging
|
||||
|
||||
from app.service.sketch2garment.tasks import sketch_to_garment
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def submit_sketch_to_garment_task(model: str = "single", task_id: str = "", callback_url: str = "", bucket_name: str = "test", user_id: str = "123", input_image_path: str = ""):
|
||||
"""提交 img_to_3D 任务(带队列长度限制)"""
|
||||
queue_name = "img_to_3d_queue"
|
||||
max_queue_length = 10
|
||||
|
||||
try:
|
||||
# current_length = get_queue_length(queue_name)
|
||||
|
||||
# if current_length >= max_queue_length:
|
||||
# return {
|
||||
# "state": "queue_full",
|
||||
# "message": "当前 3D 生成请求较多,请稍后重试。",
|
||||
# "queue_length": current_length,
|
||||
# "max_length": max_queue_length
|
||||
# }
|
||||
|
||||
# 提交任务
|
||||
task = sketch_to_garment.apply_async(
|
||||
args=(task_id, callback_url, bucket_name, input_image_path, user_id, model),
|
||||
task_id=task_id,
|
||||
queue="sketch_to_garment_queue")
|
||||
|
||||
# logger.info(f"img_to_3d_task 已提交 | task_id: {task_id} | 当前队列长度: {current_length}")
|
||||
|
||||
return {
|
||||
"state": "success",
|
||||
"task_id": task_id,
|
||||
"message": "任务已成功提交,正在后台处理...",
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"提交 img_to_3d_task 失败: {e}", exc_info=True)
|
||||
return {
|
||||
"state": "fail",
|
||||
"message": "提交失败,请稍后重试。",
|
||||
"error": str(e)
|
||||
}
|
||||
57
app/service/sketch2garment/tasks.py
Normal file
57
app/service/sketch2garment/tasks.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from app.core.config import settings
|
||||
from app.service.sketch2garment.callback import notify_callback
|
||||
import httpx
|
||||
|
||||
from app.service.sketch2garment.celery_app import celery_app
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@celery_app.task(bind=True, queue="sketch_to_garment_queue", max_retries=3, name='app.service.sketch2garment.tasks.sketch_to_garment')
|
||||
def sketch_to_garment(self, task_id: str, callback_url: str, bucket_name: str, input_image_path: str, user_id: str, category: str = None):
|
||||
payload = {
|
||||
"bucket_name": bucket_name,
|
||||
"category": category or settings.DEFAULT_CATEGORY,
|
||||
"input_image_path": input_image_path,
|
||||
"user_id": user_id
|
||||
}
|
||||
logger.info(f"payload: {payload}")
|
||||
|
||||
try:
|
||||
with httpx.Client(timeout=300.0) as client: # 注意这里用 AsyncClient 配合 Celery
|
||||
# 如果你的 LitServe 是同步 endpoint,也可以用 httpx.Client()
|
||||
response = client.post(settings.SKETCH_TO_GARMENT_URL, json=payload)
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
result_json = {
|
||||
"pattern": result[1],
|
||||
"texture": result[2],
|
||||
"glb": result[3],
|
||||
"texture_fabric": result[4]
|
||||
}
|
||||
asyncio.run(
|
||||
notify_callback(callback_url=callback_url, task_id=task_id, result=result_json, status="success")
|
||||
)
|
||||
else:
|
||||
asyncio.run(
|
||||
notify_callback(
|
||||
callback_url=callback_url,
|
||||
task_id=task_id,
|
||||
result={
|
||||
"status": "fail",
|
||||
"task_id": task_id,
|
||||
"message": "fail",
|
||||
"error": "fail"
|
||||
},
|
||||
status="fail")
|
||||
)
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "failed",
|
||||
"task_id": task_id,
|
||||
"input": payload,
|
||||
"error": str(e)
|
||||
}
|
||||
@@ -11,7 +11,12 @@ from minio import Minio
|
||||
from app.core.config import settings
|
||||
from app.service.utils.decorator import RunTime
|
||||
|
||||
minio_client = Minio(settings.MINIO_URL, access_key=settings.MINIO_ACCESS, secret_key=settings.MINIO_SECRET, secure=settings.MINIO_SECURE)
|
||||
minio_client = Minio(
|
||||
settings.MINIO_URL,
|
||||
access_key=settings.MINIO_ACCESS,
|
||||
secret_key=settings.MINIO_SECRET,
|
||||
secure=settings.MINIO_SECURE,
|
||||
)
|
||||
|
||||
|
||||
# 自定义 Retry 类
|
||||
@@ -30,7 +35,7 @@ http_client = urllib3.PoolManager(
|
||||
num_pools=10, # 设置连接池大小
|
||||
maxsize=10,
|
||||
timeout=timeout,
|
||||
cert_reqs='CERT_REQUIRED', # 需要证书验证
|
||||
cert_reqs="CERT_REQUIRED", # 需要证书验证
|
||||
retries=CustomRetry(
|
||||
total=5,
|
||||
backoff_factor=0.2,
|
||||
@@ -51,7 +56,7 @@ def oss_get_image(oss_client, bucket, object_name, data_type):
|
||||
image_array = np.frombuffer(image_bytes, np.uint8) # 转成8位无符号整型
|
||||
image_object = cv2.imdecode(image_array, cv2.IMREAD_UNCHANGED)
|
||||
if image_object.dtype == np.uint16:
|
||||
image_object = (image_object / 256).astype('uint8')
|
||||
image_object = (image_object / 256).astype("uint8")
|
||||
else:
|
||||
data_bytes = BytesIO(image_data.read())
|
||||
image_object = Image.open(data_bytes)
|
||||
@@ -63,13 +68,19 @@ def oss_get_image(oss_client, bucket, object_name, data_type):
|
||||
def oss_upload_image(oss_client, bucket, object_name, image_bytes):
|
||||
req = None
|
||||
try:
|
||||
req = oss_client.put_object(bucket_name=bucket, object_name=object_name, data=io.BytesIO(image_bytes), length=len(image_bytes), content_type='image/png')
|
||||
req = oss_client.put_object(
|
||||
bucket_name=bucket,
|
||||
object_name=object_name,
|
||||
data=io.BytesIO(image_bytes),
|
||||
length=len(image_bytes),
|
||||
content_type="image/png",
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f" | 上传图片出现异常 ######: {e}")
|
||||
return req
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
# url = "aida-results/result_0002186a-e631-11ee-86a6-b48351119060.png"
|
||||
# url = "aida-collection-element/11523/Moodboard/f60af0d2-94c2-48f9-90ff-74b8e8a481b5.jpg"
|
||||
# url = "aida-sys-image/images/female/outwear/0628000054.jpg"
|
||||
@@ -81,16 +92,26 @@ if __name__ == '__main__':
|
||||
# url = "aida-users/89/sketchboard/female/Dress/e6724ab7-8d3f-4677-abe0-c3e42ab7af85.jpeg"
|
||||
# url = "aida-users/87/print/956614a2-7e75-4fbe-9ed0-c1831e37a2c9-4-87.png"
|
||||
# url = "aida-users/89/single_logo/123-89.png"
|
||||
url = "aida-results/result_a7adcbd8-ef8d-11f0-8c92-0966ede33ab5.png"
|
||||
url = "aida-collection-element/26293/Sketchboard/b503d482-3334-46e7-9dee-44e380fb4294.png"
|
||||
|
||||
# url = "aida-collection-element/12148/Sketchboard/95ea577b-305b-4a62-b30a-39c0dd3ddb3f.png"
|
||||
read_type = "2"
|
||||
if read_type == "cv2":
|
||||
img = oss_get_image(oss_client=minio_client, bucket=url.split('/')[0], object_name=url[url.find('/') + 1:], data_type=read_type)
|
||||
img = oss_get_image(
|
||||
oss_client=minio_client,
|
||||
bucket=url.split("/")[0],
|
||||
object_name=url[url.find("/") + 1 :],
|
||||
data_type=read_type,
|
||||
)
|
||||
cv2.imshow("", img)
|
||||
cv2.waitKey(0)
|
||||
else:
|
||||
img = oss_get_image(oss_client=minio_client, bucket=url.split('/')[0], object_name=url[url.find('/') + 1:], data_type=read_type)
|
||||
img = oss_get_image(
|
||||
oss_client=minio_client,
|
||||
bucket=url.split("/")[0],
|
||||
object_name=url[url.find("/") + 1 :],
|
||||
data_type=read_type,
|
||||
)
|
||||
draw = ImageDraw.Draw(img)
|
||||
# 获取图片尺寸
|
||||
width, height = img.size
|
||||
@@ -103,7 +124,7 @@ if __name__ == '__main__':
|
||||
draw.line(
|
||||
[(center_x, 0), (center_x, height)], # 从顶部到底部的垂直线
|
||||
fill=(255, 0, 0), # 红色 (R, G, B)
|
||||
width=2 # 线宽
|
||||
width=2, # 线宽
|
||||
)
|
||||
|
||||
img.show()
|
||||
|
||||
@@ -13,17 +13,22 @@ dependencies = [
|
||||
"celery-types>=0.23.0",
|
||||
"chromadb>=1.3.7",
|
||||
"dashscope>=1.25.5",
|
||||
"deepagents>=0.6.7",
|
||||
"dominate>=2.9.1",
|
||||
"dotenv>=0.9.9",
|
||||
"fastapi[standard]>=0.125.0",
|
||||
"image>=1.5.33",
|
||||
"langchain>=1.2.0",
|
||||
"langchain-community>=0.4.1",
|
||||
"langchain-qwq>=0.3.5",
|
||||
"langgraph>=1.0.5",
|
||||
"langgraph-cli[inmem,redis]<=0.4.26",
|
||||
"load>=1.0.14",
|
||||
"load-dotenv>=0.1.0",
|
||||
"loguru>=0.7.3",
|
||||
"minio>=7.2.20",
|
||||
"moviepy==1.0.3",
|
||||
"nacos-sdk-python==2.0.1",
|
||||
"np>=1.0.2",
|
||||
"numpy<2",
|
||||
"ollama>=0.6.1",
|
||||
@@ -49,6 +54,6 @@ dependencies = [
|
||||
"tool>=0.8.0",
|
||||
"torch>=2.9.1",
|
||||
"torchvision>=0.24.1",
|
||||
"tritonclient[all]>=2.63.0",
|
||||
"tritonclient[all]>=2.69.0",
|
||||
"uvicorn>=0.38.0",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user