优化默认语言模型获取

This commit is contained in:
Azmat@qq.com
2026-09-03 16:00:14 +08:00
parent 6a628b0ca7
commit f53b6e1035
6 changed files with 49 additions and 28 deletions
+3 -18
View File
@@ -520,25 +520,10 @@ def _parse_extracted_entities_response(text: str) -> tuple[list[dict], list[dict
return entities, seg_refs
# 提取步固定锁定豆包 2.0 Pro(与脚本生成同款),不靠 get_default_model 的「最早创建」排序——
# 避免不同环境 DB 创建序漂移把提取路由到别的(中转站)推理模型。取不到再回落默认文本模型。
EXTRACT_TEXT_MODEL_NAME = "doubao-seed-2-0-pro-260215"
def _resolve_extract_model_config():
"""提取实体用的文本模型:优先豆包 2.0 Pro(active 且 provider active),否则回落默认文本模型。"""
pinned = (
ModelConfig.objects.select_related("provider")
.filter(
name=EXTRACT_TEXT_MODEL_NAME,
capability=ModelConfig.Capability.TEXT,
status=ModelConfig.Status.ACTIVE,
provider__status="active",
)
.order_by("created_at")
.first()
)
return pinned or get_default_model(ModelConfig.Capability.TEXT)
"""提取实体用平台后台钦定的默认文本模型(ModelConfig.is_default)。
未设默认时与 get_default_model 一致:回落最早创建的 active 文本模型。"""
return get_default_model(ModelConfig.Capability.TEXT)
def _collect_extract_text(
@@ -6,7 +6,7 @@ from django.test import TestCase
from apps.accounts.models import Team, TeamMember, User
from apps.ai.models import AITask, ModelConfig, ModelProvider
from apps.ai.services import EXTRACT_TEXT_MODEL_NAME, run_extract_entities_task, submit_extract_entities
from apps.ai.services import run_extract_entities_task, submit_extract_entities
from apps.billing.models import CreditAccount, CreditLedger
from apps.products.models import Product
from apps.projects.models import Project, ScriptSegment, ScriptVersion
@@ -66,7 +66,7 @@ class EntityExtractionRoutingTests(TestCase):
metadata={"routing": {"fallback_priority": priority}},
)
def model(self, provider, name, *, outbound=True, base_cost="0.50"):
def model(self, provider, name, *, outbound=True, base_cost="0.50", is_default=False):
return ModelConfig.objects.create(
provider=provider,
name=name,
@@ -75,6 +75,7 @@ class EntityExtractionRoutingTests(TestCase):
endpoint="chat/completions",
unit_price=Decimal("10"),
status=ModelConfig.Status.ACTIVE,
is_default=is_default,
metadata=_metadata(outbound=outbound, base_cost=base_cost),
)
@@ -114,7 +115,7 @@ class EntityExtractionRoutingTests(TestCase):
return CreditLedger.objects.filter(task=task, ledger_type=ledger_type).count()
def test_first_success_records_streaming_structured_attempt_and_persists_entities(self):
primary = self.model(self.provider("entity-primary", 20), "entity-primary")
primary = self.model(self.provider("entity-primary", 20), "entity-primary", is_default=True)
task = self.submit()
run_extract_entities_task(task_id=str(task.id))
@@ -213,7 +214,7 @@ class EntityExtractionRoutingTests(TestCase):
def test_direct_doubao_primary_retries_but_does_not_switch_when_outbound_disabled(self):
primary = self.model(
self.provider("doubao", 10), EXTRACT_TEXT_MODEL_NAME, outbound=False
self.provider("doubao", 10), "doubao-seed-2-0-pro-260215", outbound=False
)
candidate = self.model(
self.provider("entity-unused-candidate", 20), "entity-unused", outbound=False
@@ -236,3 +237,22 @@ class EntityExtractionRoutingTests(TestCase):
self.assertTrue(all(attempt.public_model_name == primary.display_name for attempt in attempts))
self.assertEqual(self.ledger_count(task, CreditLedger.Type.RESERVE), 1)
self.assertEqual(self.ledger_count(task, CreditLedger.Type.RELEASE), 1)
def test_uses_database_default_text_model_not_older_doubao_pin(self):
older = self.model(self.provider("volcengine-old", 20), "doubao-seed-2-0-pro-260215")
default = self.model(
self.provider("volcengine-default", 10),
"deepseek-v4-pro",
is_default=True,
)
task = self.submit()
run_extract_entities_task(task_id=str(task.id))
task.refresh_from_db()
attempt = task.model_attempts.get()
self.assertEqual(task.status, AITask.Status.SUCCEEDED)
self.assertEqual(attempt.model_config_id, default.id)
self.assertNotEqual(attempt.model_config_id, older.id)
self.assertEqual(task.request_payload["model"], "deepseek-v4-pro")
+1 -1
View File
@@ -1314,7 +1314,7 @@ body.sidebar-collapsed .user::after { display: none; }
.chip.active { border-color: var(--heat-40); color: var(--heat); background: var(--heat-12); }
.chip svg { width: 12px; height: 12px; }
/* 清空筛选:幽灵态(无边框/透明底),仅在有筛选时出现,点击重置全部筛选 */
.chip.chip-clear { border-color: transparent; background: transparent; color: var(--black-alpha-48); padding: 0 8px; }
.chip.chip-clear { border-color: transparent; background: transparent; color: var(--black-alpha-48); padding: 0 8px; line-height: 1; white-space: nowrap; }
.chip.chip-clear:hover { background: var(--black-alpha-4); color: var(--accent-black); }
/* ─── Toolbar ─── */
+1 -1
View File
@@ -26,7 +26,7 @@
.library-page .lib-head {
display: flex;
align-items: flex-start;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-bottom: 18px;
+5 -1
View File
@@ -25,7 +25,7 @@
.models-page .ml-head {
display: flex;
align-items: flex-start;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-bottom: 18px;
@@ -91,12 +91,16 @@
.models-page .ml-seg-btn {
height: 32px;
padding: 0 12px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 0;
border-radius: 8px;
color: #50555d;
background: transparent;
font: inherit;
font-size: 13px;
line-height: 1;
cursor: pointer;
}
.models-page .ml-seg-btn:hover { color: var(--accent-black); }
+15 -3
View File
@@ -21,7 +21,7 @@
.pl-head {
display: flex;
align-items: flex-start;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-bottom: 18px;
@@ -134,9 +134,9 @@
flex-wrap: wrap;
}
.pl-select { position: relative; width: 150px; flex: 0 0 150px; z-index: 40; }
.pl-select { position: relative; width: auto; min-width: 132px; flex: 0 0 auto; z-index: 40; }
.pl-select.open { z-index: 50; }
.pl-select[data-key="cat"] { width: 168px; flex-basis: 168px; }
.pl-select[data-key="cat"] { min-width: 148px; }
.pl-select-trigger {
width: 100%;
height: 34px;
@@ -226,14 +226,26 @@
.pl-clear {
height: 34px;
padding: 0 12px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
border: 0;
border-radius: 10px;
color: var(--pl-muted);
background: transparent;
font: inherit;
font-size: 13px;
line-height: 1;
white-space: nowrap;
flex-shrink: 0;
cursor: pointer;
}
.pl-clear svg {
width: 13px;
height: 13px;
flex: 0 0 auto;
}
.pl-clear:hover { color: var(--accent-black); background: rgba(34, 42, 54, 0.06); }
.pl-view {