Skip to content

feat: deliver daily push epic #179 core pipeline and channel integration#190

Merged
jerry609 merged 7 commits into
masterfrom
feat/daily-push-epic-179
Mar 3, 2026
Merged

feat: deliver daily push epic #179 core pipeline and channel integration#190
jerry609 merged 7 commits into
masterfrom
feat/daily-push-epic-179

Conversation

@jerry609

@jerry609 jerry609 commented Mar 3, 2026

Copy link
Copy Markdown
Owner

What changed

This PR delivers the main implementation for Epic #179 and follow-up fixes on feat/daily-push-epic-179:

  • Daily digest content enhancement:
    • digest_card extraction (highlight/method/finding/tags)
    • HF upvotes added into Judge prompt context
    • email template rendering updated for digest-card fields
  • MinerU integration:
    • Cloud API figure extraction + main-figure heuristic
    • ARQ cron now forwards figure flags to daily_papers_job
    • MinerU extraction result cache (URL hash + TTL)
  • Multi-channel push:
    • Apprise notifier integration
    • channel formatter registry (telegram/discord/wecom/feishu)
    • formatter payloads wired into digest send path
  • Feed support:
    • /api/feed/daily.xml
    • /api/feed/daily.atom
    • /api/feed/track/{track}.xml
    • /api/feed/keyword/{keyword}.xml (new)
    • lightweight feed cache by reports signature
    • RSS enclosure support for main_figure

Issue mapping

Notes on completion scope

This PR is intended as the core delivery for Epic #179. Some issue checklists include product-level acceptance items (for example: manual quality sampling, bot command UX, advanced provider-specific interactions) that may require a follow-up PR for strict checklist closure.

Validation

  • python -m py_compile passed for modified Python modules/tests
  • Local pytest remains unstable on this machine (historical segfault); CI should be used as source of truth

Summary by CodeRabbit

  • New Features

    • Multi-channel push notifications to Telegram, Discord, WeCom, Feishu, Slack, Email, and DingTalk with customizable configuration.
    • RSS/Atom feed generation for daily paper digests with keyword and track filtering.
    • Enhanced paper digest cards with highlights, methods, findings, and tags.
    • Automatic figure extraction from PDF papers via cloud integration.
    • Push channel management UI in Settings with test functionality.
    • Dashboard enhancements for digest card display and filtering by recommendation level and tags.
  • Documentation

    • Added feature specifications for push channel configuration and dashboard digest enhancements.
  • Tests

    • Added comprehensive test coverage for push channels, feed generation, digest cards, and figure extraction.

jerry609 and others added 7 commits March 2, 2026 19:56
Multi-channel push infrastructure with structured digest cards:

- #187: Integrate HF upvotes into Judge scoring prompt
- #180: Add digest_card extraction (highlight/method/finding/tags)
  with new LLM prompt, email template rendering, markdown output
- #181: MinerU Cloud API client for PDF figure extraction
- #182: Apprise multi-channel push layer with YAML config
- #183-185: Channel formatters (Telegram MarkdownV2, Discord Rich
  Embed, WeCom markdown+news, Feishu/Lark interactive card)
- #186: RSS 2.0 + Atom feed endpoints (/api/feed/daily.xml, .atom)

Also creates follow-up issues #190 (Push Channel Settings UI)
and #191 (Dashboard Digest Card Enhancement).

56 new tests, all passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 3, 2026 06:01
@vercel

vercel Bot commented Mar 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
paper-bot Ready Ready Preview, Comment Mar 3, 2026 6:01am

@coderabbitai

coderabbitai Bot commented Mar 3, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee8ceb6 and 81cdb0c.

📒 Files selected for processing (34)
  • config/push_channels.yaml
  • docs/issues/190-settings-push-channel-ui.md
  • docs/issues/191-dashboard-digest-card-enhancement.md
  • docs/progress/progress.txt
  • requirements.txt
  • src/paperbot/api/main.py
  • src/paperbot/api/routes/feed.py
  • src/paperbot/application/prompts/paper_analysis.py
  • src/paperbot/application/prompts/registry.py
  • src/paperbot/application/services/daily_push_service.py
  • src/paperbot/application/services/email_template.py
  • src/paperbot/application/services/llm_service.py
  • src/paperbot/application/workflows/analysis/judge_prompts.py
  • src/paperbot/application/workflows/dailypaper.py
  • src/paperbot/infrastructure/extractors/__init__.py
  • src/paperbot/infrastructure/extractors/mineru_client.py
  • src/paperbot/infrastructure/push/__init__.py
  • src/paperbot/infrastructure/push/apprise_notifier.py
  • src/paperbot/infrastructure/push/formatters/__init__.py
  • src/paperbot/infrastructure/push/formatters/base.py
  • src/paperbot/infrastructure/push/formatters/discord.py
  • src/paperbot/infrastructure/push/formatters/feishu.py
  • src/paperbot/infrastructure/push/formatters/telegram.py
  • src/paperbot/infrastructure/push/formatters/wecom.py
  • src/paperbot/infrastructure/queue/arq_worker.py
  • src/paperbot/presentation/cli/main.py
  • src/paperbot/workflows/feed.py
  • tests/unit/test_apprise_notifier.py
  • tests/unit/test_arq_daily_papers.py
  • tests/unit/test_daily_digest_card.py
  • tests/unit/test_feed_routes.py
  • tests/unit/test_hf_upvote_judge_integration.py
  • tests/unit/test_mineru_client.py
  • tests/unit/test_push_formatters.py

📝 Walkthrough

Walkthrough

This pull request introduces comprehensive multi-channel push notification infrastructure for DailyPaper digests. It adds RSS/Atom feed generation endpoints, integrates MinerU Cloud API for PDF figure extraction, implements channel-specific formatters (Telegram, Discord, WeCom, Feishu), and enriches reports with LLM-generated daily digest cards via Apprise library.

