Rukon
Back to Project Overview
Full Case StudyWeb Apps / Software

ResearchForge

ResearchForge: AI research paper assistant

ResearchForge mark
Role
Sole Developer

End-to-End Planning, Dual-Service Architecture, AI Pipeline & Automated Tests

Affiliation / Client
University of Cyberjaya, BIT4543 Artificial Intelligence
Timeline
2026
Documentation Level
18 Engineering Sections

Operational Workflow & State Machine

End-to-End Pipeline
  1. 01Sign in
  2. 02Upload a PDF
  3. 03Validate and extract
  4. 04Analyse
  5. 05Read and keep the result
01

Overview

ResearchForge reads an academic PDF and produces three things: a structured summary covering the research problem, methodology, key findings and conclusion; a research-gap analysis where each gap is shown alongside the wording in the paper that supports calling it a gap; and a literature review of the prior work the paper itself discusses.

It is deployed on its own subdomain as a single Vercel project running two services behind one origin, with email and Google sign-in and a research library private to each account.

The public ResearchForge landing page. The headline reads Understand research faster with AI, with Get Started and See How It Works buttons, three promises (private to your account, grounded in your paper, says when it cannot tell) and an illustrative panel of summary, research gaps and literature review.
The public landing page at researchforge.rukon.dev. Everything past it requires an account.

Every screenshot on this page was captured from the live deployment on 25 September 2026, using two short demonstration papers written for the purpose and labelled fictional on their first line. The analyses shown are the model's real output for those papers. The account name in the header is blurred.

02

Problem

The failure mode of an AI summarising tool is not that it produces nothing. It is that it produces something plausible for a section the source never covered: an invented methodology for a position paper, a confidently stated gap with nothing behind it.

For a tool meant to help with research, that failure is worse than no answer at all, because it is indistinguishable from a correct one unless the reader already knows the paper.

03

Context

ResearchForge was developed as part of BIT4543 Artificial Intelligence at the University of Cyberjaya. It is a university project, but it was not built as a classroom prototype: it is deployed on its own subdomain, has accounts and stored user data, and the reported results come from running the deployed system rather than from estimates.

It began as a stateless tool with no accounts, which kept the first version defensible while there was nothing stored to protect. Adding a library meant that stopped being true, so authentication, per-user ownership and database-level access control went in together rather than being retrofitted around a feature that had already shipped.

04

My Work

I took this from an empty repository to a deployed system that is still maintained, and the order matters: the first commit is the project structure, the working rules, a project plan with milestones, risks and numbered decisions, the environment contract and the documentation skeleton. Features came after that, not before it.

Planning and specification. Milestones were tracked and closed in the repository (M1 complete, risk R1 resolved), and technical choices were recorded as numbered decisions rather than made silently, including one that was locked, revisited and re-locked when a better option was found. Requirements were derived from the objectives and refined during development as defects exposed expectations nobody had written down.

Architecture and design. Two services in one Vercel project behind a single origin, a provider interface that keeps vendor SDKs out of the analysis code, a storage-independent repository, the database schema and its migrations, and the decision to enforce ownership in PostgreSQL rather than in application code.

Implementation, both sides. The Python and FastAPI backend, its API endpoints and error semantics, PDF ingestion and text normalisation, the three-pass analysis pipeline, prompt and structured-output handling, schema validation, the content-hash cache, authentication and owner configuration. On the frontend, the Next.js application: the landing page, sign-in and account flows, the analysis workspace, the research library, paper detail and cross-paper review, themes, and the responsive layout.

Then the parts that only exist once something is real: production configuration and environment management, deployment, debugging live failures, refactoring, the test suite, and maintenance after the system was already working.

05

How it works

A signed-in user drops a PDF on the dashboard. The browser checks type, emptiness and size, then posts it to POST /api/analyze. The backend validates it again, extracts and cleans the text, checks whether that exact text has been analysed before, and if not makes three structured model calls: summary, research gaps, literature review. The result comes back as one validated JSON response and renders in tabs.

Nothing is stored until the user chooses Save to library. Saved papers can then be selected together in the Workspace to produce one literature review across several of them. That second step reads the stored analyses rather than re-reading the PDFs.

A new analysis takes one to three minutes; the interface says so and lists the steps the request performs, without pretending to know which one is running, because the backend does not report progress.

