Files
2025-05-25 20:28:43 +08:00

187 lines
6.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import FastAPI, HTTPException, BackgroundTasks
from pydantic import BaseModel
from typing import List, Optional
import yaml
import os
from services.image_search import IMAGE_SEARCH_SERVICE
from config.settings import Config
from config.api_settings import load_config
from services.community_service import CommunityService
import uvicorn
from middleware.protected_mode import ProtectedModeMiddleware
from middleware.rate_limiter import RateLimitMiddleware
import re
import json
# 初始化核心组件
config = Config()
search_engine = IMAGE_SEARCH_SERVICE
api_config = load_config()
app = FastAPI(title="VVQuest API")
# 注册保护模式中间件
if api_config.protected_mode:
app.add_middleware(ProtectedModeMiddleware, config=api_config)
if api_config.rate_limit.enabled:
app.add_middleware(RateLimitMiddleware, config=api_config)
# 数据模型定义
class SearchRequest(BaseModel):
query: str
n_results: int = 5
class SearchRequestEnhanced(SearchRequest):
resource_pack_uuids: List[str] = [] # 添加默认空列表
ai_search: bool = False
class ConfigUpdate(BaseModel):
api_key: Optional[str] = None
base_url: Optional[str] = None
class ModelDownloadRequest(BaseModel):
model_id: str
def search_result_postprocess(results:List[str]):
ret = []
for v in results:
if api_config.urls.return_type == "abs_path":
pass
elif api_config.urls.return_type == "rel_path":
v = os.path.relpath(v, Config().base_dir)
elif api_config.urls.return_type == "sha256":
v = v[1] + os.path.splitext(os.path.basename(v[0]))[1] # 假设v是一个元组,v[1]是sha256,v[0]是文件名
reg_pattern = api_config.urls.path_replace_regex
if reg_pattern:
v = re.sub(reg_pattern, "", v)
if api_config.urls.url_prefix:
v = f'{api_config.urls.url_prefix}/{v}'
if api_config.urls.url_postfix:
if not v.endswith(api_config.urls.url_postfix):
v = f'{v}/{api_config.urls.url_prefix}'
ret.append(v)
return ret
# API 端点
# @app.post("/search")
# async def search_images(request: SearchRequest):
# """执行图片搜索"""
# try:
# results = search_engine.search(
# request.query,
# request.n_results,
# api_key = config.api.embedding_models.api_key
# )
# return {"results": search_result_postprocess(results)}
# except Exception as e:
# raise HTTPException(status_code=500, detail=str(e))
@app.post("/search")
async def search_images(request: SearchRequestEnhanced):
"""执行图片搜索"""
try:
results = search_engine.search(
request.query,
request.n_results,
request.resource_pack_uuids,
api_key = config.api.embedding_models.api_key,
use_llm = request.ai_search,
return_type = "hash" if api_config.urls.return_type == "sha256" else "default"
)
return {"results": search_result_postprocess(results)}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/")
async def root():
"""API根目录"""
return {"message": "Welcome to the VVQuest API"}
@app.get("/libs_manifest")
async def get_libs_manifest():
manifest_path = os.path.join(Config().temp_dir, "all_manifest.json")
if not os.path.exists(manifest_path):
CommunityService().reload_community_info()
try:
with open(manifest_path, 'r', encoding='utf-8') as f:
manifest_data = json.load(f)
return manifest_data
except FileNotFoundError:
raise HTTPException(status_code=404, detail="Manifest file not found")
except json.JSONDecodeError:
raise HTTPException(status_code=500, detail="Invalid JSON format in manifest file")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/generate-cache")
async def generate_cache(background_tasks: BackgroundTasks):
"""触发缓存生成(后台任务)"""
if not search_engine.has_cache():
background_tasks.add_task(search_engine.generate_cache)
return {"message": "Cache generation started"}
return {"message": "Cache already exists"}
@app.get("/config")
async def get_config():
"""获取当前配置"""
return {
# "mode": search_engine.get_mode(),
"model": search_engine.get_model_name(),
"api_key": search_engine.embedding_service.api_key,
"base_url": search_engine.embedding_service.base_url
}
@app.put("/api-config")
async def update_config(update: ConfigUpdate):
"""更新API配置"""
try:
if update.api_key:
search_engine.embedding_service.api_key = update.api_key
if update.base_url:
search_engine.embedding_service.base_url = update.base_url
config.save()
return {"message": "Config updated successfully"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# @app.post("/download-model")
# async def download_model(req: ModelDownloadRequest):
# """下载指定模型"""
# try:
# if search_engine.embedding_service.is_model_downloaded(req.model_id):
# return {"message": "Model already exists"}
#
# search_engine.download_model(req.model_id)
# return {"message": "Model download completed"}
# except Exception as e:
# raise HTTPException(status_code=500, detail=str(e))
# @app.get("/models")
# async def list_models():
# """获取可用模型列表"""
# models = []
# for model_id, info in Config().models.embedding_models.items():
# models.append({
# "id": model_id,
# "performance": info.performance,
# "downloaded": search_engine.embedding_service.is_model_downloaded(model_id)
# })
# return {"models": models}
if __name__ == "__main__":
search_engine.embedding_service.api_key = api_config.api_mode_config.default_api_key
search_engine.embedding_service.base_url = api_config.api_mode_config.default_base_url
# search_engine.set_mode('api')
if api_config.generate_cache:
search_engine.generate_cache()
print("Starting API server...")
uvicorn.run(app, host="0.0.0.0", port=8000)