Skip to content

Commit 49d8279

Browse files
authored
fix: 移除 HistoryItem 响应 Schema 中 sentiment_score 的… (#942) (#962)
* fix(issue-942): [bug] * fix(review-feedback-962): address latest review comments
1 parent 413ae02 commit 49d8279

3 files changed

Lines changed: 69 additions & 8 deletions

File tree

api/v1/schemas/history.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,8 @@ class HistoryItem(BaseModel):
2323
stock_name: Optional[str] = Field(None, description="股票名称")
2424
report_type: Optional[str] = Field(None, description="报告类型")
2525
sentiment_score: Optional[int] = Field(
26-
None,
27-
description="情绪评分 (0-100)",
28-
ge=0,
29-
le=100
26+
None,
27+
description="情绪评分(历史数据可能超出 0-100 范围,读取时不做约束)",
3028
)
3129
operation_advice: Optional[str] = Field(None, description="操作建议")
3230
created_at: Optional[str] = Field(None, description="创建时间")
@@ -133,10 +131,8 @@ class ReportSummary(BaseModel):
133131
operation_advice: Optional[str] = Field(None, description="操作建议")
134132
trend_prediction: Optional[str] = Field(None, description="趋势预测")
135133
sentiment_score: Optional[int] = Field(
136-
None,
137-
description="情绪评分 (0-100)",
138-
ge=0,
139-
le=100
134+
None,
135+
description="情绪评分(历史数据可能超出 0-100 范围,读取时不做约束)",
140136
)
141137
sentiment_label: Optional[str] = Field(None, description="情绪标签")
142138

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
1212
<!-- 新条目格式:- [类型] 描述(类型取值:新功能/改进/修复/文档/测试/chore)-->
1313
<!-- 每条独立一行追加到本段末尾,无需分类标题,合并时冲突最小 -->
1414
- [修复] **MiniMax-M2.7 模型连接测试支持** — 修复 LLM 通道连接测试在 MiniMax-M2.7 模型下返回 "Empty response" 的问题;增加了 `max_tokens` 上限(8→256)以容纳 MiniMax 思考过程,并添加 `content_blocks` 格式解析逻辑统一处理 MiniMax 响应格式差异。
15+
- [修复] 移除 `HistoryItem``ReportSummary` 响应 Schema 中 `sentiment_score``ge=0/le=100` 约束(fixes #942)——历史库中存储的超范围负值或大于 100 的情绪评分不再触发 Pydantic ValidationError,历史列表与详情接口恢复正常返回。
1516

1617
## [3.12.0] - 2026-04-01
1718

tests/test_analysis_history.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,5 +643,69 @@ def test_delete_history_api_deletes_selected_records(self, mock_auth) -> None:
643643
self.assertIsNotNone(session.query(AnalysisHistory).filter(AnalysisHistory.id == record_id_2).first())
644644

645645

646+
class HistoryItemSchemaNegativeSentimentTest(unittest.TestCase):
647+
"""Regression: HistoryItem / ReportSummary must accept out-of-range sentiment_score from DB rows."""
648+
649+
@classmethod
650+
def setUpClass(cls) -> None:
651+
"""Import schema classes once for all tests, skipping gracefully when deps are missing."""
652+
try:
653+
from api.v1.schemas.history import HistoryItem, ReportSummary # type: ignore
654+
except ModuleNotFoundError:
655+
cls.HistoryItem = None
656+
cls.ReportSummary = None
657+
else:
658+
cls.HistoryItem = HistoryItem
659+
cls.ReportSummary = ReportSummary
660+
661+
def test_negative_sentiment_score_does_not_raise(self) -> None:
662+
"""Bug #942: sentiment_score=-22 in DB should not cause Pydantic ValidationError."""
663+
if self.HistoryItem is None:
664+
self.skipTest("fastapi / pydantic not installed in this test environment")
665+
666+
item = self.HistoryItem(query_id="q1", stock_code="600519", sentiment_score=-22)
667+
self.assertEqual(item.sentiment_score, -22)
668+
669+
def test_out_of_range_high_sentiment_score_does_not_raise(self) -> None:
670+
"""HistoryItem should also accept scores above 100 from legacy data."""
671+
if self.HistoryItem is None:
672+
self.skipTest("fastapi / pydantic not installed in this test environment")
673+
674+
item = self.HistoryItem(query_id="q2", stock_code="600519", sentiment_score=150)
675+
self.assertEqual(item.sentiment_score, 150)
676+
677+
def test_none_sentiment_score_is_allowed(self) -> None:
678+
"""HistoryItem.sentiment_score=None should still be valid (optional field)."""
679+
if self.HistoryItem is None:
680+
self.skipTest("fastapi / pydantic not installed in this test environment")
681+
682+
item = self.HistoryItem(query_id="q3", stock_code="600519", sentiment_score=None)
683+
self.assertIsNone(item.sentiment_score)
684+
685+
def test_report_summary_negative_sentiment_score_does_not_raise(self) -> None:
686+
"""ReportSummary.sentiment_score should also accept negative values from legacy DB rows."""
687+
if self.ReportSummary is None:
688+
self.skipTest("fastapi / pydantic not installed in this test environment")
689+
690+
summary = self.ReportSummary(sentiment_score=-22)
691+
self.assertEqual(summary.sentiment_score, -22)
692+
693+
def test_report_summary_out_of_range_high_sentiment_score_does_not_raise(self) -> None:
694+
"""ReportSummary.sentiment_score should also accept scores above 100 from legacy data."""
695+
if self.ReportSummary is None:
696+
self.skipTest("fastapi / pydantic not installed in this test environment")
697+
698+
summary = self.ReportSummary(sentiment_score=150)
699+
self.assertEqual(summary.sentiment_score, 150)
700+
701+
def test_report_summary_none_sentiment_score_is_allowed(self) -> None:
702+
"""ReportSummary.sentiment_score=None should still be valid (optional field)."""
703+
if self.ReportSummary is None:
704+
self.skipTest("fastapi / pydantic not installed in this test environment")
705+
706+
summary = self.ReportSummary(sentiment_score=None)
707+
self.assertIsNone(summary.sentiment_score)
708+
709+
646710
if __name__ == "__main__":
647711
unittest.main()

0 commit comments

Comments
 (0)