Workflow diagram. Row one: sign in, upload a PDF, analyse, read the result, save to library. Beneath Analyse, three outcomes: rejected upload (422 or 413), same text seen before (cache hit, no model call) and provider problem (429, 502, 503). Row two: My Papers, select two or more, cross-paper review, read and copy. An owner-only settings box chooses the primary AI provider.
The verified user workflow, traced from the Next.js routes and API calls. Search inside papers, notes, citation export and collaboration are not in the product.
The dashboard while a paper is being analysed: an Analysing paper progress bar with elapsed time, a note that analysis normally takes one to three minutes and that the backend does not report which step is running, and a list of five steps from uploading to preparing the literature review.
Analysis in progress. The steps are listed but none is marked complete, because the backend does not report which one is running.
06

Engineering Approach

The founding principle is that every claim must be grounded in the uploaded paper, and it is enforced in four layers rather than requested once in a prompt. The prompt requires it, and the prompts live in version-controlled files rather than scattered through the code. The response schema carries explicit insufficient-evidence fields, giving the model a way to decline that is as easy as complying. Every reply is validated against that schema on return. And output that fails validation is discarded rather than repaired, which is the rule that makes the other three mean anything: a partially valid analysis that the system patched up would be an invented analysis.

Structured output is the mechanism, not a convenience. Each response model is converted to a JSON Schema and handed to the model as the required output format, with additional properties forbidden. Every reply is validated on return, and a truncated or malformed answer is refused outright rather than partially rendered.

The three analyses run as three separate model calls. They are different tasks with different evidence rules, so separating them means a failure in one does not corrupt the others, and each can be improved on its own. They run sequentially on purpose: running them in parallel would multiply the peak rate-limit burden for a latency win that does not matter on a single upload.

The Research Gaps tab of a saved paper. Limitations stated by the authors are listed, followed by identified gap 1: no individual-level randomisation or clustering-adjusted analysis. Under it, why it matters, and an evidence block quoting the paper's own sentence about tutorial-section assignment.
Each gap carries the paper's own wording as evidence. Evidence is a required field in the response schema, so a gap returned without it fails validation.
The Literature Review tab. A blue scope notice says the review covers only prior work discussed within the uploaded paper, which it identifies as a fictional demonstration paper, and that the cited works were not consulted. Below are major themes and relevant findings attributed to the authors the paper cites.
The single-paper review states its own scope: only the prior work this paper discusses, with the cited works not consulted. The model also noticed, unprompted, that the paper declares itself fictional.
07

Architecture

