feat(网关): 接口4 改由网关本机直接调豆包视觉模型,不再代理到 worker
- gateway/app.py: 接口4 handler 改为直接调用 face_features 模块 - face_features.py: API Key 读取支持 gateway/config.json - gateway/config.example.json: 增加 ark_api_key 配置项
This commit is contained in:
+8
-4
@@ -55,17 +55,21 @@ _client = None
|
|||||||
|
|
||||||
|
|
||||||
def _load_api_key() -> str | None:
|
def _load_api_key() -> str | None:
|
||||||
"""ARK_API_KEY 环境变量优先,否则读 worker_config.json 的 ark_api_key。"""
|
"""ARK_API_KEY 环境变量优先,否则读 worker_config.json / gateway/config.json 的 ark_api_key。"""
|
||||||
key = os.getenv("ARK_API_KEY")
|
key = os.getenv("ARK_API_KEY")
|
||||||
if key:
|
if key:
|
||||||
return key
|
return key
|
||||||
cfg = os.path.join(os.path.dirname(__file__), "worker_config.json")
|
base = os.path.dirname(__file__)
|
||||||
|
for cfg_name in ("worker_config.json", "gateway/config.json"):
|
||||||
|
cfg = os.path.join(base, cfg_name)
|
||||||
if os.path.isfile(cfg):
|
if os.path.isfile(cfg):
|
||||||
try:
|
try:
|
||||||
with open(cfg, encoding="utf-8") as f:
|
with open(cfg, encoding="utf-8") as f:
|
||||||
return json.load(f).get("ark_api_key")
|
v = json.load(f).get("ark_api_key")
|
||||||
|
if v:
|
||||||
|
return v
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e: # noqa: BLE001
|
||||||
logger.warning("读取 worker_config.json ark_api_key 失败:%s", e)
|
logger.warning("读取 %s ark_api_key 失败:%s", cfg_name, e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+68
-7
@@ -5,11 +5,17 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from fastapi import FastAPI, Request
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import FastAPI, File, Form, Request, UploadFile
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
from gateway.config import load_config
|
from gateway.config import load_config
|
||||||
@@ -217,9 +223,6 @@ _GROW_B_FORMS = {
|
|||||||
"marked_image_file": {"type": "file", "description": "划线图片文件"},
|
"marked_image_file": {"type": "file", "description": "划线图片文件"},
|
||||||
"marked_image_url": {"type": "string", "description": "划线图片 URL"},
|
"marked_image_url": {"type": "string", "description": "划线图片 URL"},
|
||||||
"marked_image_base64": {"type": "string", "description": "划线图片 base64"},
|
"marked_image_base64": {"type": "string", "description": "划线图片 base64"},
|
||||||
"original_image_file": {"type": "file", "description": "原始用户照片文件"},
|
|
||||||
"original_image_url": {"type": "string", "description": "原始用户照片 URL"},
|
|
||||||
"original_image_base64": {"type": "string", "description": "原始用户照片 base64"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -242,9 +245,67 @@ async def hair_grow_b(request: Request):
|
|||||||
|
|
||||||
|
|
||||||
@app.post("/api/v1/face/features", tags=["人脸分析"])
|
@app.post("/api/v1/face/features", tags=["人脸分析"])
|
||||||
async def face_features(request: Request):
|
async def face_features(
|
||||||
"""接口4:用户特征分析"""
|
image_file: Optional[UploadFile] = File(default=None, description="上传图片文件(JPG/PNG,≤ 1 MB)"),
|
||||||
return await _proxy(request, "/api/v1/face/features")
|
image_url: Optional[str] = Form(default=None, description="图片 URL"),
|
||||||
|
image_base64: Optional[str] = Form(default=None, description="图片 base64(需带前缀)"),
|
||||||
|
):
|
||||||
|
"""接口4:用户特征分析 — 本机直接调豆包视觉模型,不经过 worker。"""
|
||||||
|
import uuid as _uuid
|
||||||
|
|
||||||
|
# 三选一校验
|
||||||
|
provided = [x for x in (image_file, image_url, image_base64) if x]
|
||||||
|
if len(provided) != 1:
|
||||||
|
return JSONResponse(status_code=400, content={
|
||||||
|
"code": 1007, "message": "图片参数错误:必须且只能传 image_file / image_url / image_base64 其中一个",
|
||||||
|
"request_id": f"gw-{_uuid.uuid4().hex[:8]}", "data": None,
|
||||||
|
})
|
||||||
|
|
||||||
|
img_bytes = None
|
||||||
|
if image_file:
|
||||||
|
raw = await image_file.read()
|
||||||
|
if len(raw) > 1_000_000:
|
||||||
|
return JSONResponse(status_code=400, content={
|
||||||
|
"code": 1006, "message": "文件超出 1 MB 限制",
|
||||||
|
"request_id": f"gw-{_uuid.uuid4().hex[:8]}", "data": None,
|
||||||
|
})
|
||||||
|
img_bytes = raw
|
||||||
|
elif image_base64:
|
||||||
|
b64 = image_base64
|
||||||
|
if "," in b64:
|
||||||
|
b64 = b64.split(",", 1)[1]
|
||||||
|
try:
|
||||||
|
img_bytes = base64.b64decode(b64)
|
||||||
|
except Exception:
|
||||||
|
return JSONResponse(status_code=400, content={
|
||||||
|
"code": 1008, "message": "图片格式不支持(base64 解码失败)",
|
||||||
|
"request_id": f"gw-{_uuid.uuid4().hex[:8]}", "data": None,
|
||||||
|
})
|
||||||
|
|
||||||
|
from fastapi.concurrency import run_in_threadpool
|
||||||
|
from face_features import analyze_features, has_face
|
||||||
|
|
||||||
|
try:
|
||||||
|
feats = await run_in_threadpool(analyze_features, img_bytes, image_url)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.exception("接口4 豆包调用失败")
|
||||||
|
return JSONResponse(status_code=503, content={
|
||||||
|
"code": 1007, "message": f"分析服务异常:{ex}",
|
||||||
|
"request_id": f"gw-{_uuid.uuid4().hex[:8]}", "data": None,
|
||||||
|
})
|
||||||
|
|
||||||
|
if not has_face(feats):
|
||||||
|
return JSONResponse(status_code=400, content={
|
||||||
|
"code": 1001, "message": "无法识别人像",
|
||||||
|
"request_id": f"gw-{_uuid.uuid4().hex[:8]}", "data": None,
|
||||||
|
})
|
||||||
|
|
||||||
|
return JSONResponse(status_code=200, content={
|
||||||
|
"code": 0,
|
||||||
|
"message": "success",
|
||||||
|
"request_id": f"gw-{_uuid.uuid4().hex[:8]}",
|
||||||
|
"data": {"features": json.dumps(feats, ensure_ascii=False)},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.post("/api/v1/hairline/generate", tags=["人脸分析"])
|
@app.post("/api/v1/hairline/generate", tags=["人脸分析"])
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"unhealthy_threshold": 2,
|
"unhealthy_threshold": 2,
|
||||||
"healthy_threshold": 1
|
"healthy_threshold": 1
|
||||||
},
|
},
|
||||||
|
"ark_api_key": "",
|
||||||
"dispatch": {
|
"dispatch": {
|
||||||
"per_worker_concurrency": 1,
|
"per_worker_concurrency": 1,
|
||||||
"queue_wait_seconds": 30,
|
"queue_wait_seconds": 30,
|
||||||
|
|||||||
Reference in New Issue
Block a user