Commit 10014ca1 authored by MD. SHAHIDUL ISLAM's avatar MD. SHAHIDUL ISLAM

feat: add opportunity screener component and update backend API configuration

parent 8b74c383
...@@ -6,6 +6,7 @@ from types import SimpleNamespace ...@@ -6,6 +6,7 @@ from types import SimpleNamespace
from dohasecuritiesstockai.api.ai_analysis import ( from dohasecuritiesstockai.api.ai_analysis import (
AIStockAnalysisGenerator, AIStockAnalysisGenerator,
AIStockResearchOutput, AIStockResearchOutput,
_evidence_for_ai,
_narrative_violations, _narrative_violations,
) )
from dohasecuritiesstockai.api.models import ( from dohasecuritiesstockai.api.models import (
...@@ -190,7 +191,12 @@ def test_ai_enhancement_recomputes_score_and_weighted_value() -> None: ...@@ -190,7 +191,12 @@ def test_ai_enhancement_recomputes_score_and_weighted_value() -> None:
result = generator.enhance( result = generator.enhance(
_baseline(), _baseline(),
{"annual_financial_performance": [{"year": 2025, "eps_basic": "12.4"}]}, {
"market_snapshot": {"latest_price": 50},
"annual_financial_performance": [
{"year": 2025, "eps_basic": "12.4"}
],
},
) )
assert result.fundamental_score == 80 assert result.fundamental_score == 80
...@@ -209,11 +215,29 @@ def test_ai_enhancement_recomputes_score_and_weighted_value() -> None: ...@@ -209,11 +215,29 @@ def test_ai_enhancement_recomputes_score_and_weighted_value() -> None:
assert "Never invent" in messages[0]["content"] assert "Never invent" in messages[0]["content"]
assert "Never state, estimate, or repeat an overall" in messages[0]["content"] assert "Never state, estimate, or repeat an overall" in messages[0]["content"]
assert "Never state a weighted" in messages[0]["content"] assert "Never state a weighted" in messages[0]["content"]
assert '"year": 2025' in messages[1]["content"] assert '"latest_price": 50' in messages[1]["content"]
assert '"year": 2025' not in messages[1]["content"]
assert "annual_financial_performance" not in messages[1]["content"]
assert "ai_fundamental" in messages[1]["content"] assert "ai_fundamental" in messages[1]["content"]
assert '"fundamental_score"' not in messages[1]["content"] assert '"fundamental_score"' not in messages[1]["content"]
def test_ai_evidence_excludes_removed_ui_datasets() -> None:
evidence = {
"market_snapshot": {"latest_price": 50},
"annual_financial_performance": [{"year": 2025}],
"quarterly_performance": [{"quarter": "Q3"}],
"shareholding_history": [{"government": 50.35}],
"dividend_history": [{"year": 2025, "cash_dividend": 160}],
"nav_history": [{"year": 2025, "nav_per_share": 302.87}],
"operating_cash_flow_per_share_history": [{"nocfps": -39.83}],
}
filtered = _evidence_for_ai(evidence)
assert filtered == {"market_snapshot": {"latest_price": 50}}
def test_multi_agent_state_changes_ai_research_mode() -> None: def test_multi_agent_state_changes_ai_research_mode() -> None:
output = _ai_output() output = _ai_output()
structured = SimpleNamespace(invoke=lambda messages: output) structured = SimpleNamespace(invoke=lambda messages: output)
...@@ -257,3 +281,14 @@ def test_narrative_guard_retries_structured_output_once() -> None: ...@@ -257,3 +281,14 @@ def test_narrative_guard_retries_structured_output_once() -> None:
assert result.headline.en == good.headline.en assert result.headline.en == good.headline.en
assert len(calls) == 2 assert len(calls) == 2
assert _narrative_violations(good) == [] assert _narrative_violations(good) == []
def test_narrative_guard_rejects_removed_ui_sections() -> None:
payload = _ai_output().model_dump()
payload["in_depth_title"] = _text("Recent DSE Disclosures")
violations = _narrative_violations(
AIStockResearchOutput.model_validate(payload)
)
assert any("removed dashboard UI section" in item for item in violations)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment