Feat/datainfra refactor tracker#76
Conversation
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughThis PR introduces a comprehensive set of features for LLM provider management, long-running pipeline session handling with approval workflows, unified topic search, and enhanced research/daily paper capabilities. It includes multiple database migrations, new REST API endpoints for managing model endpoints, persistent session and LLM usage tracking stores, refined frontend components for deadline radar and approval queues, and streaming-based daily paper workflows with resume support. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Client as Client Browser
participant API as FastAPI Server
participant SessionStore as PipelineSessionStore
participant SearchService as UnifiedTopicSearch
participant LLMService as LLMService
participant EnrichmentPipeline as EnrichmentPipeline
participant DB as Database
User->>Client: Initiate daily paper search
Client->>API: POST /daily with session_id, resume flag
alt Session exists and resuming
API->>SessionStore: get_session(session_id)
SessionStore->>DB: query pipeline_sessions
DB-->>SessionStore: return saved state & checkpoint
SessionStore-->>API: return session data
API->>SearchService: skip search, restore cached results
else New session or no resume
API->>SessionStore: start_session(workflow, payload)
SessionStore->>DB: create pipeline_sessions row
DB-->>SessionStore: return session_id
SessionStore-->>API: return session data
API->>SearchService: run_unified_topic_search(queries)
SearchService-->>API: return search results
API->>SessionStore: save_checkpoint(session_id, "search_done", state)
SessionStore->>DB: update checkpoint & state_json
end
API->>EnrichmentPipeline: process papers
EnrichmentPipeline->>LLMService: summary, relevance, judge
LLMService-->>EnrichmentPipeline: enriched papers with judge
EnrichmentPipeline-->>API: return filtered papers
API->>SessionStore: save_checkpoint(session_id, "enriched", state)
SessionStore->>DB: update state
alt Approval required
API-->>Client: SSE stream with "approval_pending"
Client-->>User: Display approval queue
User->>Client: Approve or reject
Client->>API: POST /sessions/{id}/approve
API->>SessionStore: save_result & update_status
SessionStore->>DB: mark approved, persist result
API-->>Client: SSE "result" event
else Auto-finalize
API->>SessionStore: save_result(session_id, final_result)
SessionStore->>DB: update result_json, status
API-->>Client: SSE "result" event
end
Client-->>User: Display daily report
sequenceDiagram
actor User
participant Client as Settings UI
participant API as FastAPI Server
participant EndpointStore as ModelEndpointStore
participant ModelRouter as ModelRouter
participant Provider as LLM Provider
participant DB as Database
User->>Client: Open Settings / Model Providers
Client->>API: GET /api/model-endpoints
API->>EndpointStore: list_endpoints()
EndpointStore->>DB: query model_endpoints (masked secrets)
DB-->>EndpointStore: return endpoints
EndpointStore-->>API: return list
API-->>Client: return ModelEndpointListResponse
Client-->>User: Render endpoint list + form
User->>Client: Create or update endpoint
Client->>API: POST/PATCH /api/model-endpoints/{id}
API->>EndpointStore: upsert_endpoint(payload)
EndpointStore->>DB: insert/update model_endpoints row
DB-->>EndpointStore: return updated row
EndpointStore-->>API: return endpoint
API-->>Client: return response
Client-->>User: Show success/updated endpoint
User->>Client: Click "Test" button on endpoint
Client->>API: POST /api/model-endpoints/{id}/test with remote=true
API->>EndpointStore: get_endpoint(endpoint_id, include_secrets=true)
EndpointStore->>DB: query with api_key_value
DB-->>EndpointStore: return endpoint + secret
API->>ModelRouter: from_registry() load provider
ModelRouter->>DB: query model_endpoints for config
API->>Provider: invoke remote health check
Provider-->>API: return provider info & status
API-->>Client: return EndpointTestResponse (ok, provider metadata)
Client-->>User: Show test result & provider info
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
Summary of ChangesHello @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 significantly upgrades the application's data infrastructure and user interface, particularly focusing on LLM integration and research workflows. It introduces new database schemas for managing LLM providers, tracking usage, and persisting pipeline states. The LLM service is refactored for better extensibility and cost management. The user interface for research has been overhauled with a more organized layout, new features like a personalized paper feed, and a conference deadline tracker, alongside a dedicated UI for managing LLM model configurations. Highlights
🧠 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
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
Pull request overview
This PR refactors “tracker”/research UX and backend infra by introducing a split research workspace (rail/list/detail), adding model endpoint management + LLM usage tracking, normalizing SSE events with canonical event kinds, and expanding research APIs (track feed, saved-by-track, deadline radar) to support the new UI.
Changes:
- Add model endpoint registry + CRUD/test/activate APIs, plus LLM usage persistence/aggregation and dashboard UI updates.
- Introduce research workspace + tabs (Search/Feed/Saved/Memory), source toggles for search, and an approvals side panel.
- Add pipeline session checkpointing/resume support and unify topic search workflow integration (CLI + worker).
Reviewed changes
Copilot reviewed 75 out of 75 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| web/src/lib/types.ts | Replace old LLM usage record types with usage summary + deadline radar types. |
| web/src/lib/sse.ts | Add canonical event field and normalize event kinds for UI handling. |
| web/src/lib/api.ts | Fetch LLM usage summary + deadline radar from backend (with local fallback). |
| web/src/app/dashboard/page.tsx + components/dashboard/* | Render usage summary + deadline radar on dashboard. |
| web/src/app/research/page.tsx + components/research/* | New split workspace, tabs, feed/saved/memory panels, source toggle chips. |
| src/paperbot/api/routes/* | Add model endpoints + deadline radar + track feed routes and context sources wiring. |
| src/paperbot/infrastructure/stores/* | Add PipelineSessionStore, ModelEndpointStore, LLMUsageStore, track feed query, judge score lookup. |
| tests/unit/* | Add/extend unit tests for new stores, routes, SSE envelope, resume/approval flows. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export async function fetchStats(): Promise<Stats> { | ||
| // TODO: Replace with real API call | ||
| // const res = await fetch(`${API_BASE_URL}/stats`) | ||
| // return res.json() | ||
| try { | ||
| const usage = await fetchLLMUsage() | ||
| const tokenCount = usage.totals.total_tokens | ||
| const prettyTokens = tokenCount >= 1000 ? `${Math.round(tokenCount / 1000)}k` : `${tokenCount}` | ||
| return { |
There was a problem hiding this comment.
fetchStats() calls fetchLLMUsage() internally, but the dashboard page also calls fetchLLMUsage() separately. This results in two identical network requests per dashboard render. Consider either (a) removing the internal fetchLLMUsage() call from fetchStats() and letting the caller format llm_usage, or (b) changing fetchStats() to accept an optional pre-fetched usage summary to avoid duplicate fetches.
There was a problem hiding this comment.
Code Review
This is a substantial pull request that introduces significant features and refactors core parts of the application. The introduction of a dynamic model provider gateway, LLM usage tracking, and a resumable pipeline for daily paper generation are excellent additions that greatly enhance the system's capabilities and robustness. The refactoring of workflows into modular enrichment pipelines is a particularly strong design choice.
However, there is a critical security vulnerability in how API keys are stored for the new model provider gateway. Storing secrets in plaintext in the database is a major risk and must be addressed. I've also included a couple of medium-severity comments regarding database portability and maintainability. Overall, this is a very impressive set of changes, but the security issue is paramount.
| if "api_key_value" not in _columns("model_endpoints"): | ||
| op.add_column( | ||
| "model_endpoints", sa.Column("api_key_value", sa.String(length=512), nullable=True) | ||
| ) |
There was a problem hiding this comment.
Storing API keys, even if nullable, directly in the database in plaintext is a critical security vulnerability. If the database is compromised, all these secrets will be exposed.
Secrets should be stored in a dedicated secret manager (like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault) and accessed at runtime. At a minimum, these values should be encrypted at the application layer before being stored in the database, and decrypted only when needed. Please reconsider this approach to secret management.
| op.execute( | ||
| sa.text( | ||
| """ | ||
| UPDATE papers | ||
| SET title_hash = lower(hex(randomblob(16))) | ||
| WHERE title_hash IS NULL OR title_hash = '' | ||
| """ | ||
| ) | ||
| ) |
There was a problem hiding this comment.
The use of randomblob is specific to SQLite and will cause this migration to fail on other database systems like PostgreSQL. For better database portability, consider using a more generic SQLAlchemy function or dialect-specific conditional execution.
For example, you could use sqlalchemy.func.random() and then format it as a hex string within your application logic if the format is important, or use different functions based on the database dialect.
| def _estimate_cost_usd( | ||
| *, provider_name: str, model_name: str, prompt_tokens: int, completion_tokens: int | ||
| ) -> float: | ||
| provider = (provider_name or "").lower() | ||
| model = (model_name or "").lower() | ||
|
|
||
| in_price = 0.0 | ||
| out_price = 0.0 | ||
|
|
||
| if provider == "openai" and "gpt-4o-mini" in model: | ||
| in_price, out_price = 0.15, 0.60 | ||
| elif provider == "openai" and "gpt-4o" in model: | ||
| in_price, out_price = 2.50, 10.00 | ||
| elif provider == "anthropic" and "claude-3-5-sonnet" in model: | ||
| in_price, out_price = 3.00, 15.00 | ||
| elif provider == "deepseek": | ||
| in_price, out_price = 0.55, 2.19 | ||
| elif provider == "ollama": | ||
| in_price, out_price = 0.0, 0.0 | ||
|
|
||
| return ((prompt_tokens * in_price) + (completion_tokens * out_price)) / 1_000_000 |
There was a problem hiding this comment.
Hardcoding model pricing in the _estimate_cost_usd function makes it difficult to maintain. As pricing changes or new models are added, this code will need to be constantly updated.
Consider moving this pricing information into a configuration file or, even better, storing it as part of the model's configuration in the model_endpoints table. This would make the system more flexible and easier to manage.
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes