vn6295337 Claude Opus 4.5 commited on
Commit
146876d
·
1 Parent(s): a8d1079

fix: Handle 'sources' wrapper in fundamentals data structure

Browse files

The Researcher-Agent returns fundamentals wrapped in a "sources" key:
{"sources": {"sec_edgar": {"data": {...}}, "yahoo_finance": {...}}}

But analyzer expected flat structure:
{"sec_edgar": {"data": {...}}, "yahoo_finance": {...}}

This caused fundamentals (revenue, net_income, margins, etc.) to be
silently missing from SWOT analysis.

Fixed in 3 places:
- _extract_company_profile() - for company profile extraction
- _generate_data_report() - for Data Report tables
- _extract_key_metrics() - for metric reference table

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. src/nodes/analyzer.py +29 -9
src/nodes/analyzer.py CHANGED
@@ -159,7 +159,12 @@ def _extract_company_profile(raw_data: str) -> dict:
159
  profile = {}
160
 
161
  # Try SEC EDGAR for business address (most authoritative)
162
- sec_data = multi_source.get("fundamentals_all", {}).get("sec_edgar", {}).get("data", {})
 
 
 
 
 
163
  sec_profile = sec_data.get("company_info", {}) or sec_data.get("profile", {})
164
 
165
  if sec_profile:
@@ -177,7 +182,11 @@ def _extract_company_profile(raw_data: str) -> dict:
177
  yf_profile = yf_val.get("profile", {})
178
 
179
  if not yf_profile:
180
- yf_fund = multi_source.get("fundamentals_all", {}).get("yahoo_finance", {}).get("data", {})
 
 
 
 
181
  yf_profile = yf_fund.get("profile", {})
182
 
183
  if yf_profile:
@@ -337,8 +346,13 @@ def _generate_data_report(raw_data: str, is_financial: bool = False) -> str:
337
 
338
  # ========== FINANCIALS ==========
339
  fin_all = multi_source.get("fundamentals_all", {})
340
- sec_data = fin_all.get("sec_edgar", {}).get("data", {})
341
- yf_data = fin_all.get("yahoo_finance", {}).get("data", {})
 
 
 
 
 
342
 
343
  if sec_data or yf_data:
344
  lines.append("## Financials")
@@ -606,15 +620,21 @@ def _extract_key_metrics(raw_data: str) -> dict:
606
  }
607
 
608
  # Extract fundamentals with temporal data
609
- # Structure: metrics.fundamentals = {"sec_edgar": {"data": {...}}, "yahoo_finance": {"data": {...}}}
610
- # Also check multi_source.fundamentals_all for the same structure
 
611
  fin = metrics.get("fundamentals", {})
612
  if not fin or "error" in fin:
613
  fin = data.get("multi_source", {}).get("fundamentals_all", {})
614
  if fin and "error" not in fin:
615
- # Use SEC EDGAR as primary, Yahoo Finance as fallback
616
- sec_data = fin.get("sec_edgar", {}).get("data", {})
617
- yf_data = fin.get("yahoo_finance", {}).get("data", {})
 
 
 
 
 
618
  # Merge with SEC as primary
619
  fin_data = {**yf_data, **sec_data} # SEC overwrites YF where both exist
620
  extracted["fundamentals"] = {
 
159
  profile = {}
160
 
161
  # Try SEC EDGAR for business address (most authoritative)
162
+ # Handle both structures (with and without "sources" wrapper)
163
+ fin_all = multi_source.get("fundamentals_all", {})
164
+ if "sources" in fin_all:
165
+ sec_data = fin_all.get("sources", {}).get("sec_edgar", {}).get("data", {})
166
+ else:
167
+ sec_data = fin_all.get("sec_edgar", {}).get("data", {})
168
  sec_profile = sec_data.get("company_info", {}) or sec_data.get("profile", {})
169
 
170
  if sec_profile:
 
182
  yf_profile = yf_val.get("profile", {})
183
 
184
  if not yf_profile:
185
+ # Handle both structures (with and without "sources" wrapper)
186
+ if "sources" in fin_all:
187
+ yf_fund = fin_all.get("sources", {}).get("yahoo_finance", {}).get("data", {})
188
+ else:
189
+ yf_fund = fin_all.get("yahoo_finance", {}).get("data", {})
190
  yf_profile = yf_fund.get("profile", {})
191
 
192
  if yf_profile:
 
346
 
347
  # ========== FINANCIALS ==========
348
  fin_all = multi_source.get("fundamentals_all", {})
349
+ # Handle both structures (with and without "sources" wrapper)
350
+ if "sources" in fin_all:
351
+ sec_data = fin_all.get("sources", {}).get("sec_edgar", {}).get("data", {})
352
+ yf_data = fin_all.get("sources", {}).get("yahoo_finance", {}).get("data", {})
353
+ else:
354
+ sec_data = fin_all.get("sec_edgar", {}).get("data", {})
355
+ yf_data = fin_all.get("yahoo_finance", {}).get("data", {})
356
 
357
  if sec_data or yf_data:
358
  lines.append("## Financials")
 
620
  }
621
 
622
  # Extract fundamentals with temporal data
623
+ # Structure varies:
624
+ # - Old: {"sec_edgar": {"data": {...}}, "yahoo_finance": {"data": {...}}}
625
+ # - New: {"sources": {"sec_edgar": {"data": {...}}, "yahoo_finance": {"data": {...}}}}
626
  fin = metrics.get("fundamentals", {})
627
  if not fin or "error" in fin:
628
  fin = data.get("multi_source", {}).get("fundamentals_all", {})
629
  if fin and "error" not in fin:
630
+ # Handle both structures (with and without "sources" wrapper)
631
+ if "sources" in fin:
632
+ sources = fin.get("sources", {})
633
+ sec_data = sources.get("sec_edgar", {}).get("data", {})
634
+ yf_data = sources.get("yahoo_finance", {}).get("data", {})
635
+ else:
636
+ sec_data = fin.get("sec_edgar", {}).get("data", {})
637
+ yf_data = fin.get("yahoo_finance", {}).get("data", {})
638
  # Merge with SEC as primary
639
  fin_data = {**yf_data, **sec_data} # SEC overwrites YF where both exist
640
  extracted["fundamentals"] = {