优化默认语言模型获取
This commit is contained in:
@@ -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")
|
||||
|
||||
|
||||
@@ -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 ─── */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user