Skip to content

Feat/datainfra refactor tracker#100

Merged
jerry609 merged 10 commits into
masterfrom
feat/datainfra-refactor-tracker
Feb 12, 2026
Merged

Feat/datainfra refactor tracker#100
jerry609 merged 10 commits into
masterfrom
feat/datainfra-refactor-tracker

Conversation

@jerry609

@jerry609 jerry609 commented Feb 12, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

Release Notes

  • New Features

    • Added evidence quote tracking and display for paper judgments with source citations.
    • Introduced paper export in BibTeX, RIS, and Markdown formats.
    • Added evidence coverage metrics dashboard.
    • Enhanced model endpoint testing with latency measurement.
    • Integrated system keychain for secure API key storage.
  • Improvements

    • Redesigned dashboard with activity timeline, stats bar, and saved papers view.
    • Upgraded model endpoint settings with modal-based configuration workflow.
    • Improved paper library sorting and timestamp handling.
  • Removals

    • Removed legacy topic search workflows for simplified, unified search experience.

- PaperCard: collapsible evidence quotes section below judge scores
- SavedTab: export dropdown (BibTeX/RIS/Markdown) triggering browser download
- TopicWorkflowDashboard: Download .md button for daily paper reports
- SearchResults: add papers_cool and hf_daily to SOURCE_OPTIONS

Closes #96
…ion test

- Add GET /api/research/metrics/evidence-coverage endpoint that returns
  coverage_rate, total_claims, total_with_evidence, and daily trend data
- Add unit test verifying saved papers rank higher than skipped papers
  in list_track_feed
- Enhance POST /api/model-endpoints/{id}/test to return latency_ms,
  success, and error fields
- Add unit tests for all new functionality

Closes #92
Closes #95
Closes #97
- Add utils/secret.py with encrypt/decrypt using Fernet (AES-128-CBC + HMAC-SHA256)
- Encryption key from PAPERBOT_SECRET_KEY env var, fallback key with warning
- model_endpoint_store: encrypt on write, decrypt on read
- Graceful migration: non-Fernet values (legacy plaintext) pass through on decrypt
- Fix pre-existing test bugs (unquoted f-string path expressions)

Closes #77
…gine

- Remove paper_searcher parameter from ContextEngine.__init__
- Remove legacy SemanticScholarSearch import and direct API call path
- ContextEngine now exclusively uses PaperSearchService for paper search
- Fallback: logs warning and returns empty results if no search_service provided
- Remove unused PaperMeta import

Closes #78
- Replace direct harvester calls (ArxivHarvester, SemanticScholarHarvester,
  OpenAlexHarvester) with PaperSearchService.search()
- PaperSearchService handles dedup and persistence internally, removing
  the need for separate dedup and store phases
- Remove unused imports (HarvestedPaper, HarvestResult, HarvestRunResult,
  PaperDeduplicator, HarvesterPort, individual harvesters)
- Keep pipeline orchestration: keyword expansion, venue recommendation,
  harvest run records, progress events

Closes #79
Delete PapersCoolTopicSearchWorkflow, TopicSearchSourceRegistry,
SemanticScholarSearch and their test files. Update all test mocks
to patch _run_topic_search / run_unified_topic_search instead of
the removed classes. Fix pre-existing SSE event assertions that
expected per-paper judge events no longer emitted by
EnrichmentPipeline.

Closes #80
Rewrite dashboard with clean 2/3+1/3 grid (StatsBar, ActivityTimeline,
LLMUsageChart, SavedPapers, DeadlineRadar), delete 9 deprecated
components. Rebuild settings page with ccswitch-style provider cards
and Dialog form. Add KeychainStore for macOS Keychain API key storage
with DB encryption fallback. Add usage proxy route.
refactor: dashboard/settings redesign + keychain API key storage
Copilot AI review requested due to automatic review settings February 12, 2026 13:43
@jerry609
jerry609 merged commit 6064306 into master Feb 12, 2026
2 of 6 checks passed
@coderabbitai

