deepdata: core product code (backend, frontend, runtime, deepflow module)

This commit is contained in:
figmar
2026-08-30 19:23:38 +08:00
commit bd3db30be9
1405 changed files with 271727 additions and 0 deletions
@@ -0,0 +1,249 @@
import math
import uuid
from datetime import datetime, timezone
from fastapi import Depends, HTTPException
from sqlalchemy import select, func, update
from sqlalchemy.ext.asyncio import AsyncSession
from app.module.shared.llm import LLMFactory
from app.core.logging import get_logger
from app.db.datascope import DataScopeHandle, SYSTEM_USER
from app.db.models.models import Models
from app.db.session import get_db
from app.module.shared.schema import PaginatedData
from app.module.system.schema.models import (
CreateModelRequest,
QueryModelRequest,
ModelsResponse,
ProviderItem,
)
logger = get_logger(__name__)
# 固定厂商列表
PROVIDERS = [
ProviderItem(provider="ModelEngine", baseUrl="http://nginxservice/open/router/v1"),
ProviderItem(provider="Ollama", baseUrl="http://localhost:11434"),
ProviderItem(provider="OpenAI", baseUrl="https://api.openai.com/v1"),
ProviderItem(provider="DeepSeek", baseUrl="https://api.deepseek.com/v1"),
ProviderItem(provider="火山方舟", baseUrl="https://ark.cn-beijing.volces.com/api/v3"),
ProviderItem(provider="阿里云百炼", baseUrl="https://dashscope.aliyuncs.com/compatible-mode/v1"),
ProviderItem(provider="硅基流动", baseUrl="https://api.siliconflow.cn/v1"),
ProviderItem(provider="智谱AI", baseUrl="https://open.bigmodel.cn/api/paas/v4"),
ProviderItem(provider="自定义模型", baseUrl=""),
]
def _orm_to_response(row: Models) -> ModelsResponse:
return ModelsResponse(
id=row.id,
modelName=row.model_name,
provider=row.provider,
baseUrl=row.base_url,
apiKey=row.api_key or "",
type=row.type or "CHAT",
isEnabled=bool(row.is_enabled) if row.is_enabled is not None else True,
isDefault=bool(row.is_default) if row.is_default is not None else False,
createdAt=row.created_at,
updatedAt=row.updated_at,
createdBy=row.created_by,
updatedBy=row.updated_by,
)
class ModelsService:
"""模型配置服务:db 通过 FastAPI Depends(get_db) 注入,不在路由中传递。"""
def __init__(self, db: AsyncSession = Depends(get_db)):
self.db = db
async def get_providers(self) -> list[ProviderItem]:
"""返回固定厂商列表,与 Java getProviders() 一致。"""
return list(PROVIDERS)
async def get_models(self, q: QueryModelRequest) -> PaginatedData[ModelsResponse]:
"""分页查询,支持 provider/type/isEnabled/isDefault;排除已删除;page 从 0 开始。"""
query = select(Models).where(
(Models.is_deleted == False) | (Models.is_deleted.is_(None))
)
if q.provider is not None and q.provider != "":
query = query.where(Models.provider == q.provider)
if q.type is not None:
query = query.where(Models.type == q.type.value)
if q.isEnabled is not None:
query = query.where(Models.is_enabled == q.isEnabled)
if q.isDefault is not None:
query = query.where(Models.is_default == q.isDefault)
total = (await self.db.execute(select(func.count()).select_from(query.subquery()))).scalar_one()
size = max(1, min(500, q.size))
offset = max(0, q.page) * size
rows = (
await self.db.execute(
query.order_by(Models.created_at.desc()).offset(offset).limit(size)
)
).scalars().all()
total_pages = math.ceil(total / size) if total else 0
current = max(0, q.page) + 1
return PaginatedData(
page=current,
size=size,
total_elements=total,
total_pages=total_pages,
content=[_orm_to_response(r) for r in rows],
)
async def get_model_detail(self, model_id: str) -> ModelsResponse:
"""获取模型详情,已删除或不存在则 404。"""
query = select(Models).where(Models.id == model_id).where(
(Models.is_deleted == False) | (Models.is_deleted.is_(None))
)
r = (await self.db.execute(query)).scalar_one_or_none()
if not r:
raise HTTPException(status_code=404, detail="模型配置不存在")
return _orm_to_response(r)
async def create_model(self, req: CreateModelRequest) -> ModelsResponse:
"""创建模型:健康检查后 saveAndSetDefault;isEnabled 恒为 True。
若 uk_model_provider 已存在且 is_deleted=True,则恢复并更新相关字段。"""
try:
LLMFactory.check_health(req.modelName, req.baseUrl, req.apiKey, req.type.value)
except Exception as e:
logger.error("Model health check failed: model=%s type=%s err=%s", req.modelName, req.type, e)
raise HTTPException(status_code=400, detail="模型健康检查失败") from e
# 检查 uk_model_provider (model_name, provider, created_by) 是否存在且已删除
effective_user = (DataScopeHandle.get_user_info() or "").strip() or SYSTEM_USER
created_by_match = (
(Models.created_by == effective_user)
| ((Models.created_by.is_(None)) & (effective_user == SYSTEM_USER))
)
deleted_row = (
await self.db.execute(
select(Models).where(
Models.model_name == req.modelName,
Models.provider == req.provider,
created_by_match,
)
)
).scalar_one_or_none()
if deleted_row and deleted_row.is_deleted:
# 恢复并更新
entity = deleted_row
entity.base_url = req.baseUrl
entity.api_key = req.apiKey or ""
entity.type = req.type.value
entity.is_enabled = True
entity.is_deleted = False
entity.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
want_default = req.isDefault if req.isDefault is not None else False
if want_default:
await self.db.execute(
update(Models)
.where(Models.type == req.type.value, Models.is_default == True)
.values(is_default=False)
)
entity.is_default = want_default
self.db.add(entity)
await self.db.commit()
await self.db.refresh(entity)
return _orm_to_response(entity)
# 正常创建
existing = (
await self.db.execute(
select(Models).where(
(Models.is_deleted == False) | (Models.is_deleted.is_(None)),
Models.type == req.type.value,
Models.is_default == True,
)
)
).scalar_one_or_none()
is_default: bool
if existing is None:
is_default = True
else:
await self.db.execute(
update(Models)
.where(Models.type == req.type.value, Models.is_default == True)
.values(is_default=False)
)
is_default = req.isDefault if req.isDefault is not None else False
now = datetime.now(timezone.utc).replace(tzinfo=None)
entity = Models(
id=str(uuid.uuid4()),
model_name=req.modelName,
provider=req.provider,
base_url=req.baseUrl,
api_key=req.apiKey or "",
type=req.type.value,
is_enabled=True,
is_default=is_default,
is_deleted=False,
created_at=now,
updated_at=now,
)
self.db.add(entity)
await self.db.commit()
await self.db.refresh(entity)
return _orm_to_response(entity)
async def update_model(self, model_id: str, req: CreateModelRequest) -> ModelsResponse:
"""更新模型:存在性校验、健康检查后 updateAndSetDefault;isEnabled 恒为 True。"""
res = await self.db.execute(
select(Models).where(Models.id == model_id).where(
(Models.is_deleted == False) | (Models.is_deleted.is_(None))
)
)
entity = res.scalar_one_or_none()
if not entity:
raise HTTPException(status_code=404, detail="模型配置不存在")
try:
LLMFactory.check_health(req.modelName, req.baseUrl, req.apiKey, req.type.value)
except Exception as e:
logger.error("Model health check failed: model=%s type=%s err=%s", req.modelName, req.type, e)
raise HTTPException(status_code=400, detail="模型健康检查失败") from e
entity.model_name = req.modelName
entity.provider = req.provider
entity.base_url = req.baseUrl
entity.api_key = req.apiKey or ""
entity.type = req.type.value
entity.is_enabled = True
want_default = req.isDefault if req.isDefault is not None else False
if (entity.is_default is not True) and want_default:
await self.db.execute(
update(Models)
.where(Models.type == req.type.value, Models.is_default == True)
.values(is_default=False)
)
entity.is_default = want_default
entity.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
await self.db.commit()
await self.db.refresh(entity)
return _orm_to_response(entity)
async def delete_model(self, model_id: str) -> None:
"""软删除模型配置。"""
entity = (
await self.db.execute(
select(Models).where(Models.id == model_id).where(
(Models.is_deleted == False) | (Models.is_deleted.is_(None))
)
)
).scalar_one_or_none()
if not entity:
raise HTTPException(status_code=404, detail="模型配置不存在")
entity.is_deleted = True
entity.updated_at = datetime.now(timezone.utc).replace(tzinfo=None)
await self.db.commit()
await self.db.refresh(entity)