feat(news): rank this company's news first and label each item

MSN's feed for a stock is often mostly general market news: of 10 items,
ADRO, BREN and CUAN had none about the company, BBRI one, yet they were
presented as the company's news. Fetch at least 50 items, label each
`relevance: company` (tagged by MSN with the stock's instrument id, or
naming the ticker or the company) or `market`, and return company items
first, newest first, trimmed to --limit.

Company names come from the quote's short and display names, best
effort: MSN tags Telkom articles with its NYSE ADR, not TLKM, so names
catch what tags miss. Classification stays a pure function in parse_news.

Output change, intentional: news JSON gains `relevance`, the table an
ABOUT column (goldens regenerated), and the get_company tool description
explains the field (tools.json and the website tools page regenerated).
News cache keys change so entries without the field are not reused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V3syn2fN2LNo5pvTSqc6eh
This commit is contained in:
hermes 2026-10-16 07:58:57 +00:00
commit 1796e58105
13 changed files with 254 additions and 38 deletions

View file

@ -122,7 +122,8 @@ impl Financial<'_> {
policy: CachePolicy,
) -> Result<Fetched<Vec<NewsItem>>, IdxError> {
let resolved = self.idx.resolve(symbol)?;
let key = format!("{resolved}-{limit}");
// "ranked": entries cached before relevance ranking lack the field.
let key = format!("{resolved}-{limit}-ranked");
let provider = self.idx.provider.news_provider(&resolved)?;
let ttl = self.idx.config.quote_ttl;
let fetched = self.fetch("news", &key, &resolved, ttl, policy, || {