feat: 增加内容生成配置管理
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.models import Base
|
||||
from app.models.ai_config import ContentGenerationConfig
|
||||
from app.services.content_generation_config_service import (
|
||||
SAMPLE_VALUES,
|
||||
ContentGenerationConfigService,
|
||||
)
|
||||
from app.services.tracked_generation_service import TrackedGenerationService
|
||||
|
||||
|
||||
def _db() -> Session:
|
||||
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False}, poolclass=StaticPool)
|
||||
Base.metadata.create_all(engine)
|
||||
return Session(engine)
|
||||
|
||||
|
||||
def test_template_preview_keeps_locked_notice_and_rejects_unknown_variables():
|
||||
content = ContentGenerationConfigService.preview(
|
||||
"help_card",
|
||||
"问题:{{issue}}\n摘要:{{summary}}",
|
||||
)
|
||||
|
||||
assert "问题:我第一次参加带练" in content
|
||||
assert "不会自动发送给老师" in content
|
||||
assert "不代表已经转人工处理" in content
|
||||
|
||||
with pytest.raises(HTTPException) as error:
|
||||
ContentGenerationConfigService.preview(
|
||||
"help_card",
|
||||
"问题:{{issue}}\n摘要:{{summary}}\n未知:{{unknown}}",
|
||||
)
|
||||
assert error.value.status_code == 400
|
||||
assert "未知变量" in str(error.value.detail)
|
||||
|
||||
with pytest.raises(HTTPException) as malformed:
|
||||
ContentGenerationConfigService.preview(
|
||||
"help_card",
|
||||
"问题:{{issue}}\n摘要:{{summary}}\n错误变量:{{bad-name}}",
|
||||
)
|
||||
assert malformed.value.status_code == 400
|
||||
|
||||
|
||||
def test_config_versions_save_reset_and_restore_without_overwriting_history():
|
||||
with _db() as db:
|
||||
first = ContentGenerationConfigService.save(
|
||||
db,
|
||||
config_type="help_card",
|
||||
template_content="自定义一:{{issue}}\n{{summary}}",
|
||||
instruction_content="只整理明确内容",
|
||||
updated_by=1,
|
||||
)
|
||||
db.commit()
|
||||
first_id = first.id
|
||||
|
||||
reset = ContentGenerationConfigService.reset(db, config_type="help_card", updated_by=1)
|
||||
db.commit()
|
||||
restored = ContentGenerationConfigService.restore(
|
||||
db,
|
||||
config_type="help_card",
|
||||
source_config_id=first_id,
|
||||
updated_by=2,
|
||||
)
|
||||
db.commit()
|
||||
|
||||
assert db.query(ContentGenerationConfig).count() == 3
|
||||
assert restored.id != first_id
|
||||
assert restored.source_config_id == first_id
|
||||
assert restored.change_type == "restore"
|
||||
assert restored.template_content == "自定义一:{{issue}}\n{{summary}}"
|
||||
assert reset.change_type == "reset"
|
||||
assert ContentGenerationConfigService.current(db, "help_card").id == restored.id
|
||||
|
||||
|
||||
def test_ai_generation_uses_configured_instruction_and_only_accepts_allowed_fields(monkeypatch):
|
||||
captured: dict[str, str] = {}
|
||||
|
||||
def fake_generate(db, *, prompt, scenario, user_id):
|
||||
captured["prompt"] = prompt
|
||||
return SimpleNamespace(
|
||||
answer=json.dumps(
|
||||
{
|
||||
"issue": "整理后的问题",
|
||||
"summary": "整理后的摘要",
|
||||
"current_focus": "整理后的关注",
|
||||
"unknown": "不能进入卡片",
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
)
|
||||
|
||||
monkeypatch.setattr(TrackedGenerationService, "generate", fake_generate)
|
||||
with _db() as db:
|
||||
generated, used_fallback = ContentGenerationConfigService.generate_values(
|
||||
db,
|
||||
config_type="help_card",
|
||||
instruction_content="优先保留用户原话",
|
||||
values=dict(SAMPLE_VALUES),
|
||||
user_id=1,
|
||||
)
|
||||
|
||||
assert used_fallback is False
|
||||
assert generated["issue"] == "整理后的问题"
|
||||
assert generated["summary"] == "整理后的摘要"
|
||||
assert "unknown" not in generated
|
||||
assert "优先保留用户原话" in captured["prompt"]
|
||||
assert "不得分析人格" in captured["prompt"]
|
||||
@@ -9,6 +9,7 @@ from sqlalchemy.orm import Session
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.models import Base
|
||||
from app.models.ai_config import ContentGenerationConfig
|
||||
from app.models.chat import ChatMessage, ChatSession, TopicSession
|
||||
from app.models.entitlement import EntitlementPlan
|
||||
from app.models.growth import TeacherHelpCard
|
||||
@@ -43,6 +44,15 @@ def test_generate_help_card_from_topic_summary_and_mark_copied():
|
||||
started_at=_now(),
|
||||
)
|
||||
db.add_all([user, plan, session, topic])
|
||||
db.add(
|
||||
ContentGenerationConfig(
|
||||
config_type="help_card",
|
||||
template_content="【给老师的求助卡·自定义模板】\n问题:{{issue}}\n摘要:{{summary}}",
|
||||
instruction_content="只整理用户明确表达的内容",
|
||||
change_type="save",
|
||||
updated_by=1,
|
||||
)
|
||||
)
|
||||
db.add_all(
|
||||
[
|
||||
ChatMessage(id=1, session_id=1, topic_session_id=1, user_id=1, role="user", content="我练习时身体抗拒。", created_at=_now()),
|
||||
@@ -54,6 +64,7 @@ def test_generate_help_card_from_topic_summary_and_mark_copied():
|
||||
card = HelpCardService.generate_for_session(db, user=user, session=session)
|
||||
|
||||
assert "给老师的求助卡" in card.content
|
||||
assert "自定义模板" in card.content
|
||||
assert "不会自动发送给老师" in card.content
|
||||
assert "阴影人格练习步骤是否正确" in card.content
|
||||
assert "情绪 / 身体感受" not in card.content
|
||||
|
||||
@@ -9,6 +9,7 @@ from sqlalchemy.orm import Session
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
from app.models import Base
|
||||
from app.models.ai_config import ContentGenerationConfig
|
||||
from app.models.chat import ChatMessage, ChatSession, TopicSession
|
||||
from app.models.entitlement import EntitlementPlan
|
||||
from app.models.growth import ShareDraft
|
||||
@@ -43,6 +44,15 @@ def test_generate_share_draft_from_topic_summary_and_mark_copied():
|
||||
started_at=_now(),
|
||||
)
|
||||
db.add_all([user, plan, session, topic])
|
||||
db.add(
|
||||
ContentGenerationConfig(
|
||||
config_type="share_draft",
|
||||
template_content="【实修分享稿草稿·自定义模板】\n主题:{{issue}}\n回顾:{{summary}}",
|
||||
instruction_content="只整理当下明确谈到的内容",
|
||||
change_type="save",
|
||||
updated_by=1,
|
||||
)
|
||||
)
|
||||
db.add_all(
|
||||
[
|
||||
ChatMessage(id=1, session_id=1, topic_session_id=1, user_id=1, role="user", content="我不敢表达。", created_at=_now()),
|
||||
@@ -54,6 +64,7 @@ def test_generate_share_draft_from_topic_summary_and_mark_copied():
|
||||
draft = ShareDraftService.generate_for_session(db, user=user, session=session)
|
||||
|
||||
assert "实修分享稿草稿" in draft.content
|
||||
assert "自定义模板" in draft.content
|
||||
assert "系统不会自动发送到任何群" in draft.content
|
||||
assert "不代表结论" in draft.content
|
||||
assert "情绪和身体反应" not in draft.content
|
||||
|
||||
Reference in New Issue
Block a user