coderabbitai Bot commented Feb 12, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This PR consolidates the search architecture by removing topic search workflows and semantic scholar utilities, integrating a unified PaperSearchService into harvest and context pipelines. It adds system keychain support for API key storage, introduces evidence quote capture in paper judgments, expands the research API with metrics and export endpoints, and redesigns the frontend dashboard with updated data structures and component composition.

Changes

Cohort / File(s) Summary
Dependencies & Secret Management
pyproject.toml, src/paperbot/utils/secret.py
Added cryptography and keyring dependencies; introduced encryption/decryption functions using Fernet with configurable key derivation from environment.
Keychain Integration
src/paperbot/infrastructure/stores/keychain.py, src/paperbot/infrastructure/stores/model_endpoint_store.py
Implemented system keyring store with availability checks and fallback logic; extended model endpoint storage to optionally persist API keys in keychain with marker-based retrieval and key_source field in responses.
Search Architecture Refactoring
src/paperbot/application/workflows/paperscool_topic_search.py, src/paperbot/application/workflows/topic_search_sources.py, src/paperbot/utils/search.py, tests/unit/test_paperscool_topic_search.py, tests/unit/test_topic_search_sources.py
Removed legacy PapersCoolTopicSearchWorkflow, TopicSearchSourceRegistry, and SemanticScholarSearch utilities; eliminated per-source harvester abstractions and topic source protocols in favor of unified search service approach.
Harvest Pipeline Unification
src/paperbot/application/workflows/harvest_pipeline.py
Replaced per-source harvesters and in-memory deduplication with PaperSearchService dependency injection; consolidated search phases and updated result reporting to reflect unified search outcomes (papers_found, papers_new, papers_deduplicated).
Context Engine Simplification
src/paperbot/context_engine/engine.py
Removed legacy paper_searcher parameter; simplified paper search path with warning when PaperSearchService unavailable; eliminated SemanticScholarSearch instantiation.
Evidence Capture & Paper Judgments
src/paperbot/application/workflows/analysis/judge_prompts.py, src/paperbot/application/workflows/analysis/paper_judge.py
Extended judge prompts with evidence_quotes field schema; added evidence_quotes list to PaperJudgment with parsing/validation logic for quote text, source URLs, and page hints.
Model Endpoint Testing
src/paperbot/api/routes/model_endpoints.py, tests/unit/test_connection_test_endpoint.py
Added runtime latency measurement and success flag to EndpointTestResponse; extended with optional error field; added unit tests validating latency reporting and 404 handling.
Research API Expansion
src/paperbot/api/routes/research.py
Introduced GET /research/metrics/evidence-coverage endpoint with EvidenceCoverageResponse; added GET /research/papers/export with BibTeX, RIS, and Markdown format support; included citation key generation, deduplication, and per-format rendering helpers.
PapersCool Route Logic
src/paperbot/api/routes/paperscool.py
Updated _count_report_claims_and_evidence to account for nested judge.evidence_quotes; simplified _run_topic_search to directly return unified search results instead of conditionally delegating to workflow.
Paper Store Sorting
src/paperbot/infrastructure/stores/paper_store.py
Fixed sort key logic in get_user_library to use timestamp value directly instead of accessing .ts attribute; maintains None fallback behavior.
Frontend Dashboard Redesign
web/src/app/dashboard/page.tsx, web/src/components/dashboard/StatsBar.tsx, web/src/components/dashboard/ActivityTimeline.tsx, web/src/components/dashboard/SavedPapers.tsx, web/src/components/dashboard/ActivityFeed.tsx, web/src/components/dashboard/PipelineStatus.tsx, web/src/components/dashboard/QuickActions.tsx, web/src/components/dashboard/QuickFilters.tsx, web/src/components/dashboard/ReadingQueue.tsx, web/src/components/dashboard/StatsCard.tsx, web/src/components/dashboard/feed/*
Replaced ActivityFeed, StatsCard, PipelineStatus, ReadingQueue, QuickActions with StatsBar, ActivityTimeline, SavedPapers components; updated API calls to fetchActivities and fetchSavedPapers; removed trending/pipeline data flows; restructured layout with timeline and sidebar stacking.
Frontend Metric Components
web/src/components/dashboard/DeadlineRadar.tsx, web/src/components/dashboard/LLMUsageChart.tsx
Tightened spacing and typography; limited DeadlineRadar to 5 items with simplified empty state; added totals bar to LLMUsageChart with conditional data rendering and no-data fallback.
Research UI Enhancements
web/src/components/research/PaperCard.tsx, web/src/components/research/SavedTab.tsx, web/src/components/research/SearchResults.tsx, web/src/components/research/TopicWorkflowDashboard.tsx
Added evidence quotes toggle and display in PaperCard with source link and page hint support; added export dropdown (BibTeX, RIS, Markdown) to SavedTab; added papers_cool and HF Daily source options; added Markdown download capability to topic workflow dashboard.
Type & API Layer Updates
web/src/lib/types.ts, web/src/lib/api.ts, web/src/app/api/model-endpoints/usage/route.ts
Replaced Activity with TimelineItem (id, kind, title, subtitle, timestamp); added SavedPaper type; removed PipelineTask and ReadingQueueItem; added Paper.status "Saved" literal; updated fetchActivities and added fetchSavedPapers; removed fetchPipelineTasks and fetchReadingQueue; added model-endpoints usage proxy route.
Test Suite Additions & Updates
tests/unit/test_connection_test_endpoint.py, tests/unit/test_evidence_coverage_route.py, tests/unit/test_export.py, tests/unit/test_feed_ranking.py, tests/unit/test_model_endpoint_store.py, tests/unit/test_paper_judge.py, tests/unit/test_arq_daily_papers.py, tests/unit/test_paperscool_cli.py, tests/unit/test_paperscool_route.py, tests/framework.py
Added tests for endpoint testing, evidence coverage metrics, BibTeX/RIS/Markdown export, feed ranking, and evidence quote parsing; updated mocks to use unified topic search function instead of workflow; removed TestSearchModule.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • ThankUYou
  • wen-placeholder

Poem

🐰 A Rabbit's Ode to the Search Refresh

The warren once scattered, now gathers as one,
Through keychains and secrets, the harvest is spun.
With evidence quoted and papers set free,
The dashboard blooms bright—what a sight it will be! 📚✨

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/datainfra-refactor-tracker

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @jerry609, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a comprehensive set of improvements focused on strengthening the application's data infrastructure, enhancing security, and refining the user experience. It streamlines paper discovery and management through a unified search service, introduces robust API key handling, and provides new tools for analyzing and exporting research data. The user interface has also been modernized for better clarity and functionality.

Highlights

  • Unified Paper Search Service: The paper harvesting pipeline has been refactored to use a new unified PaperSearchService, consolidating search logic and removing several legacy search-related files and classes.
  • Secure API Key Management: API key storage has been enhanced with integration of system keychains (via keyring) and a new symmetric encryption utility (cryptography), providing a more secure way to manage model endpoint credentials.
  • Enhanced Model Endpoint Testing: Model endpoint connection tests now include latency tracking and more detailed success/error reporting, improving diagnostic capabilities.
  • Evidence Coverage Metrics: A new API endpoint and response model have been introduced to track and report evidence coverage in research reports, enhancing data quality assessment.
  • Paper Export Functionality: Users can now export saved papers in various formats including BibTeX, RIS, and Markdown, improving interoperability and data sharing.
  • Frontend Dashboard Redesign: The main dashboard has been significantly redesigned for a cleaner, more organized layout, featuring new components like an activity timeline, stats bar, and saved papers display.
  • Improved Model Endpoint Settings UI: The settings page for model endpoints has been updated with a new dialog for adding/editing providers, API key masking, and clear indication of API key storage source (keychain or database).
  • Paper Judgment with Evidence Quotes: The paper judgment system now supports storing and displaying exact evidence quotes from papers, enriching the review process and providing direct textual support for judgments.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • pyproject.toml
    • Added cryptography and keyring dependencies.
  • src/paperbot/api/routes/model_endpoints.py
    • Enhanced model endpoint testing to include latency measurement and detailed success/error reporting.
  • src/paperbot/api/routes/paperscool.py
    • Updated evidence counting logic to include judge-provided quotes and refactored topic search to use a unified service.
  • src/paperbot/api/routes/research.py
    • Introduced new API endpoints for evidence coverage metrics and exporting saved papers in BibTeX, RIS, and Markdown formats.
  • src/paperbot/application/workflows/analysis/judge_prompts.py
    • Modified the paper judgment prompt to request evidence quotes.
  • src/paperbot/application/workflows/analysis/paper_judge.py
    • Updated paper judgment logic to incorporate and parse evidence quotes.
  • src/paperbot/application/workflows/harvest_pipeline.py
    • Refactored the paper harvesting pipeline to utilize a unified search service, simplifying source management and deduplication.
  • src/paperbot/application/workflows/paperscool_topic_search.py
    • Removed the legacy PapersCool topic search workflow file.
  • src/paperbot/application/workflows/topic_search_sources.py
    • Removed the legacy topic search sources definition file.
  • src/paperbot/context_engine/engine.py
    • Simplified context engine initialization by removing direct paper searcher dependency and PaperMeta import.
  • src/paperbot/infrastructure/stores/keychain.py
    • Added a new module for secure API key storage using system keychains.
  • src/paperbot/infrastructure/stores/model_endpoint_store.py
    • Integrated Keychain for API key storage and retrieval, enhancing security and key management.
  • src/paperbot/infrastructure/stores/paper_store.py
    • Corrected a sorting key in the user library retrieval function.
  • src/paperbot/utils/search.py
    • Removed the legacy Semantic Scholar search utility file.
  • src/paperbot/utils/secret.py
    • Added a new module for symmetric encryption of secrets using Fernet.
  • tests/test_framework.py
    • Removed tests related to the deprecated search module.
  • tests/unit/test_arq_daily_papers.py
    • Updated daily papers job tests to reflect the new unified topic search workflow.
  • tests/unit/test_connection_test_endpoint.py
    • Added new unit tests for model endpoint connection testing.
  • tests/unit/test_evidence_coverage_route.py
    • Added new unit tests for the evidence coverage API endpoint.
  • tests/unit/test_export.py
    • Added new unit tests for paper export functionalities.
  • tests/unit/test_feed_ranking.py
    • Added new unit tests for feed ranking logic.
  • tests/unit/test_model_endpoint_store.py
    • Corrected database path string literals in model endpoint store tests.
  • tests/unit/test_paper_judge.py
    • Added unit tests for parsing and handling evidence quotes in paper judgments.
  • tests/unit/test_paperscool_cli.py
    • Updated CLI tests to use the unified topic search workflow.
  • tests/unit/test_paperscool_route.py
    • Updated API route tests to use the unified topic search and removed obsolete judge event assertions.
  • tests/unit/test_paperscool_topic_search.py
    • Removed the unit test file for the deprecated PapersCool topic search.
  • tests/unit/test_topic_search_sources.py
    • Removed the unit test file for deprecated topic search sources.
  • web/src/app/api/model-endpoints/usage/route.ts
    • Added a new API route for proxying model endpoint usage data.
  • web/src/app/dashboard/page.tsx
    • Redesigned the dashboard page to feature new activity timeline, stats bar, and saved papers components.
  • web/src/app/settings/page.tsx
    • Refactored the model endpoint settings page with an improved UI, including a dialog for editing and adding providers, and displaying API key source.
  • web/src/components/dashboard/ActivityFeed.tsx
    • Removed the ActivityFeed component.
  • web/src/components/dashboard/ActivityTimeline.tsx
    • Added a new component to display recent user activities.
  • web/src/components/dashboard/DeadlineRadar.tsx
    • Adjusted the display of deadline items and links.
  • web/src/components/dashboard/LLMUsageChart.tsx
    • Updated the LLM usage chart to display total tokens and cost, and improved chart rendering.
  • web/src/components/dashboard/PipelineStatus.tsx
    • Removed the PipelineStatus component.
  • web/src/components/dashboard/QuickActions.tsx
    • Removed the QuickActions component.
  • web/src/components/dashboard/QuickFilters.tsx
    • Removed the QuickFilters component.
  • web/src/components/dashboard/ReadingQueue.tsx
    • Removed the ReadingQueue component.
  • web/src/components/dashboard/SavedPapers.tsx
    • Added a new component to display recently saved papers.
  • web/src/components/dashboard/StatsBar.tsx
    • Added a new component to display key statistics in a compact bar format.
  • web/src/components/dashboard/StatsCard.tsx
    • Removed the StatsCard component.
  • web/src/components/dashboard/feed/ConferenceCard.tsx
    • Removed the ConferenceCard component.
  • web/src/components/dashboard/feed/MilestoneCard.tsx
    • Removed the MilestoneCard component.
  • web/src/components/dashboard/feed/NewPaperCard.tsx
    • Removed the NewPaperCard component.
  • web/src/components/research/PaperCard.tsx
    • Added UI elements to display evidence quotes within paper judgment details.
  • web/src/components/research/SavedTab.tsx
    • Implemented functionality to export saved papers in various formats.
  • web/src/components/research/SearchResults.tsx
    • Expanded search source options to include papers.cool and HF Daily.
  • web/src/components/research/TopicWorkflowDashboard.tsx
    • Added a download button for markdown reports in the topic workflow dashboard.
  • web/src/lib/api.ts
    • Updated API fetching logic to align with new dashboard components and removed deprecated fetches.
  • web/src/lib/types.ts
    • Updated data types to support new dashboard components and removed deprecated activity types.
Activity
  • The pull request introduces new dependencies cryptography and keyring for enhanced security.
  • Refactoring efforts have consolidated paper search logic into a unified PaperSearchService, leading to the removal of several legacy search-related files and classes.
  • API key management has been improved with support for system keychains, providing a more secure storage mechanism.
  • New metrics and export functionalities have been added to the research API, allowing for better tracking and sharing of research data.
  • The frontend dashboard and settings pages have undergone a significant overhaul, resulting in a cleaner, more organized, and feature-rich user experience.
  • Comprehensive unit tests have been added for new features like connection testing, evidence coverage, and paper export.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the web dashboard/activity feed and the backend search/harvest + provider configuration plumbing, adding export + evidence/metrics capabilities and removing legacy topic-search/search utilities.

Changes:

  • Replace the dashboard “feed” cards with a simplified ActivityTimeline + SavedPapers + StatsBar layout and update related types/API helpers.
  • Add paper export (BibTeX/RIS/Markdown) and evidence coverage metrics endpoints; extend paper judge output with evidence quotes.
  • Add encrypted/keychain-backed model provider API key storage and update settings UI; migrate harvest pipeline/context engine to unified PaperSearchService.

Reviewed changes

Copilot reviewed 50 out of 50 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
web/src/lib/types.ts Extend Paper.status; replace legacy Activity with TimelineItem; add SavedPaper type; remove pipeline/queue types.
web/src/lib/api.ts Update dashboard data fetchers (fetchActivities, fetchSavedPapers); make LLM usage fetch SSR-compatible via Next proxy.
web/src/components/research/TopicWorkflowDashboard.tsx Add markdown report download button.
web/src/components/research/SearchResults.tsx Add new search source options.
web/src/components/research/SavedTab.tsx Add export dropdown (BibTeX/RIS/Markdown) for saved papers.
web/src/components/research/PaperCard.tsx Render judge evidence quotes section in paper cards.
web/src/components/dashboard/feed/NewPaperCard.tsx Remove legacy feed card.
web/src/components/dashboard/feed/MilestoneCard.tsx Remove legacy feed card.
web/src/components/dashboard/feed/ConferenceCard.tsx Remove legacy feed card.
web/src/components/dashboard/StatsCard.tsx Remove legacy stats card component.
web/src/components/dashboard/StatsBar.tsx Add compact stats bar component for dashboard header.
web/src/components/dashboard/SavedPapers.tsx Add “Saved Papers” dashboard card.
web/src/components/dashboard/ReadingQueue.tsx Remove legacy reading queue card.
web/src/components/dashboard/QuickFilters.tsx Remove legacy quick filters card.
web/src/components/dashboard/QuickActions.tsx Remove legacy quick actions card.
web/src/components/dashboard/PipelineStatus.tsx Remove legacy pipeline status card.
web/src/components/dashboard/LLMUsageChart.tsx Compact chart header and add “no data” empty state.
web/src/components/dashboard/DeadlineRadar.tsx Compact layout, limit items shown, tweak track badges/links.
web/src/components/dashboard/ActivityTimeline.tsx Add “Recent Activity” timeline card.
web/src/components/dashboard/ActivityFeed.tsx Remove legacy activity feed container.
web/src/app/settings/page.tsx Redesign provider settings UI around cards + add/edit dialog; surface key presence/source.
web/src/app/dashboard/page.tsx Rebuild dashboard page using new components and new fetchers.
web/src/app/api/model-endpoints/usage/route.ts Add Next.js proxy route for usage endpoint.
tests/unit/test_topic_search_sources.py Remove tests for deleted legacy topic source registry.
tests/unit/test_paperscool_topic_search.py Remove tests for deleted legacy papers.cool workflow.
tests/unit/test_paperscool_route.py Update route tests to monkeypatch async unified search runner; adjust SSE expectations.
tests/unit/test_paperscool_cli.py Update CLI tests to mock unified topic search entrypoint.
tests/unit/test_paper_judge.py Add coverage for parsing/serializing evidence_quotes.
tests/unit/test_model_endpoint_store.py Fix sqlite tmp_path quoting.
tests/unit/test_feed_ranking.py Add test asserting saved papers rank above skipped papers.
tests/unit/test_export.py Add unit tests for BibTeX/RIS/Markdown export helpers.
tests/unit/test_evidence_coverage_route.py Add test for evidence coverage metrics endpoint.
tests/unit/test_connection_test_endpoint.py Add tests for provider connection test endpoint behavior.
tests/unit/test_arq_daily_papers.py Update job test to mock unified topic search.
tests/test_framework.py Remove tests for deleted tools.search module.
src/paperbot/utils/secret.py Add Fernet-based encryption helpers for secrets at rest.
src/paperbot/utils/search.py Remove legacy Semantic Scholar search utility module.
src/paperbot/infrastructure/stores/paper_store.py Fix library sorting key to use timestamp directly.
src/paperbot/infrastructure/stores/model_endpoint_store.py Store API keys in keychain when available, else encrypted in DB; expose key source.
src/paperbot/infrastructure/stores/keychain.py Add keyring-based keychain abstraction.
src/paperbot/context_engine/engine.py Remove legacy S2 search fallback; rely on injected search_service.
src/paperbot/application/workflows/topic_search_sources.py Remove legacy topic search source registry.
src/paperbot/application/workflows/paperscool_topic_search.py Remove legacy papers.cool topic search workflow.
src/paperbot/application/workflows/harvest_pipeline.py Switch harvesting to PaperSearchService unified search path.
src/paperbot/application/workflows/analysis/paper_judge.py Add evidence_quotes parsing and serialization.
src/paperbot/application/workflows/analysis/judge_prompts.py Update judge prompt schema to request evidence quotes.
src/paperbot/api/routes/research.py Add evidence coverage endpoint + paper export endpoint and helpers.
src/paperbot/api/routes/paperscool.py Count evidence quotes as evidence in metrics.
src/paperbot/api/routes/model_endpoints.py Add latency/success fields to connection test response.
pyproject.toml Add cryptography and keyring dependencies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1748 to +1755
const blob = new Blob([dailyResult.markdown || ""], { type: "text/markdown" })
const url = URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = `dailypaper-${dailyResult.report.date || "report"}.md`
a.click()
URL.revokeObjectURL(url)
}}

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The blob download revokes the object URL immediately after a.click(). This can intermittently cancel downloads in some browsers; revoke the URL asynchronously (e.g., in setTimeout) after the click is triggered.

Copilot uses AI. Check for mistakes.
Comment on lines 138 to +142
elif not api_key_text.startswith("***"):
row.api_key_value = api_key_text
if KeychainStore.store_key(name, api_key_text):
row.api_key_value = _KEYCHAIN_MARKER
else:
row.api_key_value = _encrypt_secret(api_key_text)

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When storing a new API key, KeychainStore.store_key(name, …) uses the updated name. If the endpoint was renamed, the old keychain secret under the previous name will remain orphaned (and could be used unintentionally if a provider is recreated with the old name). Consider deleting/migrating the old keychain entry when row.name changes.

Copilot uses AI. Check for mistakes.
Comment on lines +586 to 590
Logger.warning(
"No search_service provided — skipping paper search. "
"Pass a PaperSearchService instance to ContextEngine.",
file=LogFiles.HARVEST,
)

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If search_service is None, the engine now silently skips paper search and returns no recommendations. However, the API layer still logs that it will “fallback to legacy S2 path” when PaperSearchService init fails. Either restore a real fallback here (e.g., legacy Semantic Scholar search) or propagate an explicit error so the caller/UI can handle degraded mode intentionally.

Copilot uses AI. Check for mistakes.
search_result = await self.search_service.search(
search_query,
sources=sources,
max_results=config.max_results_per_source * len(sources),

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PaperSearchService.search() forwards max_results to each adapter. Passing config.max_results_per_source * len(sources) means each source may now fetch far more than max_results_per_source, increasing latency and load. To preserve the config semantics, pass config.max_results_per_source to the service (or extend the service API to accept per-source limits).

Suggested change
max_results=config.max_results_per_source * len(sources),
max_results=config.max_results_per_source,

Copilot uses AI. Check for mistakes.
Comment on lines +209 to +211
papers_found = search_result.total_raw
papers_new = len(search_result.papers)
deduplicated_count = search_result.duplicates_removed

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

papers_new is set to len(search_result.papers), which is “unique returned” (and also capped by max_results), not “newly created rows” as in the previous implementation. If HarvestRunModel.papers_new is used as “created count”, this will misreport; consider tracking created/updated counts during the persist loop and storing created as papers_new (or rename the field/variable to reflect the new meaning).

Copilot uses AI. Check for mistakes.
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional

from loguru import logger

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logger from loguru is imported but not used in this module; please remove it to avoid lint/typecheck failures from unused imports.

Suggested change
from loguru import logger

Copilot uses AI. Check for mistakes.
Comment on lines 134 to 138
api_key_text = str(payload.get("api_key") or "").strip()
if not api_key_text:
row.api_key_value = None
KeychainStore.delete_key(name)
elif not api_key_text.startswith("***"):

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When clearing an API key (api_key provided but empty), KeychainStore.delete_key(name) uses the new name value. If the endpoint is being renamed in the same update, the old keychain entry under the previous name will be left behind. Capture the previous row.name before overwriting it and delete both old/new names (or rename the keychain entry).

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +49
logger.warning(
f"{_ENV_KEY} is not set — using deterministic fallback key. "
"Set this variable in production to secure API keys at rest."
)
key = base64.urlsafe_b64encode(
hashlib.sha256(b"paperbot-default-insecure-key").digest()
)

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “deterministic fallback key” means secrets are effectively encrypted with a hard-coded constant when PAPERBOT_SECRET_KEY is unset, which provides little real protection at rest (an attacker with DB access can decrypt across installs). For production safety, consider failing fast when the env var is missing (or generating a random key persisted outside the DB) rather than using a deterministic default.

Copilot uses AI. Check for mistakes.
papers={stats.new_papers}
tokens={stats.llm_usage}
saved={stats.read_later}
tracks={deadlines.length}

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StatsBar displays the label “tracks”, but the value passed is deadlines.length (number of upcoming deadlines, not number of research tracks). This will show misleading UI (e.g. “5 tracks” when it’s 5 deadlines); either pass an actual track count or rename the prop/label to reflect what’s being shown.

Suggested change
tracks={deadlines.length}
tracks={stats.tracked_scholars}

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +70
const handleExport = async (format: "bibtex" | "ris" | "markdown") => {
const qs = new URLSearchParams({ format, user_id: userId })
if (trackId) qs.set("track_id", String(trackId))
try {
const res = await fetch(`/api/papers/export?${qs.toString()}`)
if (!res.ok) throw new Error(`${res.status}`)
const blob = await res.blob()

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleExport fetches /api/papers/export, but this PR only adds the FastAPI route at /api/research/papers/export and there is no Next.js proxy route for /api/papers/export (only /api/papers/library etc.). This will 404 and make Export unusable; either add a Next.js route that proxies to the backend export endpoint, or update the client to call the existing /api/research/papers/export proxy (and add that proxy route if missing).

Copilot uses AI. Check for mistakes.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant refactoring of the data infrastructure, focusing on tracker functionality. Key changes include:

  • Secret Management: API keys are now securely stored using keyring for system keychain integration and cryptography for fallback database encryption. This is a great security enhancement.
  • Search Service Refactoring: The paper harvesting and search logic has been centralized into a PaperSearchService, simplifying components like HarvestPipeline and ContextEngine. Several old workflow and search modules have been removed, leading to a cleaner architecture.
  • New Features:
    • Paper export functionality (BibTeX, RIS, Markdown) has been added.
    • An evidence coverage metric endpoint is introduced.
    • The paper judging mechanism now supports evidence_quotes.
  • UI Overhaul: The dashboard and settings pages have been completely redesigned for a much-improved user experience. The settings page now uses a more intuitive card-based layout with dialogs for editing, and the dashboard is more streamlined.

My feedback focuses on improving API consistency and fixing a minor bug in the frontend's handling of masked keys. Overall, this is a high-quality pull request with substantial improvements across the board.

Comment on lines 69 to +70
ok: bool
success: bool

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The EndpointTestResponse model has both ok and success boolean fields, which are redundant. To improve API clarity and consistency, it's best to use a single field to indicate the outcome. I recommend removing the ok field and relying solely on success.

Suggested change
ok: bool
success: bool
success: bool

Comment on lines 210 to 212
except Exception as exc:
latency_ms = int((time.monotonic() - t0) * 1000)
raise HTTPException(status_code=400, detail=f"test failed: {exc}") from exc

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error handling in this except block can be improved for better API consistency. Currently, it calculates latency_ms but this value is lost as an HTTPException is raised. The EndpointTestResponse model is already designed to handle error states with its success and error fields.

I suggest refactoring this to return an EndpointTestResponse with success=False on failure. This provides the client with a consistent response structure, including the latency and a detailed error message, rather than an abrupt HTTP error.

Suggested change
except Exception as exc:
latency_ms = int((time.monotonic() - t0) * 1000)
raise HTTPException(status_code=400, detail=f"test failed: {exc}") from exc
except Exception as exc:
latency_ms = int((time.monotonic() - t0) * 1000)
return EndpointTestResponse(
ok=False,
success=False,
endpoint_id=endpoint_id,
provider={},
api_key_present=api_key_present,
latency_ms=latency_ms,
message=f"Test failed: {exc}",
error=str(exc),
)

Comment on lines +122 to +126
function maskKey(key?: string): string {
if (!key) return ""
if (key.length <= 8) return "****"
return "****" + key.slice(-4)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The maskKey function is redundant and its logic is inconsistent with the backend's masking function. The backend already provides a masked API key for display. Applying another mask on the frontend can lead to confusing output or unintentionally reveal parts of the key.

I recommend removing this maskKey function and directly using the item.api_key value from the backend in the UI. The display logic on line 312 should be simplified to Key: {item.api_key_present ? item.api_key : "missing"}.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants