-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathREADME.TXT
More file actions
617 lines (457 loc) · 24.8 KB
/
Copy pathREADME.TXT
File metadata and controls
617 lines (457 loc) · 24.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
FINORA - COMPLETE BEGINNER GUIDE
================================
Last updated: 21 August 2026
This guide explains the project from the first browser click to the last database
write. You do not need backend experience to follow it. Read the QUICK START first
if you only want to run the app. Read the remaining sections to understand how it
works internally.
1. WHAT FINORA IS
-----------------
Finora is a full-stack document intelligence application. A user can upload a
PDF, DOC, DOCX, or TXT file, keep the original file in private object storage,
process its contents, search source-linked passages, review extracted fields, ask
questions with citations, edit an extraction template, and view workspace analytics.
The browser is not the source of truth. Users, workspaces, documents, processing
states, fields, schemas, activities, and assistant history are persisted by the
backend. The interface does not contain fake documents, fake AI answers, or fake
processing timers.
2. QUICK START IN VS CODE ON WINDOWS
------------------------------------
Prerequisites:
1. Install Node.js 22.13 or newer.
2. Install Visual Studio Code.
3. Open VS Code.
4. Choose File -> Open Folder.
5. Select:
C:\Users\anubh\Desktop\Talk2PDF
6. In VS Code choose Terminal -> New Terminal.
7. Run these commands one at a time:
npm install
Copy-Item .env.example .env.local
npm run db:migrate:local
npm run dev
8. Wait until the terminal prints:
Local: http://localhost:3000/
9. Open http://localhost:3000 in a browser.
10. Click "Open workspace" or go directly to:
http://localhost:3000/workspace
Keep the terminal open. The development server stops when the terminal closes or
when you press Ctrl+C.
3. ENABLE FREE-TIER PDF AI PROCESSING
-------------------------------------
TXT files work locally without an AI key. They are really stored, split into
passages, indexed, and searchable. PDF understanding and the cited AI assistant can
use Google's Gemini API free tier. Google applies free usage limits, so this means
free within its current quota, not unlimited use forever.
Create a Gemini API key at:
https://aistudio.google.com/api-keys
Sign in with Google, create an API key in a free project, and do not enable billing
unless you personally decide to upgrade later. Then open .env.local and set:
AI_PROVIDER=gemini
GEMINI_API_KEY=your_private_gemini_key
GEMINI_MODEL=gemini-3.1-flash-lite
GEMINI_EMBEDDING_MODEL=gemini-embedding-001
Then stop and restart npm run dev.
Important rules:
- Never add NEXT_PUBLIC_ in front of GEMINI_API_KEY. NEXT_PUBLIC values can be sent
to the browser.
- Never paste the key into a chat, screenshot, source file, or Git commit. Put it
only after GEMINI_API_KEY= in the private .env.local file.
- Never commit .env.local. The .gitignore already excludes it.
- If the key is missing, Finora deliberately reports a configuration error. It
does not invent processing results or AI answers.
- Gemini mode supports PDF and TXT input. Convert DOC or DOCX files to PDF before
uploading them. The optional paid OpenAI provider remains available for those
formats if you configure AI_PROVIDER=openai and OPENAI_API_KEY.
- Google's free-tier terms say submitted content may be used to improve Google
products. Do not use the free tier for confidential documents unless that policy
is acceptable to you.
4. TECHNOLOGY USED
------------------
Frontend:
- React 19 renders the interactive interface.
- TypeScript checks the shapes of data and catches mistakes before runtime.
- vinext provides the Next.js-compatible application router on Vite.
- TanStack Query loads server data, caches it, handles errors, and refreshes it.
- Zustand stores small device/UI choices such as the active section and selected
document. It does not replace the server database.
- React Hook Form and Zod validate editable forms.
- Framer Motion provides restrained interface animation.
- Lucide supplies accessible interface icons.
Backend and storage:
- Route handlers in app/api are the HTTP backend.
- A Cloudflare Worker runs the full application.
- D1 (SQLite) stores relational application records.
- R2 stores the original uploaded document bytes.
- Drizzle ORM creates typed SQL queries and migrations.
- unpdf supplies a serverless PDF.js build that extracts text separately from every
PDF page inside Node.js and Cloudflare Workers.
- Gemini Generate Content reads PDF/TXT documents and produces strict,
source-faithful structured output on the free tier.
- Gemini Embeddings creates vectors for semantic ranking on the free tier.
- OpenAI Responses and Embeddings remain an optional paid alternative.
The frontend and backend are in one deployable project. Browser requests use /api,
so no separate Express server or CORS configuration is required.
5. IMPORTANT FOLDERS AND FILES
------------------------------
app/
Page routes and API routes. app/page.tsx is the landing page.
app/workspace/page.tsx is the signed-in product shell.
app/api/
The real backend HTTP endpoints. Every protected endpoint creates an authenticated
request context before reading or changing workspace data.
components/
Shared visual building blocks: top bar, sidebar, status, upload dialog, command
palette, and empty states.
features/
Product areas grouped by purpose: dashboard, documents, search, assistant,
schemas, analytics, settings, and the landing tour.
services/api-client.ts
The one browser API gateway. UI components call this file instead of constructing
unrelated fetch requests in many places.
stores/workspace-store.ts
Short-lived interface state such as the current navigation section, theme,
selected document, page number, and open dialogs.
types/index.ts
Shared browser-side TypeScript contracts for documents, snapshots, fields,
results, citations, and analytics.
server/auth.ts
Turns trusted hosting headers or the local development identity into a user,
workspace, membership, and role. It also checks document ownership and write roles.
server/ai.ts
Analyzes compact document metadata, creates rate-aware grouped embeddings, and
answers only from page-linked source passages through Gemini or OpenAI.
server/document-content.ts
Uses unpdf to read every original PDF page locally, measures readable-page
coverage, rejects image-only scans that need OCR, and produces searchable chunks.
server/text-chunking.ts
Normalizes extracted text and splits long pages into overlapping passages so a
fact crossing a chunk boundary is not silently lost.
server/documents.ts
Runs actual document processing, persists pages/chunks/fields, changes job state,
records audit activity, and saves actionable failures.
server/search.ts
Loads only chunks from the active workspace, applies semantic similarity when
embeddings exist, and uses honest keyword ranking when they do not.
server/runtime.ts
Reads D1, R2, AI model configuration, and local identity from the runtime.
next.config.ts
Raises vinext's multipart/Server Action request envelope from its 1 MB default to
100 MB. The upload API still applies the stricter product rules: 25 MB per file
and no more than 10 files. Without this framework setting, a 1.3 MB book can be
rejected before Finora's upload route gets a chance to validate or store it.
server/serializers.ts
Converts raw database rows into stable, human-friendly API responses.
db/schema.ts
The complete relational data model.
drizzle/
Generated SQL migrations. These files create the database in a repeatable way.
worker/index.ts
The production Cloudflare Worker entry point. It serves application routes and
image optimization.
.openai/hosting.json
Declares the hosted project plus its DB and DOCUMENTS bindings.
wrangler.jsonc
Defines matching local D1/R2 resources and the migration directory.
.env.example
A safe template of environment variable names. It contains no secret value.
aboutme.txt
A dated implementation and verification log, updated as backend work progresses.
6. DATABASE TABLES
------------------
users
One row per authenticated person: ID, email, display name, timestamps.
workspaces
A separate data boundary for a team. created_by records the creator.
memberships
Connects users to workspaces and assigns owner, admin, reviewer, member, or viewer.
documents
File metadata: workspace, uploader, R2 key, MIME type, size, detected type,
processing state, pages, confidence, favorite flag, and failure information.
processing_jobs
Real processing state: stage, progress, attempts, start/end times, and errors.
document_pages
Readable text grouped by original page number.
document_chunks
Smaller searchable passages with workspace, document, page, section, content,
and an optional JSON-encoded embedding.
extraction_schemas
Workspace extraction-template headers such as Invoice and its version.
schema_fields
Ordered field definitions: name, type, required flag, and description.
extraction_fields
Values found in one document, including confidence, page, provenance, and verifier.
activities
Audit-friendly workspace events for uploads, processing, AI, and human review.
assistant_queries
Successfully generated questions, answers, and their citations.
Foreign keys prevent orphan records. Workspace indexes make the actual application
queries efficient. Unique indexes stop duplicate memberships, page numbers, jobs,
schemas, and extracted field keys.
7. AUTHENTICATION AND AUTHORIZATION
-----------------------------------
In hosted production, the platform supplies trusted headers:
- oai-authenticated-user-id
- oai-authenticated-user-email
- oai-authenticated-user-full-name
The backend reads these headers; the browser is not allowed to choose a workspace
ID or user role. On the first request, the backend upserts the user. If that user has
no membership, it creates "My Workspace", owner membership, and a default invoice
schema.
For local development, .env.local provides LOCAL_DEV_USER_EMAIL and
LOCAL_DEV_USER_NAME. This avoids building a fake login screen while still exercising
the same persisted user/workspace/membership checks.
Every document-specific route verifies both the document ID and active workspace.
Viewer roles cannot upload, edit fields, change schemas, or retry processing.
8. COMPLETE UPLOAD AND PROCESSING FLOW
--------------------------------------
1. The user opens Upload documents.
2. The browser validates extension, size (25 MB maximum), and a maximum of 10 files.
3. services/api-client.ts sends multipart/form-data to the upload API. The 100 MB
framework envelope lets ordinary document requests reach the application; the
API then enforces the documented per-file and file-count rules.
4. The backend authenticates the request and checks write permission.
5. The backend validates again. Browser validation is helpful but never trusted.
6. A random document ID and workspace-scoped storage key are created.
7. Original bytes are written to the DOCUMENTS R2 bucket.
8. A document row, processing-job row, and upload activity are inserted in D1.
9. The processing service reads the bytes back from R2.
10. For PDF, unpdf reads every original page using its Cloudflare-compatible PDF.js
engine. TXT is split by form-feed page boundaries. This extraction does not ask
Gemini to summarize the file and therefore cannot silently shrink a whole book
into a few AI-selected snippets.
11. Each original page is normalized and saved, including blank pages. Pages with
text are divided into passages of about 1,600 characters with 240 characters of
overlap. The overlap preserves sentences and facts near passage boundaries.
12. The backend checks extraction coverage. A mostly image-only/scanned PDF is not
marked complete with missing content; it returns instructions to run OCR first.
13. Gemini or OpenAI receives a small spread of page samples only for classification,
a concise description, and useful fields. That metadata response is never used
as the full search index.
14. Gemini embeddings combine up to five neighboring passages under a safe character
limit. One group vector is attached back to each member passage. This covers a
book within far fewer free-tier embedding operations. If quota is unavailable,
full keyword search remains usable and processing still stores every page.
15. Page rows are inserted in batches of 20 and chunk rows in batches of 10 so D1's
SQL parameter limit is not exceeded by long books.
16. Retry clears old processing output and rebuilds the page/chunk/field set in small
batches. A failed rebuild remains visibly Failed and can be retried safely.
17. Coverage below 80 percent or low-confidence fields puts the document in Review.
Coverage below 50 percent fails with OCR guidance instead of false success.
18. The activity log records total pages, readable pages, coverage, characters,
chunks, embedded chunks, and fields without storing document text in logs.
19. TanStack Query refreshes the workspace so the persisted result appears.
If processing throws, the original file remains stored, document/job rows become
failed, the exact safe message is saved, and the UI offers Retry processing. Retry
uses the same authorized pipeline and increments the attempt count.
9. SEARCH FLOW
--------------
1. The user enters at least two characters.
2. The backend restricts candidate chunks to the active workspace, selected document,
and optional type. It evaluates every stored chunk; there is no old 300-chunk
cutoff that could exclude later chapters.
3. With embeddings, it compares the query with grouped passage vectors by cosine
similarity and combines semantic and lexical relevance.
4. Without embeddings, it counts honest query-term matches across the complete
document. Common question words are removed and simple word endings are reduced
so "hire", "hired", and similar forms rank more consistently. Free-tier quota
failure never deletes the extracted page index.
5. Results include document ID/name, original page, section, passage, type, and score.
6. Clicking a result selects that document and opens its source page.
10. AI ASSISTANT AND CITATIONS
------------------------------
1. The question is validated (3 to 2,000 characters).
2. If a document is selected, workspace access to it is verified.
3. The backend classifies the question as focused, whole-document overview, or
ending analysis. A focused question retrieves up to 12 relevant passages from
every indexed page.
4. A broad prompt containing terms such as summarize, synopsis, overview, whole
book, or main themes loads the complete ordered book index (within the provider's
safe context budget) so the answer represents the whole work. Ending phrases
such as "explain the ending," "final twist," "epilogue," "last chapter," and
"what really happened" also load the complete book instead of relying on a few
keyword matches.
5. Only permitted page-linked passages and the question are sent to the AI service.
If a focused query has no lexical or semantic match at all, the assistant falls
back to the complete selected-document context instead of failing prematurely.
It also performs that full-context second pass when the focused answer contains
no valid citation, protecting paraphrased questions from a weak first retrieval.
6. Strict output requires an answer and source indexes. By default, focused answers
target 250 to 600 words, full summaries target 700 to 1,200 words, and ending
explanations target 600 to 1,000 words. An explicit request for a brief answer
overrides those targets.
7. Ending answers must walk through the decisive events, reveal or twist, motives,
consequences, conflicting evidence, and any deliberate ambiguity. Confirmed
source facts must be separated from interpretation.
8. If a cited answer is still below its mode's minimum depth, the backend makes one
controlled rewrite request asking for a fuller explanation without repetition.
Unsupported questions may remain short because the system must not pad a refusal
with invented details.
9. The prompt may correct an incorrectly named role, but it may never invent an
unsupported relationship. The provider has a 6,144-token output budget so it is
not accidentally forced into a tiny response.
10. Invalid indexes are removed and citations are capped at 6 for focused answers or
12 for overviews/endings so the interface remains useful.
11. The successful answer and citations are saved to assistant_queries.
12. Paragraph breaks from detailed answers are preserved on screen. Each source
button opens the exact stored document page.
When the selected provider key is absent, the endpoint returns HTTP 503 with setup
guidance. There is no local fabricated answer fallback.
11. EXTRACTION AND SCHEMA EDITING
---------------------------------
Opening a document calls its detail endpoint, so the extracted fields always belong
to that selected document. Editing a value changes its provenance to modified.
Saving performs validated upserts keyed by document plus field key and creates an
activity record.
The Schema page edits an ordinary visual form. Saving validates 1 to 50 fields,
replaces the ordered Invoice field set, increments the schema version, and records
the activity. Returned IDs are the same IDs stored in D1.
12. ANALYTICS
-------------
Analytics are calculated from persisted workspace rows:
- processed documents
- completed/review success percentage
- average processing duration
- documents needing review
- average extraction confidence
- original storage bytes
- daily upload throughput
No chart number is hardcoded in the frontend.
13. API REFERENCE
-----------------
GET /api/health
Service readiness and whether AI is configured.
GET /api/workspaces/current/snapshot
Current user/workspace, documents, activity, pipeline, latest fields, and schema.
POST /api/workspaces/current/documents
Validated multipart upload, R2 storage, processing, and persisted result.
GET /api/workspaces/current/documents/:id
One authorized document plus its own extracted fields.
GET /api/workspaces/current/documents/:id/file
Original R2 object with inline content headers and private no-store caching.
POST /api/workspaces/current/documents/:id/retry
Authorized retry of a failed or reprocessable document.
PUT /api/workspaces/current/documents/:id/extraction
Validate and save reviewed/modified extraction fields.
GET /api/workspaces/current/search?q=...
Workspace-scoped semantic/keyword passage results.
POST /api/workspaces/current/assistant
Source-constrained cited answer.
PUT /api/workspaces/current/schemas/invoice
Validate and save the Invoice extraction field template.
GET /api/workspaces/current/analytics
Live workspace metrics.
Errors use JSON with message and code. Unknown internal details are not leaked.
14. LOCAL DATA AND MIGRATIONS
-----------------------------
Local D1 and R2 emulator state is under .wrangler/. It is ignored by Git.
To apply new migrations after schema changes:
npm run db:generate
npm run db:migrate:local
Do not manually edit generated migration snapshots. Change db/schema.ts first and
generate the migration.
Deleting .wrangler removes local development data. That is destructive and cannot
be undone, so stop the server and back up anything important before doing it.
15. QUALITY CHECKS
------------------
Run:
npx tsc --noEmit
npm run lint
npm test
npx tsc checks TypeScript. npm run lint checks React, unused values, and code
quality. npm test makes a production build, checks important rendered HTML, creates
a two-page PDF in memory, verifies that both pages are extracted separately, and
checks that long overlapping chunks retain their beginning and ending.
Manual primary journey:
1. Open /workspace.
2. Upload tests/fixtures/sample-agreement.txt.
3. Confirm Completed appears.
4. Open the document and confirm the original text loads.
5. Search for "termination" and open the result.
6. Add/edit an extracted field and save it.
7. Open Analytics and confirm persisted counts.
8. With GEMINI_API_KEY configured, ask a question and open its citation.
9. Without the key, confirm the assistant gives an actionable configuration error.
16. PRODUCTION HOSTING
----------------------
For a complete beginner-friendly launch sequence, access decisions, secret setup,
private-first deployment, smoke testing, security checks, monitoring, rollback, and
scaling plan, read HOSTING_ROADMAP.TXT in this project.
The project is prepared for OpenAI Sites/Cloudflare hosting through
.openai/hosting.json. Production needs:
- the hosted D1 binding named DB
- the hosted R2 binding named DOCUMENTS
- the D1 migration applied to the production database
- GEMINI_API_KEY stored as a server secret when free-tier AI features are wanted
- authenticated hosting enabled so trusted user headers are supplied
Do not upload/deploy source code or create paid resources merely by running the
local app. Publishing is a separate external action and should be done only after
the owner approves the target project and secret configuration.
17. CURRENT ARCHITECTURE BOUNDARIES
-----------------------------------
This repository originally had no PostgreSQL, Redis, BullMQ, Python service, or
WebSocket server to preserve. The working implementation uses its existing native
Sites/Cloudflare runtime: D1, R2, route handlers, and server-side AI-provider calls.
Processing currently completes inside the upload/retry request. Its stages are real
persisted job states, but it is not a detached queue worker. NEXT_PUBLIC_SOCKET_URL
is optional support for an external authenticated Socket.IO event source; the local
product refreshes server state after mutations and does not simulate socket events.
For very large production workloads, the deliberate next scaling step is a managed
queue/consumer that calls the same processing service and publishes authenticated
workspace events. D1 chunk embeddings are suitable for this portfolio-scale product;
a dedicated vector index is the next step for millions of passages.
18. TROUBLESHOOTING
-------------------
"npm is not recognized"
Install Node.js, restart VS Code, and open a new terminal.
"No connection could be made to localhost:3000"
Run npm run dev and keep that terminal open.
"no such table"
Run npm run db:migrate:local, then restart npm run dev.
"AI document processing is not configured"
Create a Gemini key, add GEMINI_API_KEY to .env.local, and restart the server.
TXT search still works without the key.
"Gemini free-tier quota is exhausted"
Wait for Google's quota window to reset, reduce requests, or choose a paid plan.
Finora does not silently charge money or switch providers. PDF pages remain
fully stored and keyword-searchable even when an embedding window is unavailable.
"Only N of M PDF pages contain extractable text"
The PDF is probably a scan made from page images. Run OCR in Adobe Acrobat,
Microsoft Lens, Google Drive, or another trusted OCR tool, export a searchable
PDF, and upload it again. Finora refuses false completion when most page text
cannot actually be read.
"Gemini mode currently supports PDF and TXT"
Open the DOC/DOCX file in Word or Google Docs, export/download it as PDF, and
upload that PDF.
"The document was not found or you do not have access"
The selected ID is absent or belongs to another workspace. Return to Documents
and open a file visible in the current workspace.
Upload rejected
Use PDF, DOC, DOCX, or TXT; keep each file at or below 25 MB and each batch at or
below 10 files. The whole multipart request must also stay below the 100 MB
framework envelope, so split a very large batch into multiple uploads.
"Payload Too Large" or HTTP 413
Restart npm run dev so the current next.config.ts is loaded. If the error remains,
upload fewer files in one batch. The browser now displays this server reason
instead of replacing it with a generic upload failure.
Changes do not appear
Refresh once. In development, also check the terminal for a compilation/runtime
error and restart npm run dev after changing environment values.
19. SECURITY CHECKLIST
----------------------
- Server secrets never use NEXT_PUBLIC prefixes.
- Protected routes authenticate before database or object reads.
- Document access is always workspace-scoped.
- Write operations enforce roles.
- File extension, count, and size are validated on server and client.
- R2 file responses are private and no-store.
- AI receives files from the server, not an exposed browser key.
- AI output is schema-constrained and citations are bounded to retrieved sources.
- Successful and failed important actions produce persisted activity records.
- No fake success fallback hides missing storage, database, authentication, or AI.
20. ONE-SENTENCE MENTAL MODEL
-----------------------------
The React interface sends authorized requests to the same Cloudflare application;
the backend stores file bytes in R2, structured truth in D1, uses the configured
Gemini or OpenAI provider only when its private key is present, and returns
source-linked persisted results for the interface to render.