Spaces:
Sleeping
Sleeping
Commit History
-
feat: rebuild The Brief around a sourced article and scannable panels
The Brief was fourteen stacked cards that repeated each other. It is now a piece of writing with the evidence arranged around it. The article is composed by a second model call that runs AFTER deterministic verification and is shown nothing but what passed it: verified facts, the code-computed quarter deltas, the XBRL segment lines. The fail-closed gate is not relaxed to allow prose, it is reused as the prose's input filter. validate_article then discards any paragraph citing no known entry or containing a number its citations do not state, and voids the whole piece on recommendation or price-target language, in four languages. Around it: - a scorecard of four to six coloured tiles, each carrying the figure that justifies its direction, validated exactly like a paragraph; - the product and region mix, an eight-quarter revenue and margin chart, and figures placed between article sections by role rather than by the model: what moved the group, what the company itself quantified,…0f869bb
-
feat: report which metrics passed the consensus gate, not just whether any did
_market_expectations_aligned returns True if EITHER revenue or eps aligns, and by design only revenue normally will: basis_mismatch is the documented, expected outcome for eps, because yfinance publishes adjusted estimates against this repo's GAAP reported figure. A future consumer reading the boolean and rendering an actual-versus-consensus EPS line would render a comparison the gate explicitly refused -- a false claim produced by a fail-closed gate working correctly. display_policy gains market_expectations_aligned_metrics: the list of metrics that actually passed, e.g. ["revenue"]. The boolean stays for backward compatibility and is documented as coarse -- "at least one metric aligned", not sufficient authority to render any particular comparison. The provider-flag branch it ORs with is payload-wide and names no metric, so it can set the boolean True over an empty list; the helper docstring says so. Both fail-closed sites gain the key as an empty list rather than leaving it absent…586fba9 -
feat: compute market_expectations_aligned from the consensus archive
4a70f17
-
fix: close 4 post-merge review gaps in continuity threads
Post-merge fix wave on the whole-branch review of continuity-threads: - Verification: continuity_threads now goes through the same fail-closed filtering (_filter_unverified_claims) and reliability recompute (_collect_facts) as earnings_quality_signals/ between_the_lines, so an unverified/hallucinated thread evidence item is dropped before it can be persisted to data/threads.db or shown on screen, and never keeps the LLM's self-assigned reliability. - storage/threads_db.py: mark_missed and continue_thread are now idempotent per period, so regenerating a brief for the same filing period (a normal Streamlit action) no longer double-counts a missed quarter or duplicates a history row. - Error isolation: continuity_node, the apply_continuity_threads call in synthesis_node, and continuity_view.render() now wrap their new threads_db I/O in try/except and fail gracefully, matching the established pattern elsewhere in the codebase (signals_node, _finalize_synthesis_pr…05863a5 -
feat: wire continuity_node into the LangGraph pipeline
Adds a deterministic continuity_node between profile_evidence and agent that loads open continuity threads for the ticker and injects them as an "== OPEN THREADS FROM PRIOR QUARTERS ==" context message. synthesis_node now persists the LLM's thread classifications via apply_continuity_threads right after attach_edge_signals, and retires threads the LLM didn't reference this run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>131143b
-
fix: raise synthesis max_tokens to 64000 and surface truncation explicitly
The single-call synthesis (brief + company profile + per-fact evidence_ref, merged in 8727e56) needs 25-40K output tokens; the inherited 16384 cap (from the pre-merge two-call split) cut the JSON mid-object every time (stop_reason=max_tokens confirmed on a live AAPL run), failing parsing and producing an empty PARTIAL brief (verified: 0/0) regardless of data quality or freshness. This was the actual cause behind every "not enough verified evidence" report throughout this incident, including the original stale NVDA brief. 64000 is claude-haiku-4-5's output ceiling (verified live), with billing only for tokens actually generated. Also detect stop_reason/finish_reason == max_tokens/length on the stream and raise SynthesisTruncatedError, so a future truncation produces an explicit "truncated by the token limit" reason instead of a generic JSON-parse failure message. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>602548d -
fix: keep fail-closed per fact but stop zero-verified synthesis from emptying the whole brief
When no AI-generated claim passes deterministic evidence verification, the brief used to be replaced wholesale by an empty _partial_brief skeleton, discarding filing_date, company_name, what_to_watch, coverage, etc. even though those don't depend on LLM verification. Now only the per-fact filtering (unchanged, still fail-closed) empties individual sections, and a plain-language notice + data freshness banner (120-day staleness heuristic) tell the user what's missing instead of hiding the whole report. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>b419b7b
-
feat: merge Company Overview generation into single synthesis call
Company Primer (renamed Company Overview) was generated by a separate background LLM call, decoupled from the main brief synthesis. It now shares the same synthesis call via a deterministic profile-evidence node, dropping the standalone thread/job in app.py. Nav reordered for a PM/equity-analyst flow (Overview -> PM Flash -> Evidence & Deltas -> Financials -> Ask AI) and hardcoded nav labels now route through i18n. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>8727e56
-
feat: multi-provider LLM support, prominent chat, design pass, and new analytics
- Add agent/llm.py provider factory (Anthropic/OpenAI) with a sidebar model picker so visitors can paste their own API key instead of relying solely on the server's; thread the resulting config through generation, chat, and translation. - Promote Chat to primary nav (was buried under a secondary "Evidence tool" heading) with suggested-question chips and a "sources searched" line. - Fix a pre-existing bug where every nav/CTA color accent silently no-oped (Streamlit exposes st.button(key=...) as a `st-key-{key}` wrapper class, not a `key` attribute) and tighten the palette to three families (emerald brand, violet AI, semantic bull/bear). - Add a "Sources analyzed" provenance card, quarter/annual basis split on the financials grid, and cross-category signal dedup. - Add valuation + peer comparison, next-catalysts, brief-vs-brief delta, and one-page PDF export. - Rename the product from Primer to Amplegest across the app, docs, and the Hugging Face Space (old URL redire…7880373
-
feat: embed synthesis language in brief dict
brief_dict["language"] = lang after attach_edge_signals in synthesis_node, so saved briefs carry their generation language for translation routing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>43f40b2
-
chore: checkpoint sidebar nav + i18n work-in-progress before Decision Stack redesign
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>e6496c0
-
fix: harden _extract_json — use raw_decode to ignore trailing JSON objects and prose
Fixes 'Extra data' synthesis error when LLM outputs multiple JSON objects (e.g. when asked about 'NVDA AAPL' as a single ticker before the sidebar fix). json.JSONDecoder().raw_decode stops at the end of the first complete object, so a second object or closing commentary no longer breaks json.loads. Adds 5 unit tests covering: simple object, nested braces, two concatenated objects, trailing prose, and markdown code fence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>b002600 -
feat: Analyst Edge layer — deterministic text delta signals (Phase 0+1)
Adds a signal engine that computes verbatim filing text changes across consecutive periods and injects them into the LangGraph pipeline before the LLM loop, so the agent investigates signals and the synthesis cites exact before→after fragments rather than summarizing what it already knows. Phase 0 — foundation: - storage/sections_db.py: persist raw MD&A, Risk Factors, and transcript text keyed by (ticker, period, section); populated at ingest time - ingest.py: call upsert_section for each text block after edgar fetch - analysis/signals.py: QuarterDelta Pydantic model (kind, before_text, after_text, computed_metric, significance) shared across all modules - agent/graph.py: signals_node as new entry point; edge_signals state key; attach_edge_signals() called in synthesis_node after apply_reliability() - agent/post_synthesis.py: attach_edge_signals() writes authoritative computed data — LLM cannot fabricate signal numbers - agent/schemas.py: BriefOutput.quarter_deltas optional fi…559c2ff
-
fix: insert stub ToolMessages when round cap hit mid-tool-call
When the 10-round cap is reached while the agent still has pending tool_calls, synthesis received an AIMessage with tool_use blocks but no matching tool_result, causing Anthropic to reject the request with a bad format error. Now we insert stub ToolMessages for any dangling tool_calls before building the synthesis conversation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>567e993
-
fix: retry on overloaded_error + friendlier API error messages
Add max_retries=5 to ChatAnthropic (agent + synthesis) so transient 529 overloaded errors are retried with exponential backoff before surfacing to the user. Also map overloaded/rate-limit exceptions to readable strings instead of showing the raw Anthropic error dict. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>e3e2b3c -
fix: background generation, parallel tools, prompt caching, Tavily cache
Bug fixes: - Generation now runs in a background thread so sidebar nav clicks no longer cancel in-flight graph.stream — brief persists regardless - @st.fragment polls the trace every 400ms; auto-navs to Verdict when done - Generate Brief button disabled while generation is running (no double-spawn) - Synthesis streamed via llm_plain.stream() to show in-progress indicator Latency improvements: - Tool calls within a round dispatched in parallel via ThreadPoolExecutor - cache_control: ephemeral on SYSTEM_PROMPT and SYNTHESIS_STRUCTURED_PROMPT reduces input tokens per agent round by ~80-90% after first cache hit - storage/news_cache.py: 1-hour SQLite TTL cache for Tavily news results - Reranker (BAAI/bge-reranker-base) eagerly loaded at app startup to avoid cold-start penalty on the first search_filing/search_transcript call Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2401ce5 -
deploy: Primer app for HF Spaces — clean orphan history
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>35676b4