One Vercel project runs two services. A Next.js frontend serves everything except the API, and a FastAPI backend serves /health and /api/*, with routing declared in the project configuration. Because both share one origin, the frontend calls the API with a relative path, which is what makes the custom domain, the .vercel.app domain and every preview URL work from the same build.

Generation sits behind a provider interface. The analysis service depends on that interface and never on a vendor SDK, each vendor's SDK is imported only inside its own provider module, and the concrete provider is built by a factory with a local import so adding one never forces every caller to import every SDK. An earlier version used Google Gemini; it is a historical provider only and produces none of the current analyses.

Vendor errors are wrapped in project-owned exception types, with a missing API key separated out from the rest because it is a deployment problem rather than a user's fault and maps to a different status code. Status codes are chosen so the frontend can tell the cases apart without parsing message text: too large, unusable PDF, unusable model reply, no credentials configured. Nothing expected returns a 500.

System architecture diagram. The browser reaches one Vercel project at researchforge.rukon.dev, where vercel.json routes /api and /health to a FastAPI backend and everything else to a Next.js 16 frontend. The backend calls Supabase Auth to verify tokens, Supabase Postgres with the user's own token so Row Level Security applies, and the Anthropic and Groq APIs. Dashed boxes mark Jina embeddings and pgvector retrieval as planned scaffolding that nothing calls, and Google Gemini as retired.
System architecture. Solid boxes run in production; dashed boxes are scaffolding nothing calls; Gemini is retired and kept only for historical rows.
Data flow diagram of one analysis: PDF bytes, validation, extraction and cleaning with pypdf, a SHA-256 content hash checked against the analysis cache, a 400,000-character decision between whole-document context and map-reduce digests, three structured model calls through the routed provider to Anthropic or Groq, Pydantic validation, and the response to the browser. The cache is written only on success, and the library is written only when the user saves.
One analysis, end to end. There is no retrieval step: the whole paper is the context. The PDF itself is never stored.
08

Providers and fallback

The owner picks which of the two providers is primary, and the other automatically becomes the fallback. Groq running qwen3.6-27b is the configured primary and Anthropic claude-opus-5 the fallback, as the live owner settings showed on 25 September 2026. There is no per-user model picker: the choice is an operational one, made once, and the interface does not pretend otherwise.

Availability is enforced when the router is constructed rather than checked at call time, so a provider that has been switched off is never built and no code path can reach it.

Fallback is deliberately narrow. It fires once per analysis, and only for a rate limit or a temporary provider failure, checked against a whitelist so a new error type does not become retryable by default. It does not fire for a malformed PDF, a schema validation failure or a missing key, because those fail identically on either vendor and retrying them just spends a second vendor's quota to produce the same error more slowly.

One switch per analysis, not per call. An analysis makes at least three calls, and allowing each to fail over independently would let different sections be written by different models, which makes the recorded model identity meaningless.

Every stored analysis records which provider and model actually produced it, whether the fallback was used, and how long the call took. Without that, a result whose quality looks off has no explanation attached to it, and 'which model wrote this' becomes unanswerable a week later.

The screenshots below show that working rather than asserted. Both demonstration papers were analysed with Groq as primary, and both records say Claude produced them with the fallback used. That is the behaviour the evaluation predicted, since the primary's free tier is smaller than one call over a whole paper.

Provider routing flowchart. The router is built per request from the owner's primary and the enabled providers, then calls the primary. On success the primary is recorded. On error, a whitelist decides whether it is retryable: rate limits and transient errors are, credential and response errors are not. A retryable error switches to the fallback, which stays in place for the rest of the analysis. If the fallback also fails, one error names both vendors and there is no third attempt.
Routing and fallback, as implemented in src/rag/llm/router.py.
The recent-analysis card on the dashboard for the second demonstration paper, with badges reading Claude, Fallback used and claude-opus-5, a Save to library button, and the summary tab open.
Provenance on a real result: Groq was primary, Claude wrote it, and the record says so.
The owner-only AI configuration panel in Settings. Primary AI model is set to Groq Qwen 3.6 27B, the fallback shows Claude (automatic), and an availability list shows Claude claude-opus-5 and Groq qwen/qwen3.6-27b both enabled, with Groq marked Primary.
The owner-only control. Choosing a primary makes the other provider the fallback; switching one off removes it from routing entirely. API keys are never stored or shown here.
09

Accounts and data ownership

Authentication is Supabase Auth: email and password with sign-up, sign-in, forgot-password and reset flows, plus Google sign-in completing at a dedicated callback route. Passwords never reach the ResearchForge database.

Every paper, analysis and review belongs to exactly one account. Application-level ownership checks alone proved insufficient for reliable isolation in production, so the guarantee was moved into the database: PostgreSQL Row Level Security enforces per-user access at the data layer.

The mechanism is which credential the backend uses to reach the database. Requests are made as the signed-in user, so PostgreSQL resolves the authenticated identity and applies every policy automatically. A forgotten ownership filter then returns nothing rather than everything, which is the opposite of how that mistake usually fails. Ownership columns default to the authenticated identity, so a row cannot be inserted without an owner even if the application code omits it.

Records created before authentication existed remain unowned. They were deliberately neither deleted nor assigned to an owner that could not be established, and they are unreachable because a null owner never matches an authenticated identity.

Re-uploading a paper that has already been analysed reuses the stored analysis instead of paying for it again. Identity is a content hash of the extracted text, not the filename, so the same paper saved under a different name still matches.

Database relationship diagram. Supabase auth.users owns papers, analyses and literature_reviews, each with user_id defaulting to auth.uid() and a Row Level Security policy. Analyses reference papers with cascade delete; literature_review_papers links reviews to papers and restricts deleting a reviewed paper. An analysis_cache table is keyed by content hash and analysis version. system_settings and app_owners hold the owner's AI configuration. A dashed chunks table with a 1024-dimension pgvector column is marked planned: nothing writes or reads it.
The schema built by six additive migrations. Every table has RLS enabled; the chunks table is scaffolding for retrieval that was never built.
10

Library and cross-paper review

A saved paper keeps its analysis, and the library can be searched by title or filename, filtered by status and sorted. Opening one shows the same four tabs as a fresh analysis, plus the document facts that were measured rather than generated: pages, characters extracted, file size, whether it was analysed whole, and the model recorded for it.

Selecting two or more saved papers in the Workspace builds one literature review across them. It deliberately reads each paper's stored summary, findings, limitations and themes rather than re-extracting the PDFs, which keeps the combined prompt inside the context window and avoids paying for the same reading twice. The trade-off is stated in the code: the cross-paper review cannot surface anything the original analyses missed.

Every paper in a review must still exist and carry a stored analysis, or the request is refused: 404 for a paper that is missing or belongs to someone else, 422 naming any paper with no stored analysis. A review that quietly covered four of the five papers the user picked would be worse than an error, because the interface would still say five.

The Research Workspace. The library is filtered to the two demonstration papers, both ticked. A Selected papers panel lists them in order with remove buttons and a Generate literature review button.
Choosing papers for a cross-paper review. The panel refuses to continue with fewer than two.
A generated cross-paper review titled Review of 2 papers, 25 Sep 2026, based on 2 selected papers. It lists the papers included, a scope notice saying the review covers only the two supplied papers, both labelled fictional, and major themes that attribute each point to the paper it came from.
The cross-paper review names the papers it was built from and attributes each theme to its source paper.
A saved paper's Paper Details tab showing filename, 3 pages, 7,393 characters extracted, 6 KB file size, analysed as a single document, model claude-opus-5, saved date, and content truncated: No.
Measured document facts, kept apart from anything a model generated.

Saved cross-paper reviews are stored and counted on the dashboard, but no screen lists or reopens them yet. The API route exists; the interface does not call it.

11

Interface

The frontend is Next.js 16 with React 19, TypeScript in strict mode and plain CSS rather than a component library. Light and dark themes follow a stored preference or the operating system, the navigation collapses to a menu on narrow screens, and empty and error states are written to say what happened, for example distinguishing an empty library from one that is not connected.

The saved paper view in the dark theme, with the Research Gaps tab open showing seven limitations stated by the authors.
Dark theme. The limitations the authors state are listed separately from the gaps the analysis identifies.
Two mobile screenshots side by side. Left: the landing page with the headline, full-width Get Started and See How It Works buttons, and three promises. Right: the sign-in screen with email and password fields, Sign In, Continue with Google, and a link to create an account.
Landing and sign-in at mobile width. Google sign-in uses the PKCE flow, so no token ever appears in a URL.
12

Document ingestion

An upload is validated twice. The browser checks type, emptiness and size; the backend re-checks the real byte count and verifies the file actually begins with a PDF signature, so a renamed file cannot get through on its content type alone.

Encrypted files get an empty-password decrypt attempt, which covers the common printing-restricted case, and are rejected otherwise rather than guessed at. A file whose extraction yields almost no text is a scanned image with no text layer; it is rejected with an explanation instead of returning an empty result.

Extracted text is cleaned conservatively: ligatures normalised, words rejoined across hyphenated line breaks, runs of spaces collapsed but never across newlines, because paragraph structure is a real signal about where sections begin. Nothing in the cleaning removes content.

Chunking is deliberately conditional. A paper that fits is sent whole, because cross-section reasoning is exactly what gap analysis depends on and chunking would destroy it. Only a genuinely oversized document takes the map step, and its boundaries are chosen at paragraph breaks where possible, falling back to sentence ends and only then to a hard offset.

13

Testing

538 passing tests: 522 backend with pytest and 16 frontend with Vitest, plus 5 live-provider tests skipped by design. No test in the suite calls a paid interface. Every provider is replaced by a fake through FastAPI's dependency-override mechanism, which is the practical reason the endpoint takes its provider as a dependency rather than constructing one.

The distribution is deliberate rather than even. Authentication is the largest module at 61 tests, on the judgement that a silent failure there costs most. Then the LLM providers at 49, every database access path at 48, rate-limit recognition and retry discipline at 42, and the provider router at 36.

Automated tests were not sufficient on their own. The isolation defect described above passed every one of them while being wrong in production, because the ownership tests modelled Row Level Security with a stub. Verifying it needed live scripts running a scenario as two different people against the deployed system. That is the argument for keeping unit, integration and live testing as separate levels: each can pass while another fails.

14

Technical Decisions

The model tier was chosen against a platform constraint rather than a benchmark. A Vercel function has a 300-second ceiling, and three sequential schema-constrained calls have to complete inside it. A deeper-reasoning model is one environment variable away with no code change.

pypdf was chosen over faster alternatives on licensing. The fastest option ships native binaries and is AGPL, which is incompatible with an MIT repository intended to be read publicly. Only one function touches the library, so swapping it later is a one-function change.

Provenance columns degrade per migration rather than all at once: a database that has not yet run a later migration loses only the fields that migration added, instead of the whole write failing. A schema change should not be able to take the feature down while it is rolling out.

15

Deployment

This did not stop at localhost. One Vercel project runs both services behind a single origin: a Next.js frontend serving everything except the API, and a FastAPI backend serving /health and /api/*, with the routing declared in the project configuration and a 300-second function ceiling the analysis has to fit inside.

Single-origin is the decision that makes the rest work. Because both services answer on the same origin, the frontend calls the API with a relative path, so the custom domain, the .vercel.app domain and every preview URL all work from one build with no per-environment base URL to get wrong.

Production configuration is its own body of work: environment and secret management across the application, the database and two model providers, a guard that refuses a publishable key in a slot that requires a private one rather than silently showing an empty library, and a health endpoint that reports whether auth and the library are actually wired rather than just that the process is up.

16

Production debugging

The most instructive defect was in data isolation, and it is instructive precisely because nothing caught it. An earlier version of the backend connected to the database with a privileged key, the kind designed to bypass Row Level Security. Every policy was present. Every policy was correctly written. Every policy was inert. The application worked, every feature behaved correctly, and the whole automated suite passed, because at the unit level nothing was wrong.

It was found by signing in as a second real account and checking whether the first account's papers were visible. The fix was to connect as the signed-in user so PostgreSQL resolves the caller identity itself. That is the episode that turned isolation from something every query has to remember into something the database enforces.

A content delivery network hid the real errors. The custom domain is served through a CDN that replaces an origin error body with a short generic message, so every failed analysis looked identical and carried no explanation. Addressing the application origin directly returned the real provider message, which is how the free-tier token limit behind the whole evaluation was identified. Without that step the central finding would have stayed invisible.

A schema change broke every library read. Queries were selecting new provenance and cache columns before the matching migration had been applied, and the database rejected the entire request rather than the missing columns, so all reads failed at once. The fix retries and degrades one migration level at a time, returning the columns that do exist instead of failing completely.

Rate limiting arrived disguised as generic server errors. Handling was rewritten to be vendor-neutral: rate limits are recognised as such, retry information is passed through, an exhausted quota is distinguished from temporary throttling, and the hidden retries inside client libraries were switched off because they multiply cost without telling the caller.

Sign-out, sign-in and same-origin routing each needed their own fix: pinning the PKCE flow and deriving every redirect from the running origin, making logout take effect promptly, and calling the backend same-origin so the custom domain and every preview URL work from one build.

17

Maintenance

Work did not stop at the first working version. After the analysis path was live, the system gained authentication and private libraries, Google sign-in, a second provider with owner-controlled availability, account settings, analysis reuse, and provenance recording, each of which meant revisiting code that already worked.

Some of that was correcting earlier decisions rather than adding to them. The provider architecture was rebuilt when the original vendor's rate limits proved unworkable, the frontend was rebuilt as a research dashboard, and provenance columns were changed to degrade per migration after the schema-skew failure rather than being left to fail as a unit.

A caching subtlety is recorded rather than quietly fixed: because the cache is keyed by content rather than by user or provider, a cached result can be returned where a fresh analysis was expected. That affected measurement twice during evaluation. Neither was a defect in the cache, which behaved as designed; both were defects in measurement, resolved by clearing the relevant rows before measuring. It is worth stating because a correctness-preserving optimisation invalidating an experiment silently is a general risk, not a one-off.

18

Outcome

A working, deployed application with accounts, a private research library and recorded provenance, that does what it says and states what it does not do. The analysis and library paths are complete and in production; retrieval over a stored corpus is written only as far as the embedding request construction, and both the interface and the documentation say so rather than implying otherwise.

Measured on the deployed system: a new analysis of a full paper takes a median of 98.8 seconds, ranging from 72.6 to 115.2 across the evaluation corpus, because three separate passes are made over the whole document. A cache hit returns in 2.7 to 3.2 seconds.

The evaluation also produced a negative result worth stating. The intended comparison between the two providers could not be completed: with the fallback removed, the configured primary refused every request, because its free tier allows 7,000 input tokens per minute and every corpus paper needed more than that in a single call. The architecture supports two providers and the fallback is verified, but the system is effectively single-provider until a primary that can accept a whole paper is configured.

What's live, and what isn't

“It exists in the repository” and “it runs in production” are different claims. This is the difference, stated rather than left to be assumed.

  • ImplementedPDF upload, validation and extractionLive. Double validation, signature check, encryption and scanned-file rejection.
  • ImplementedSummary, gap analysis, literature reviewLive. Three schema-constrained model calls, each validated on return.
  • ImplementedAccounts and authenticationLive. Supabase Auth with email and password, plus Google sign-in. The deployed /health endpoint reports auth enabled.
  • ImplementedPrivate research libraryLive and database-backed. Papers, analyses and reviews are scoped to one account by Postgres Row Level Security.
  • ImplementedProvider abstraction and automatic fallbackGroq qwen3.6-27b is the configured primary and Anthropic claude-opus-5 the fallback. Availability is enforced when the router is constructed, so a disabled provider is never built.
  • ImplementedRecorded provenanceEach analysis stores the provider and model that produced it, whether the fallback was used, and the elapsed time.
  • ImplementedAnalysis reuseA paper already analysed is matched by a content hash of its extracted text and its stored analysis reused.
  • ImplementedConditional chunking for long papersWhole-document by default; map-reduce only above a configured character threshold.
  • ImplementedCross-paper literature reviewLive. Built from the stored analyses of two or more saved papers in one model call, and saved with links to the papers it covers.
  • Built, not liveReopening saved cross-paper reviewsReviews are stored and an API route lists them, but no screen reads that route yet, so a saved review cannot be reopened from the interface.
  • Not connectedGrounded Q&A chat and exportPlanned in the project plan (F8, F10). Not built. Results can be copied section by section; there is no file export.
  • Not connectedEmbedding providerThe provider interface and request construction are written and unit-tested offline. The HTTP call is deliberately not implemented, so no embedding is ever produced.
  • Not connectedRetrieval / vector searchNot implemented. The analysis path performs no retrieval: it sends the document, not retrieved passages. This is not a RAG system.

Technical stack

Backend

Python 3.14FastAPIPydanticpydantic-settingsuvicorn

Frontend

Next.js 16 (App Router)React 19TypeScript (strict)Plain CSS

AI

Groq (qwen3.6-27b, primary)Anthropic (claude-opus-5, fallback)Schema-constrained structured outputAutomatic provider fallback

Data & auth

Supabase PostgresSupabase AuthRow Level SecuritySQL migrationsContent-hash analysis cache

Documents

pypdfConditional chunkingSignature and encryption checks

Quality & platform

pytestVitestruffVercel (multi-service, one origin)

Limitations

  • Effectively single-provider. The two-provider architecture works and the fallback is tested, but the configured primary's free tier cannot accept a full paper, so genuine redundancy is not achieved today.
  • Grounded is not the same as accurate. Output is constrained to the supplied document and non-conforming output is discarded, but nothing establishes that a summary is correct. Generated content needs checking before it is relied on or cited.
  • No labelled benchmark, so no accuracy figure is reported. The evaluation measures completion, fallback behaviour, provenance, latency and cost avoidance instead.
  • The evaluation covers five English-language papers from one discipline, analysed once per configuration and assessed by the project team rather than independently.
  • A revoked session stays valid for up to about five seconds, a deliberate trade against contacting the auth service on every request.
  • No OCR, so scanned papers with no text layer are rejected rather than processed.
  • The literature review covers only the prior work one paper discusses. It does not search a corpus.
  • Upstream provider rate limits are handled and surfaced with a retry hint, but the application does not rate-limit its own users.
  • Papers are identified by filename and content hash: bibliographic metadata extraction is not implemented.
  • Retrieval over a stored corpus is planned, not built.
  • The dashboard's How ResearchForge works panel still says the paper goes to Gemini, which is out of date since Gemini was retired from routing. A copy fix, not yet made.

What's next

  1. 01Show saved cross-paper reviews in the interface, using the list and fetch routes that already exist.
  2. 02Correct the stale Gemini wording on the dashboard.
  3. 03Configure a primary provider whose limits accept a whole paper, so the fallback is redundancy rather than the normal path.
  4. 04Planned in the project plan, not started: embeddings and retrieval over a stored library (F3), grounded question answering across papers (F8), page-level citations (F9) and Markdown or PDF export (F10).

Source and further reading

Finished the technical breakdown?

Return to the concise HR overview or discover other projects in the portfolio.