feat(avatar): add private vision chat support
This commit is contained in:
@@ -37,6 +37,8 @@ async def create_model(req: AIModelCreateRequest, db=Depends(get_db)):
|
||||
api_base_url=req.api_base_url,
|
||||
api_key_enc=encrypt(req.api_key) if req.api_key else None,
|
||||
model_version=req.model_version,
|
||||
vision_model_version=req.vision_model_version,
|
||||
ocr_model_version=req.ocr_model_version,
|
||||
temperature=req.temperature,
|
||||
max_tokens=req.max_tokens,
|
||||
timeout_seconds=req.timeout_seconds,
|
||||
@@ -100,6 +102,8 @@ async def get_digital_avatar_runtime_model(
|
||||
"api_base_url": model.api_base_url or "https://api.openai.com/v1",
|
||||
"api_key": decrypt(model.api_key_enc) if model.api_key_enc else "",
|
||||
"model": model.model_version or model.model_name,
|
||||
"vision_model": model.vision_model_version or "qwen3.6-flash",
|
||||
"ocr_model": model.ocr_model_version or "qwen-vl-ocr",
|
||||
"temperature": model.temperature,
|
||||
"max_tokens": model.max_tokens,
|
||||
"timeout_seconds": model.timeout_seconds,
|
||||
@@ -129,6 +133,8 @@ def _format_model(m: AIModelConfig) -> dict:
|
||||
"usage_scope": m.usage_scope,
|
||||
"api_base_url": m.api_base_url, "has_api_key": bool(m.api_key_enc),
|
||||
"model_version": m.model_version, "temperature": m.temperature,
|
||||
"vision_model_version": m.vision_model_version,
|
||||
"ocr_model_version": m.ocr_model_version,
|
||||
"max_tokens": m.max_tokens, "timeout_seconds": m.timeout_seconds,
|
||||
"is_default": m.is_default, "is_enabled": m.is_enabled,
|
||||
"created_at": m.created_at.isoformat(),
|
||||
|
||||
@@ -66,21 +66,36 @@ async def init_db():
|
||||
PendingReplyTask, TokenStat, AIModelConfig, SystemConfig, LoginLog
|
||||
)
|
||||
async with engine.begin() as conn:
|
||||
await conn.execute(text("SELECT GET_LOCK('ai_model_usage_scope_migration', 30)"))
|
||||
await conn.execute(text("SELECT GET_LOCK('ai_model_config_migration', 30)"))
|
||||
try:
|
||||
result = await conn.execute(text(
|
||||
"SELECT COUNT(*) FROM information_schema.COLUMNS "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_model_configs' "
|
||||
"AND COLUMN_NAME = 'usage_scope'"
|
||||
))
|
||||
if result.scalar_one() == 0:
|
||||
await conn.execute(text(
|
||||
columns = (
|
||||
(
|
||||
"usage_scope",
|
||||
"ALTER TABLE ai_model_configs ADD COLUMN usage_scope "
|
||||
"VARCHAR(16) NOT NULL DEFAULT 'general' AFTER provider"
|
||||
))
|
||||
logger.info("AI模型配置表已增加 usage_scope 字段")
|
||||
"VARCHAR(16) NOT NULL DEFAULT 'general' AFTER provider",
|
||||
),
|
||||
(
|
||||
"vision_model_version",
|
||||
"ALTER TABLE ai_model_configs ADD COLUMN vision_model_version "
|
||||
"VARCHAR(64) NULL AFTER model_version",
|
||||
),
|
||||
(
|
||||
"ocr_model_version",
|
||||
"ALTER TABLE ai_model_configs ADD COLUMN ocr_model_version "
|
||||
"VARCHAR(64) NULL AFTER vision_model_version",
|
||||
),
|
||||
)
|
||||
for column_name, ddl in columns:
|
||||
result = await conn.execute(text(
|
||||
"SELECT COUNT(*) FROM information_schema.COLUMNS "
|
||||
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_model_configs' "
|
||||
"AND COLUMN_NAME = :column_name"
|
||||
), {"column_name": column_name})
|
||||
if result.scalar_one() == 0:
|
||||
await conn.execute(text(ddl))
|
||||
logger.info("AI模型配置表已增加 %s 字段", column_name)
|
||||
finally:
|
||||
await conn.execute(text("SELECT RELEASE_LOCK('ai_model_usage_scope_migration')"))
|
||||
await conn.execute(text("SELECT RELEASE_LOCK('ai_model_config_migration')"))
|
||||
logger.info("✅ 数据库模型注册成功")
|
||||
logger.info("✅ 数据库初始化完成")
|
||||
|
||||
|
||||
@@ -126,6 +126,8 @@ class AIModelConfig(Base):
|
||||
api_base_url: Mapped[str | None] = mapped_column(String(256))
|
||||
api_key_enc: Mapped[str | None] = mapped_column(String(512))
|
||||
model_version: Mapped[str | None] = mapped_column(String(64))
|
||||
vision_model_version: Mapped[str | None] = mapped_column(String(64))
|
||||
ocr_model_version: Mapped[str | None] = mapped_column(String(64))
|
||||
temperature: Mapped[float] = mapped_column(Float, default=0.7)
|
||||
max_tokens: Mapped[int] = mapped_column(Integer, default=1000)
|
||||
timeout_seconds: Mapped[int] = mapped_column(Integer, default=30)
|
||||
|
||||
@@ -158,6 +158,8 @@ class AIModelCreateRequest(BaseModel):
|
||||
api_base_url: Optional[str] = None
|
||||
api_key: Optional[str] = None
|
||||
model_version: Optional[str] = None
|
||||
vision_model_version: Optional[str] = Field(None, max_length=64)
|
||||
ocr_model_version: Optional[str] = Field(None, max_length=64)
|
||||
temperature: float = Field(default=0.7, ge=0.0, le=2.0)
|
||||
max_tokens: int = Field(default=1000, ge=1, le=32000)
|
||||
timeout_seconds: int = Field(default=30, ge=5, le=300)
|
||||
@@ -171,6 +173,8 @@ class AIModelUpdateRequest(BaseModel):
|
||||
api_base_url: Optional[str] = None
|
||||
api_key: Optional[str] = None
|
||||
model_version: Optional[str] = None
|
||||
vision_model_version: Optional[str] = Field(None, max_length=64)
|
||||
ocr_model_version: Optional[str] = Field(None, max_length=64)
|
||||
temperature: Optional[float] = Field(None, ge=0.0, le=2.0)
|
||||
max_tokens: Optional[int] = Field(None, ge=1, le=32000)
|
||||
timeout_seconds: Optional[int] = Field(None, ge=5, le=300)
|
||||
@@ -186,6 +190,8 @@ class AIModelResponse(BaseModel):
|
||||
api_base_url: Optional[str]
|
||||
has_api_key: bool
|
||||
model_version: Optional[str]
|
||||
vision_model_version: Optional[str]
|
||||
ocr_model_version: Optional[str]
|
||||
temperature: float
|
||||
max_tokens: int
|
||||
timeout_seconds: int
|
||||
|
||||
Reference in New Issue
Block a user