diff --git a/src/services/stock_profile_service.py b/src/services/stock_profile_service.py index 7b833cc63..975bfd052 100644 --- a/src/services/stock_profile_service.py +++ b/src/services/stock_profile_service.py @@ -16,6 +16,10 @@ from src.services.market_symbol_utils import get_suffix_market from src.services.research_artifact_service import build_research_artifact from src.services.stock_code_utils import resolve_daily_stock_identity from src.services.stock_service import StockService +from src.utils.data_processing import ( + extract_fundamental_detail_fields, + extract_market_structure_detail_field, +) _BLOCK_NAMES = ("quote", "history", "research", "intelligence", "portfolio", "monitors") @@ -341,7 +345,22 @@ class StockProfileService: @staticmethod def _artifact_input(detail: Dict[str, Any]) -> Dict[str, Any]: - context_overview = extract_analysis_context_pack_overview(detail.get("context_snapshot")) + context_snapshot = detail.get("context_snapshot") + raw_result = detail.get("raw_result") + raw_fundamental = ( + raw_result.get("fundamental_context") or raw_result + if isinstance(raw_result, dict) + else None + ) + extracted_fundamental = extract_fundamental_detail_fields( + context_snapshot, + raw_fundamental, + ) + context_overview = extract_analysis_context_pack_overview(context_snapshot) + market_structure = extract_market_structure_detail_field( + context_snapshot, + raw_result, + ) return { "meta": { "id": detail.get("id"), @@ -368,6 +387,11 @@ class StockProfileService: "news_content": detail.get("news_content"), "empty_news_disclosure": detail.get("empty_news_disclosure"), "analysis_context_pack_overview": context_overview, + "financial_report": detail.get("financial_report") + or extracted_fundamental.get("financial_report"), + "dividend_metrics": detail.get("dividend_metrics") + or extracted_fundamental.get("dividend_metrics"), + "market_structure": detail.get("market_structure") or market_structure, }, } diff --git a/tests/test_stock_code_utils.py b/tests/test_stock_code_utils.py index 7f0623103..af51990a0 100644 --- a/tests/test_stock_code_utils.py +++ b/tests/test_stock_code_utils.py @@ -94,8 +94,14 @@ class TestBuildDailyCodeCandidates: assert identity.market == "kr" assert identity.code_candidates == ("005930.KS", "005930") - def test_bare_code_with_unsupported_market_hint_fails_closed(self): - assert resolve_daily_stock_identity("005930", market_hint="tw") is None + def test_bare_taiwan_code_uses_explicit_market_hint(self): + identity = resolve_daily_stock_identity("005930", market_hint="tw") + + assert identity is not None + assert identity.normalized_code == "005930" + assert identity.market == "tw" + assert identity.refill_code == "" + assert identity.code_candidates == ("005930",) def test_cross_market_bare_code_without_hint_fails_closed(self): assert resolve_daily_stock_identity("8035") is None diff --git a/tests/test_stock_profile_api.py b/tests/test_stock_profile_api.py index f00a22862..d2790ff74 100644 --- a/tests/test_stock_profile_api.py +++ b/tests/test_stock_profile_api.py @@ -157,6 +157,41 @@ def test_profile_uses_one_canonical_code_and_returns_structured_research() -> No } +def test_profile_research_preserves_specialized_report_evidence() -> None: + service, dependencies = _service() + detail = _report_detail() + detail["context_snapshot"] = { + "fundamental_context": { + "earnings": { + "data": { + "financial_report": {"report_date": "2026-06-30"}, + "dividend": {"ttm_cash_dividend_per_share": 1.2}, + } + } + } + } + detail["raw_result"] = { + "market_structure_context": { + "schema_version": "market-structure-v1", + "status": "available", + "market_theme_context": {"schema_version": "market-theme-v1"}, + "stock_market_position": {"schema_version": "stock-market-position-v1"}, + } + } + dependencies["history_service"].get_history_detail_by_id.return_value = detail + + payload = service.get_profile("AAPL") + + artifact = payload["research"]["data"]["structured_report"] + evidence_ids = {item["id"] for item in artifact["evidence"]} + assert { + "fundamental:financial_report", + "fundamental:dividend_metrics", + "market:structure", + } <= evidence_ids + assert artifact["data_quality"]["source_count"] == len(artifact["evidence"]) + + def test_hk_alias_is_canonicalized_before_every_downstream_query() -> None: service, dependencies = _service() dependencies["stock_service"].get_realtime_quote.return_value = _quote("HK00700")