Changes

Cohort / File(s) Summary
Configuration & Documentation
config/push_channels.yaml, docs/issues/19*.md, docs/progress/progress.txt, requirements.txt
Adds push channels YAML template, feature plans for push channels UI and dashboard enhancements, progress tracking, and dependencies (apprise, feedgen).
Feed API & Routing
src/paperbot/api/main.py, src/paperbot/api/routes/feed.py
New feed router endpoints for RSS 2.0 and Atom feeds with caching, filtering by track/keyword, and figure inclusion.
LLM Prompts & Integration
src/paperbot/application/prompts/paper_analysis.py, src/paperbot/application/prompts/registry.py, src/paperbot/application/services/llm_service.py
Adds daily_digest_card prompt templates and LLMService method to extract digest cards (highlight, method, finding, tags) from paper titles/abstracts.
Daily Paper Workflow
src/paperbot/application/workflows/dailypaper.py, src/paperbot/application/workflows/analysis/judge_prompts.py
Integrates digest_card feature flag, figure extraction via MinerU, digest card rendering, and HuggingFace upvotes in judge scoring.
Email Template Rendering
src/paperbot/application/services/email_template.py, src/paperbot/application/services/daily_push_service.py
Adds digest_card HTML/text rendering in email templates and Apprise push method in DailyPushService.
Push Infrastructure - Core
src/paperbot/infrastructure/push/__init__.py, src/paperbot/infrastructure/push/apprise_notifier.py
AppriseNotifier class for YAML-based multi-channel notification routing, daily digest pushing with per-channel formatting, and tag-based filtering.
Push Formatters
src/paperbot/infrastructure/push/formatters/*
Base formatter interface and five channel-specific formatters: Telegram (MarkdownV2 with inline keyboard), Discord (Rich Embeds), WeCom (markdown + news articles), Feishu (interactive cards), with per-paper badges and tag support.
Figure Extraction
src/paperbot/infrastructure/extractors/mineru_client.py, src/paperbot/infrastructure/extractors/__init__.py
MinerU Cloud API client with local disk caching, TTL invalidation, and heuristic-based main figure selection from PDF extractions.
Queue & CLI Integration
src/paperbot/infrastructure/queue/arq_worker.py, src/paperbot/presentation/cli/main.py, src/paperbot/workflows/feed.py
ARQ job orchestration for optional figure extraction, CLI flags for figure enablement, and ScholarFeedService export method for feed entries.
Tests
tests/unit/test_*.py (7 new modules)
Comprehensive unit tests covering apprise notifier, feed routes, daily digest card workflow, formatters (all channels), MinerU client, HF upvote integration, and ARQ job enqueuing.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant DailyPushService
    participant LLMService
    participant MineruClient
    participant Formatters
    participant AppriseNotifier
    participant ExternalAPI as External APIs<br/>(Telegram/Discord/etc)

    Client->>DailyPushService: push_dailypaper(report)
    
    loop For each top_item
        DailyPushService->>LLMService: extract_daily_digest_card(title, abstract)
        LLMService-->>DailyPushService: {highlight, method, finding, tags}
    end
    
    alt If figures enabled
        DailyPushService->>MineruClient: extract_figures(pdf_url)
        MineruClient-->>DailyPushService: [figures + main_figure]
    end
    
    DailyPushService->>DailyPushService: channel == "apprise"
    DailyPushService->>AppriseNotifier: push_daily_digest(report, markdown, html)
    
    loop For each configured channel URL
        AppriseNotifier->>Formatters: format_digest(report, channel_type)
        Formatters-->>AppriseNotifier: {formatted_payload}
        AppriseNotifier->>ExternalAPI: POST notification
        ExternalAPI-->>AppriseNotifier: {status}
    end
    
    AppriseNotifier-->>DailyPushService: {results}
    DailyPushService-->>Client: {ok, channels_notified}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A rabbit hops through channels wide,
With Telegram and Discord at its side,
Digests flow swift on Apprise wings,
While figures dance and highlights sing,
Each formatter spins its own design—
A push notification feast divine! ✨

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/daily-push-epic-179

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, 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 the foundational infrastructure for an enhanced daily paper digest system. It significantly improves the content richness of the digests by extracting key insights and figures, expands the delivery capabilities through multi-channel push notifications, and introduces flexible RSS/Atom feed generation. These changes aim to provide users with more comprehensive and accessible research paper summaries across various platforms.

Highlights

  • Daily Digest Content Enhancement: Implemented digest_card extraction (highlight, method, finding, tags) and integrated HuggingFace upvotes into the Judge prompt context. The email template rendering has been updated to display these new digest-card fields.
  • MinerU Integration for Figure Extraction: Integrated MinerU Cloud API for figure extraction and main-figure heuristic identification. The ARQ cron job now forwards figure flags to the daily_papers_job, and a cache for MinerU extraction results (URL hash + TTL) has been added.
  • Multi-channel Push Notifications: Introduced Apprise notifier integration, a channel formatter registry (Telegram, Discord, WeCom, Feishu), and wired these formatter payloads into the digest sending path for unified multi-channel delivery.
  • RSS/Atom Feed Support: Added API endpoints for /api/feed/daily.xml, /api/feed/daily.atom, /api/feed/track/{track}.xml, and a new /api/feed/keyword/{keyword}.xml. This includes lightweight feed caching by reports signature and RSS enclosure support for main_figure.
  • New Dependencies and Documentation: Added apprise and feedgen to requirements.txt. New documentation issues (#190, #191) outline future UI enhancements for push channel configuration and dashboard digest card display.

🧠 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
  • config/push_channels.yaml
    • Added a configuration template for push channels, detailing Apprise URL formats and tags for various services.
  • docs/issues/190-settings-push-channel-ui.md
    • Added a new issue document outlining requirements for a future UI to manage push channel configurations.
  • docs/issues/191-dashboard-digest-card-enhancement.md
    • Added a new issue document detailing requirements for enhancing the dashboard with digest card fields and recommendation badges.
  • docs/progress/progress.txt
  • requirements.txt
    • Added apprise and feedgen libraries for multi-channel push notifications and RSS/Atom feed generation.
  • src/paperbot/api/main.py
    • Integrated the new feed API router into the main FastAPI application.
  • src/paperbot/api/routes/feed.py
    • Added a new module defining API endpoints for daily, track-specific, and keyword-filtered RSS 2.0 and Atom feeds, including caching and figure enclosure support.
  • src/paperbot/application/prompts/paper_analysis.py
    • Added new system and user prompt templates (DAILY_DIGEST_CARD_SYSTEM, DAILY_DIGEST_CARD_USER) for extracting structured digest card information.
  • src/paperbot/application/prompts/registry.py
    • Registered the new daily_digest_card prompt template for use by the LLM service.
  • src/paperbot/application/services/daily_push_service.py
    • Modified the DailyPushService to include an _send_apprise method, enabling multi-channel push notifications via Apprise.
  • src/paperbot/application/services/email_template.py
    • Updated the email template rendering logic to incorporate and display the newly extracted digest_card fields (highlight, method, finding, tags) in both HTML and plain text formats.
  • src/paperbot/application/services/llm_service.py
    • Added a new method extract_daily_digest_card to the LLM service for extracting structured digest information from paper titles and abstracts.
  • src/paperbot/application/workflows/analysis/judge_prompts.py
    • Modified the build_paper_judge_user_prompt function to conditionally include HuggingFace upvotes in the prompt context for paper judging.
  • src/paperbot/application/workflows/dailypaper.py
    • Extended SUPPORTED_LLM_FEATURES to include digest_card, added extract_figures_for_report for MinerU integration, and integrated both digest card and figure extraction into the daily paper enrichment pipeline.
  • src/paperbot/infrastructure/extractors/init.py
    • Added an __init__.py file to define the extractors package and expose MineruClient and Figure.
  • src/paperbot/infrastructure/extractors/mineru_client.py
    • Added a new MineruClient class for interacting with the MinerU Cloud API to extract figures from PDFs, including caching and heuristics for identifying the main figure.
  • src/paperbot/infrastructure/push/init.py
    • Added an __init__.py file to define the push package and expose AppriseNotifier.
  • src/paperbot/infrastructure/push/apprise_notifier.py
    • Added a new AppriseNotifier class that wraps the Apprise library for unified multi-channel push notifications, supporting YAML configuration and channel-specific formatters.
  • src/paperbot/infrastructure/push/formatters/init.py
    • Added an __init__.py file to define the formatters package, including a registry for push formatters.
  • src/paperbot/infrastructure/push/formatters/base.py
    • Added an abstract base class PushFormatter for defining channel-specific digest formatting logic.
  • src/paperbot/infrastructure/push/formatters/discord.py
    • Added a DiscordFormatter class to format daily paper digests into Discord Rich Embed JSON payloads.
  • src/paperbot/infrastructure/push/formatters/feishu.py
    • Added a FeishuFormatter class to format daily paper digests into Feishu/Lark interactive card and post formats.
  • src/paperbot/infrastructure/push/formatters/telegram.py
    • Added a TelegramFormatter class to format daily paper digests for Telegram using MarkdownV2 and inline keyboards.
  • src/paperbot/infrastructure/push/formatters/wecom.py
    • Added a WeComFormatter class to format daily paper digests for WeCom webhooks using markdown messages and news cards.
  • src/paperbot/infrastructure/queue/arq_worker.py
    • Updated the ARQ cron job (cron_daily_papers) to pass enable_figures and figures_max_items parameters to the daily_papers_job.
  • src/paperbot/presentation/cli/main.py
    • Modified the CLI to support new arguments (--with-figures, --mineru-api-key, --figures-max-items) for controlling figure extraction during daily paper generation.
  • src/paperbot/workflows/feed.py
    • Added an export_feed_entries method to ScholarFeedService to facilitate the generation of RSS/Atom feed entries from processed events.
  • tests/unit/test_apprise_notifier.py
    • Added new unit tests for the AppriseNotifier class, covering YAML configuration parsing and push logic.
  • tests/unit/test_arq_daily_papers.py
    • Added new tests to verify that the ARQ cron job correctly enqueues figure-related flags.
  • tests/unit/test_daily_digest_card.py
    • Added comprehensive unit tests for the daily digest card feature, including prompt templates, LLM extraction, and integration into the pipeline and email templates.
  • tests/unit/test_feed_routes.py
    • Added new unit tests for the RSS/Atom feed API routes and their underlying helper functions, including content generation and filtering.
  • tests/unit/test_hf_upvote_judge_integration.py
    • Added new unit tests to confirm the correct inclusion or omission of HuggingFace upvotes in the paper judging prompt.
  • tests/unit/test_mineru_client.py
    • Added new unit tests for the MineruClient, covering figure parsing, main figure identification, and caching mechanisms.
  • tests/unit/test_push_formatters.py
    • Added new unit tests for the various push channel formatters (Telegram, Discord, WeCom, Feishu), verifying their output for sample reports and edge cases.
Activity
  • The core implementation for Epic [Epic] 每日推送优化 — 内容增强 + 图表提取 + 多渠道推送 #179, focusing on daily push, has been delivered.
  • Daily digest content has been enhanced with digest_card extraction and HuggingFace upvotes.
  • MinerU integration for Cloud API figure extraction and caching has been completed.
  • Multi-channel push capabilities via Apprise and a formatter registry have been integrated.
  • RSS/Atom feed support, including keyword feeds and enclosures, has been added.
  • The docs/progress/progress.txt file was updated on 2026-03-02 by 'claude' to reflect the completion of various issues related to this epic, including 56 new tests passing.
  • The author noted that local pytest is unstable due to historical segfaults, relying on CI for validation.
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.

@jerry609
jerry609 merged commit d417203 into master Mar 3, 2026
11 of 12 checks passed

@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 several new features and improvements, including multi-channel push notifications via Apprise, RSS/Atom feed generation, and MinerU figure extraction. It also includes a daily digest card prompt for LLM-based summarization. Review comments highlight potential security vulnerabilities with an unbounded in-memory cache, suggest using a more reliable deduplication key, and recommend refactoring the push notification logic to preserve rich formatting. Additional feedback focuses on improving error handling, code organization, and cache loading.

router = APIRouter()

_REPORTS_DIR = Path("./reports/dailypaper")
_FEED_CACHE: Dict[str, Dict[str, Any]] = {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-high high

The _FEED_CACHE dictionary is an unbounded in-memory cache. Since cache keys are derived from user-supplied path parameters (track_name and keyword), an attacker can send a large number of requests with unique values to exhaust the server's memory, leading to a Denial of Service (DoS).

Recommendation: Use a cache with a maximum size and an eviction policy (e.g., functools.lru_cache or cachetools.LRUCache).

papers: List[Dict[str, Any]] = []
for q in report.get("queries") or []:
for item in q.get("top_items") or []:
key = item.get("title") or id(item)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using id(item) as a fallback key for deduplication is not reliable. The id() will be different each time the report is loaded from a file, and it will also be different for two separate dictionary objects even if they represent the same paper. This will cause deduplication to fail. A more stable unique identifier, like the paper's URL, should be used as a primary or secondary key.

Suggested change
key = item.get("title") or id(item)
key = item.get("url") or item.get("title")

Comment on lines +224 to +245
def _build_channel_body(
self,
url: str,
*,
report: Dict[str, Any],
fallback_html: str,
fallback_markdown: str,
subject: str,
) -> tuple[str, str]:
channel_type = self._channel_type_from_url(url)
payload = self._format_payload(channel_type, report)

if payload:
body, body_format = self._payload_to_body(channel_type, payload)
if body:
return body, body_format

if fallback_html:
return fallback_html, "html"
if fallback_markdown:
return fallback_markdown, "markdown"
return f"New daily digest: {subject}", "text"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

This implementation degrades the rich, structured payloads generated by the formatters into simple markdown strings before sending. For example, it converts Discord embeds into plain markdown and ignores Telegram's inline_keyboard and photo_url fields. This defeats the purpose of having channel-specific formatters that create native rich messages.

To fix this, push_daily_digest should be refactored to inspect the formatter's payload and pass the rich content to Apprise correctly. Apprise's notify method is flexible and can often handle richer content when the right parameters are used, or you may need to construct the notification body differently for each channel type to preserve the rich formatting.

Comment thread config/push_channels.yaml
#
# Examples (uncomment and fill in credentials):

channels: []

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 channels: [] syntax can be confusing for users. When they uncomment the example channels, they will also need to remember to remove the [] to make the YAML valid. Using channels: (with a null value) would be more intuitive, as it works correctly for both an empty configuration and when channels are added.

channels:

Comment on lines +35 to +36
except Exception:
continue

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

Silently ignoring all exceptions with except Exception: continue can hide important issues, such as file permission errors or malformed JSON that is not a JSONDecodeError. It would be more robust to catch specific expected exceptions and log a warning for any failures to aid in debugging.

Suggested change
except Exception:
continue
except (json.JSONDecodeError, IOError) as e:
logger.warning("Failed to load or parse report %s: %s", path, e)
continue

Comment on lines +203 to +218
try:
fe.enclosure(url=fig_url, length=0, type="image/jpeg")
except Exception:
pass

# Publication date
if generated_at:
try:
dt = datetime.fromisoformat(
generated_at.replace("Z", "+00:00")
)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
fe.pubDate(dt)
except Exception:
pass

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 broad except Exception: pass statements silently ignore potential errors during the generation of the RSS feed, for example when adding an enclosure or parsing a publication date. This can lead to incomplete feed entries without any indication of what went wrong. It's better to log these exceptions as warnings to help with debugging.

Comment on lines +293 to +305
import xml.sax.saxutils as saxutils

t = saxutils.escape(paper_title)
d = saxutils.escape(snippet[:500])
u = saxutils.escape(url)
items.append(
f"<item><title>{t}</title>"
f"<link>{u}</link>"
f"<description>{d}</description>"
f"</item>"
)

import xml.sax.saxutils as saxutils

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 xml.sax.saxutils module is imported multiple times within the _fallback_rss_xml function. According to PEP 8, imports should be at the top of the file. This improves readability and avoids the small overhead of re-importing within a function that might be called multiple times.

Comment on lines +138 to +140
except Exception:
return None
return None

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

While it's reasonable for a cache-loading failure to not crash the application, silently returning None can hide persistent problems with the cache (e.g., permission issues, disk full, corrupted files). Logging a warning here would be beneficial for debugging without changing the graceful failure behavior.

Suggested change
except Exception:
return None
return None
except Exception as e:
logger.warning("Failed to load figures from cache at %s: %s", cache_path, e)
return None

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

Implements the core delivery for Epic #179 by enhancing DailyPaper digest content (digest-card fields + HF upvotes in Judge prompt), adding MinerU-based figure extraction, introducing an Apprise-backed multi-channel push layer with per-channel formatters, and exposing RSS/Atom feed endpoints with lightweight caching.

Changes:

  • Add digest_card LLM extraction (highlight/method/finding/tags) and render it in Markdown + email (HTML/text); include HF upvotes in Judge prompt context.
  • Integrate MinerU Cloud API figure extraction (with main-figure heuristic) and forward figure flags through CLI + ARQ cron/job.
  • Add Apprise notifier + formatter registry (Telegram/Discord/WeCom/Feishu) and add RSS/Atom feed routes (daily/track/keyword) with caching + enclosures.

Reviewed changes

Copilot reviewed 33 out of 34 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/unit/test_push_formatters.py Adds unit coverage for formatter registry and per-channel digest formatting.
tests/unit/test_mineru_client.py Adds tests for MinerU client parsing, caching, and main-figure selection + pipeline hook.
tests/unit/test_hf_upvote_judge_integration.py Verifies HF upvotes are conditionally included in Judge prompt.
tests/unit/test_feed_routes.py Unit-tests feed helpers and RSS/Atom XML generation behavior.
tests/unit/test_daily_digest_card.py Tests digest-card prompt registration, pipeline enrichment, and email/markdown rendering.
tests/unit/test_arq_daily_papers.py Ensures ARQ cron forwards figure flags to the daily job.
tests/unit/test_apprise_notifier.py Tests YAML config parsing and AppriseNotifier digest push path behavior.
src/paperbot/workflows/feed.py Adds export_feed_entries() for converting feed events into feedgen-ready dicts.
src/paperbot/presentation/cli/main.py Adds CLI flags/env support to enable MinerU figure extraction.
src/paperbot/infrastructure/queue/arq_worker.py Adds figure flags to cron/job args and calls extract_figures_for_report() when enabled.
src/paperbot/infrastructure/push/formatters/wecom.py Implements WeCom digest formatting (markdown + news articles).
src/paperbot/infrastructure/push/formatters/telegram.py Implements Telegram MarkdownV2 digest formatting + inline keyboard + optional main figure.
src/paperbot/infrastructure/push/formatters/feishu.py Implements Feishu/Lark interactive card + post digest formatting.
src/paperbot/infrastructure/push/formatters/discord.py Implements Discord embed-style digest formatting.
src/paperbot/infrastructure/push/formatters/base.py Introduces PushFormatter base + shared paper collection/sorting helpers.
src/paperbot/infrastructure/push/formatters/init.py Adds formatter registry and lookup helpers.
src/paperbot/infrastructure/push/apprise_notifier.py Adds Apprise-based notifier with YAML config and per-channel formatting fallback logic.
src/paperbot/infrastructure/push/init.py Exposes AppriseNotifier at package level.
src/paperbot/infrastructure/extractors/mineru_client.py Adds MinerU Cloud API client with cache + main-figure heuristic.
src/paperbot/infrastructure/extractors/init.py Exports MineruClient/Figure symbols.
src/paperbot/application/workflows/dailypaper.py Adds digest_card feature and extract_figures_for_report() pipeline hook.
src/paperbot/application/workflows/analysis/judge_prompts.py Injects HF upvotes into Judge user prompt when present.
src/paperbot/application/services/llm_service.py Adds extract_daily_digest_card() using a new prompt template and safe JSON parsing.
src/paperbot/application/services/email_template.py Renders digest-card fields into email HTML and text output.
src/paperbot/application/services/daily_push_service.py Adds apprise as a push channel and wires AppriseNotifier into digest send flow.
src/paperbot/application/prompts/registry.py Registers the new daily_digest_card prompt template.
src/paperbot/application/prompts/paper_analysis.py Adds system/user prompts for digest-card extraction.
src/paperbot/api/routes/feed.py Adds RSS/Atom endpoints (daily/track/keyword) with helpers + caching + enclosures.
src/paperbot/api/main.py Registers the feed router under /api.
requirements.txt Adds dependencies for Apprise and feedgen.
docs/progress/progress.txt Updates progress log with completed items and validation notes.
docs/issues/191-dashboard-digest-card-enhancement.md Adds follow-up dashboard enhancement issue for digest-card/feed UI.
docs/issues/190-settings-push-channel-ui.md Adds follow-up issue for push channel configuration UI.
config/push_channels.yaml Adds Apprise push channel YAML template/config stub.

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

Comment on lines +17 to +18
_REPORTS_DIR = Path("./reports/dailypaper")
_FEED_CACHE: Dict[str, Dict[str, Any]] = {}

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

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

_FEED_CACHE is an unbounded global dict keyed by user-controlled path params (track_name, keyword). Over time (or via crafted requests) this can grow without limit and increase memory usage. Consider adding an LRU/size cap and/or TTL eviction, and optionally normalizing/limiting the length of cache keys.

Copilot uses AI. Check for mistakes.
return cls(urls=[])

with open(config_path, encoding="utf-8") as fh:
data = yaml.safe_load(fh) or {}

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

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

from_yaml() doesn't handle YAML parse errors (e.g., malformed config). yaml.safe_load() can raise and would crash daily push at runtime. Consider wrapping the load in a try/except (yaml.YAMLError/Exception) and returning an empty notifier with a warning log, similar to the missing-file path.

Suggested change
data = yaml.safe_load(fh) or {}
try:
data = yaml.safe_load(fh) or {}
except yaml.YAMLError as exc:
logger.warning("Failed to parse push config YAML %s: %s", path, exc)
return cls(urls=[])
except Exception as exc:
logger.warning("Unexpected error loading push config %s: %s", path, exc)
return cls(urls=[])

Copilot uses AI. Check for mistakes.
Comment on lines +253 to +289
def _payload_to_body(channel_type: str, payload: Dict[str, Any]) -> tuple[str, str]:
if channel_type == "telegram":
return str(payload.get("text") or ""), "markdown"
if channel_type == "wecom":
return str((payload.get("markdown") or {}).get("content") or ""), "markdown"
if channel_type in {"feishu", "lark"}:
interactive = (payload.get("interactive") or {}).get("card") or {}
elements = interactive.get("elements") or []
lines: List[str] = []
for e in elements:
text_obj = e.get("text") if isinstance(e, dict) else None
if isinstance(text_obj, dict):
content = str(text_obj.get("content") or "").strip()
if content:
lines.append(content)
return "\n".join(lines), "markdown"
if channel_type == "discord":
embeds = payload.get("embeds") or []
if not embeds:
return "", "text"
embed = embeds[0] if isinstance(embeds[0], dict) else {}
chunks: List[str] = []
title = str(embed.get("title") or "").strip()
desc = str(embed.get("description") or "").strip()
if title:
chunks.append(f"## {title}")
if desc:
chunks.append(desc)
for field in embed.get("fields") or []:
if not isinstance(field, dict):
continue
name = str(field.get("name") or "").strip()
value = str(field.get("value") or "").strip()
if name or value:
chunks.append(f"- {name}: {value}")
return "\n".join(chunks), "markdown"
return "", "text"

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

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

push_daily_digest() builds rich per-channel payloads (Telegram photo/keyboard, Discord embeds, WeCom news, Feishu interactive cards), but _payload_to_body() flattens them into a plain text/markdown body, so those channel-specific fields are never sent through Apprise. If the goal is rich channel-native messages, this needs a different send path (e.g., plugin-specific kwargs or direct API clients) or the formatters should be simplified to explicitly generate only the body that Apprise will send.

Copilot uses AI. Check for mistakes.
Comment on lines +121 to +135
rows = raw.get("figures") or []
figures: List[Figure] = []
for row in rows:
if not isinstance(row, dict):
continue
figures.append(
Figure(
url=str(row.get("url") or "").strip(),
caption=str(row.get("caption") or "").strip(),
page=int(row.get("page") or 0),
width=int(row.get("width") or 0),
height=int(row.get("height") or 0),
index=int(row.get("index") or 0),
)
)

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

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

When reading cached figures, entries with an empty url are still converted into Figure objects and may be returned to callers. This can propagate invalid figures into identify_main_figure() and downstream payloads if the cache file is corrupted/partially written. Consider skipping cached rows where url is empty (mirroring _parse_figures() behavior).

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +206
main_figure = item.get("main_figure") or {}
if isinstance(main_figure, dict):
fig_url = str(main_figure.get("url") or "").strip()
if fig_url:
try:
fe.enclosure(url=fig_url, length=0, type="image/jpeg")
except Exception:
pass

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

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

RSS enclosure type is hard-coded to image/jpeg, but main_figure.url may frequently be PNG/WebP/etc (and the tests add a .png URL). Consider inferring the MIME type from the URL extension (or storing it in main_figure) to avoid incorrect enclosure metadata for feed readers.

Copilot uses AI. Check for mistakes.
Comment thread config/push_channels.yaml
#
# Examples (uncomment and fill in credentials):

channels: []

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

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

The template uses channels: [], which makes the YAML invalid if a user simply uncomments any of the example - url: ... entries (flow-style list cannot be extended with block-style items). Consider using channels: (empty block list) instead, or add a clear instruction to remove [] before uncommenting examples.

Suggested change
channels: []
channels:

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +46
for q in report.get("queries") or []:
for item in q.get("top_items") or []:
key = item.get("title") or id(item)
if key not in seen:
seen.add(key)
papers.append(item)

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

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

Deduplicating papers by title alone can drop distinct papers that share the same title (common for arXiv revisions / workshop variants), and it also differs from other dedupe logic in the repo that considers URLs. Consider using a more stable key like url (preferred) or url|title when available, falling back to id(item) only if neither exists.

Copilot uses AI. Check for mistakes.
papers: List[Dict[str, Any]] = []
for q in report.get("queries") or []:
for item in q.get("top_items") or []:
key = item.get("title") or id(item)

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

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

_collect_papers_from_report() deduplicates using title (or id(item)), which can incorrectly drop distinct papers that share a title. Since feed entries use url/external_url elsewhere, consider deduping by url (or url|title) when present to avoid missing items in RSS/Atom output.

Suggested change
key = item.get("title") or id(item)
url = item.get("url") or item.get("external_url")
title = item.get("title")
if url:
key = ("url", url)
elif title:
key = ("title", title)
else:
key = ("id", id(item))

Copilot uses AI. Check for mistakes.
@jerry609

jerry609 commented Mar 3, 2026

Copy link
Copy Markdown
Owner Author

Follow-up pushes added:

  • e6b4053 (#191): Telegram command webhook + subscription persistence + route tests
  • 344041e (#191): apprise retry/backoff for transient channel failures + tests
  • 32277cc (#193): HF source trending modes/cache/metrics + arXiv-id dedupe + docs/tests

Issue updates already synced:

@jerry609

jerry609 commented Mar 3, 2026

Copy link
Copy Markdown
Owner Author

Follow-up batch pushed to this PR branch:

  • 10b1230 (#191): idempotency + normalized channel error-code mapping
  • 63b9964 (#191): channel mock E2E and failure injection tests
  • fe1174c (#192): audit script + ops runbook + regression test

Issue progress:

jerry609 added a commit that referenced this pull request Mar 3, 2026
* feat(push): implement Epic #179 — daily push optimization

Multi-channel push infrastructure with structured digest cards:

- #187: Integrate HF upvotes into Judge scoring prompt
- #180: Add digest_card extraction (highlight/method/finding/tags)
  with new LLM prompt, email template rendering, markdown output
- #181: MinerU Cloud API client for PDF figure extraction
- #182: Apprise multi-channel push layer with YAML config
- #183-185: Channel formatters (Telegram MarkdownV2, Discord Rich
  Embed, WeCom markdown+news, Feishu/Lark interactive card)
- #186: RSS 2.0 + Atom feed endpoints (/api/feed/daily.xml, .atom)

Also creates follow-up issues #190 (Push Channel Settings UI)
and #191 (Dashboard Digest Card Enhancement).

56 new tests, all passing.

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

* feat: integrate MinerU figure extraction into CLI and ARQ worker daily-paper workflow

Refs #179

* fix(daily-push): enable MinerU flags in ARQ cron payload (refs #181)

* feat(push): wire channel formatters into apprise digest delivery (refs #182 #183 #184 #185)

* feat(feed): add keyword RSS, feed cache, and figure enclosure support (refs #186)

* feat(mineru): add extraction result cache for daily push figure pipeline (refs #181)

* feat(push): add telegram bot command subscription endpoint (refs #191)

* fix(push): add retry/backoff for channel delivery failures (refs #191)

* feat(hf): add trending modes, cache, and arxiv identity dedupe (refs #193)

* feat(push): add idempotency and error-code mapping for channel delivery (refs #191)

* test(push): add mock channel e2e and failure-injection coverage (refs #191)

* docs(push): add audit script and ops runbook for acceptance closure (refs #192)

* docs: update README for AgentSwarm execution transition

* docs: archive stale docs and rename AgentSwarm todo

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
jerry609 added a commit that referenced this pull request Mar 3, 2026
* feat(push): implement Epic #179 — daily push optimization

Multi-channel push infrastructure with structured digest cards:

- #187: Integrate HF upvotes into Judge scoring prompt
- #180: Add digest_card extraction (highlight/method/finding/tags)
  with new LLM prompt, email template rendering, markdown output
- #181: MinerU Cloud API client for PDF figure extraction
- #182: Apprise multi-channel push layer with YAML config
- #183-185: Channel formatters (Telegram MarkdownV2, Discord Rich
  Embed, WeCom markdown+news, Feishu/Lark interactive card)
- #186: RSS 2.0 + Atom feed endpoints (/api/feed/daily.xml, .atom)

Also creates follow-up issues #190 (Push Channel Settings UI)
and #191 (Dashboard Digest Card Enhancement).

56 new tests, all passing.

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

* feat: integrate MinerU figure extraction into CLI and ARQ worker daily-paper workflow

Refs #179

* fix(daily-push): enable MinerU flags in ARQ cron payload (refs #181)

* feat(push): wire channel formatters into apprise digest delivery (refs #182 #183 #184 #185)

* feat(feed): add keyword RSS, feed cache, and figure enclosure support (refs #186)

* feat(mineru): add extraction result cache for daily push figure pipeline (refs #181)

* feat(push): add telegram bot command subscription endpoint (refs #191)

* fix(push): add retry/backoff for channel delivery failures (refs #191)

* feat(hf): add trending modes, cache, and arxiv identity dedupe (refs #193)

* feat(push): add idempotency and error-code mapping for channel delivery (refs #191)

* test(push): add mock channel e2e and failure-injection coverage (refs #191)

* docs(push): add audit script and ops runbook for acceptance closure (refs #192)

* docs: update README for AgentSwarm execution transition

* docs: archive stale docs and rename AgentSwarm todo

* docs: add AgentSwarm design proposal + web Playwright e2e setup

- Add docs/proposals/agentswarm-design.md covering multi-agent
  orchestration architecture, adapter pattern, session management,
  skills ecosystem, and OpenCode/OpenClaw/oMo integration (refs #197, #214)
- Add Playwright e2e test scaffolding for web dashboard (smoke,
  papers, research specs)
- Update web/.gitignore for Playwright artifacts

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
jerry609 added a commit that referenced this pull request Mar 4, 2026
* Feat/daily push epic 179 (#218)

* feat(push): implement Epic #179 — daily push optimization

Multi-channel push infrastructure with structured digest cards:

- #187: Integrate HF upvotes into Judge scoring prompt
- #180: Add digest_card extraction (highlight/method/finding/tags)
  with new LLM prompt, email template rendering, markdown output
- #181: MinerU Cloud API client for PDF figure extraction
- #182: Apprise multi-channel push layer with YAML config
- #183-185: Channel formatters (Telegram MarkdownV2, Discord Rich
  Embed, WeCom markdown+news, Feishu/Lark interactive card)
- #186: RSS 2.0 + Atom feed endpoints (/api/feed/daily.xml, .atom)

Also creates follow-up issues #190 (Push Channel Settings UI)
and #191 (Dashboard Digest Card Enhancement).

56 new tests, all passing.

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

* feat: integrate MinerU figure extraction into CLI and ARQ worker daily-paper workflow

Refs #179

* fix(daily-push): enable MinerU flags in ARQ cron payload (refs #181)

* feat(push): wire channel formatters into apprise digest delivery (refs #182 #183 #184 #185)

* feat(feed): add keyword RSS, feed cache, and figure enclosure support (refs #186)

* feat(mineru): add extraction result cache for daily push figure pipeline (refs #181)

* feat(push): add telegram bot command subscription endpoint (refs #191)

* fix(push): add retry/backoff for channel delivery failures (refs #191)

* feat(hf): add trending modes, cache, and arxiv identity dedupe (refs #193)

* feat(push): add idempotency and error-code mapping for channel delivery (refs #191)

* test(push): add mock channel e2e and failure-injection coverage (refs #191)

* docs(push): add audit script and ops runbook for acceptance closure (refs #192)

* docs: update README for AgentSwarm execution transition

* docs: archive stale docs and rename AgentSwarm todo

* docs: add AgentSwarm design proposal + web Playwright e2e setup

- Add docs/proposals/agentswarm-design.md covering multi-agent
  orchestration architecture, adapter pattern, session management,
  skills ecosystem, and OpenCode/OpenClaw/oMo integration (refs #197, #214)
- Add Playwright e2e test scaffolding for web dashboard (smoke,
  papers, research specs)
- Update web/.gitignore for Playwright artifacts

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(studio): fix context pack generation, duplicate import UX, and CLI chat errors (#223)

* fix(studio): fix context pack generation, duplicate import UX, and CLI chat errors

- 支持内联标题/摘要,以绕过 PaperInputRouter 获取 studio 论文 ID

- 使上下文包生成 SSE 流期间的数据库持久化操作不再致命

- 从库中导入重复论文时显示错误消息

- 将左侧文件面板宽度缩小至约 18%(默认宽度);移除默认模板文件

- 为 Claude CLI 添加 --verbose 标志,以兼容 stream-json 输出

Related to #222

* fix(studio): fix context pack generation, duplicate import UX, and CLI chat errors
- 修改Gemini AI review代码问题

* fix(studio): fix context pack generation, duplicate import UX, and CLI chat errors
- 修改Gemini AI review代码问题

---------

Co-authored-by: Jerry Zhang <1772030600@qq.com>

* fix(web): correct package.json scripts JSON syntax

* chore(gitignore): block nested .env secrets and keep examples

* docs(env): add MinerU key and figure extraction env templates

* refactor(mineru): standardize on v4 task API for daily push (#229)

* fix(mineru): support v4 async task extraction flow

* refactor(mineru): standardize on v4 task API for daily push (#179)

* feat(push): render MinerU main figure in email via inline fallback

* docs(readme): update email push demo screenshot

* docs(readme): refresh Phase 6 daily push descriptions

* fix(security): harden runbook allowed-dir path validation

* fix(review): resolve security and reliability findings from AI review

* fix(codeql): harden studio chat project_dir normalization

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Boyu liu <oor2020@163.com>
jerry609 added a commit that referenced this pull request Mar 4, 2026
* Feat/daily push epic 179 (#218)

* feat(push): implement Epic #179 — daily push optimization

Multi-channel push infrastructure with structured digest cards:

- #187: Integrate HF upvotes into Judge scoring prompt
- #180: Add digest_card extraction (highlight/method/finding/tags)
  with new LLM prompt, email template rendering, markdown output
- #181: MinerU Cloud API client for PDF figure extraction
- #182: Apprise multi-channel push layer with YAML config
- #183-185: Channel formatters (Telegram MarkdownV2, Discord Rich
  Embed, WeCom markdown+news, Feishu/Lark interactive card)
- #186: RSS 2.0 + Atom feed endpoints (/api/feed/daily.xml, .atom)

Also creates follow-up issues #190 (Push Channel Settings UI)
and #191 (Dashboard Digest Card Enhancement).

56 new tests, all passing.

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

* feat: integrate MinerU figure extraction into CLI and ARQ worker daily-paper workflow

Refs #179

* fix(daily-push): enable MinerU flags in ARQ cron payload (refs #181)

* feat(push): wire channel formatters into apprise digest delivery (refs #182 #183 #184 #185)

* feat(feed): add keyword RSS, feed cache, and figure enclosure support (refs #186)

* feat(mineru): add extraction result cache for daily push figure pipeline (refs #181)

* feat(push): add telegram bot command subscription endpoint (refs #191)

* fix(push): add retry/backoff for channel delivery failures (refs #191)

* feat(hf): add trending modes, cache, and arxiv identity dedupe (refs #193)

* feat(push): add idempotency and error-code mapping for channel delivery (refs #191)

* test(push): add mock channel e2e and failure-injection coverage (refs #191)

* docs(push): add audit script and ops runbook for acceptance closure (refs #192)

* docs: update README for AgentSwarm execution transition

* docs: archive stale docs and rename AgentSwarm todo

* docs: add AgentSwarm design proposal + web Playwright e2e setup

- Add docs/proposals/agentswarm-design.md covering multi-agent
  orchestration architecture, adapter pattern, session management,
  skills ecosystem, and OpenCode/OpenClaw/oMo integration (refs #197, #214)
- Add Playwright e2e test scaffolding for web dashboard (smoke,
  papers, research specs)
- Update web/.gitignore for Playwright artifacts

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(studio): fix context pack generation, duplicate import UX, and CLI chat errors (#223)

* fix(studio): fix context pack generation, duplicate import UX, and CLI chat errors

- 支持内联标题/摘要,以绕过 PaperInputRouter 获取 studio 论文 ID

- 使上下文包生成 SSE 流期间的数据库持久化操作不再致命

- 从库中导入重复论文时显示错误消息

- 将左侧文件面板宽度缩小至约 18%(默认宽度);移除默认模板文件

- 为 Claude CLI 添加 --verbose 标志,以兼容 stream-json 输出

Related to #222

* fix(studio): fix context pack generation, duplicate import UX, and CLI chat errors
- 修改Gemini AI review代码问题

* fix(studio): fix context pack generation, duplicate import UX, and CLI chat errors
- 修改Gemini AI review代码问题

---------

Co-authored-by: Jerry Zhang <1772030600@qq.com>

* fix(web): correct package.json scripts JSON syntax

* chore(gitignore): block nested .env secrets and keep examples

* docs(env): add MinerU key and figure extraction env templates

* refactor(mineru): standardize on v4 task API for daily push (#229)

* fix(mineru): support v4 async task extraction flow

* refactor(mineru): standardize on v4 task API for daily push (#179)

* feat(push): render MinerU main figure in email via inline fallback

* docs(readme): update email push demo screenshot

* docs(readme): refresh Phase 6 daily push descriptions

* fix(security): harden runbook allowed-dir path validation

* fix(review): resolve security and reliability findings from AI review

* fix(codeql): harden studio chat project_dir normalization

* docs(readme): restructure README for professional presentation

- Add centered hero header with badges (CI, Roadmap, License, Python, Next.js)
- Replace verbose 18-row feature table with categorized bullet lists
- Move Roadmap Phase 1-6 content to pinned issue #232
- Move module maturity matrix to Roadmap #232
- Consolidate screenshots into collapsible sections
- Remove inline API endpoint table (40+ rows) and directory tree
- Add Contributing section with Roadmap link
- Streamline Getting Started with collapsible config details
- Add repo topics for GitHub discoverability

References: vLLM, Docling, PaperQA2, R2R README patterns

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Boyu liu <oor2020@163.com>
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