Skip to content

Commit 2ecd863

Browse files
keli-wenclaude
andauthored
refactor(paper): add source-first Paper Flow V1 (#120)
## Summary - Replaces the model-authored `Paper(TreeKnowledge)` output with a source-first `PaperFlowResult` containing an immutable exact source revision, a deterministic page-aware `PaperChunkSet`, and an independently versioned cited `PaperGlobalSummary`. - Implements manager-style multi-agent summarization with the OpenAI Agents SDK: one coordinator retains final-summary ownership, delegates required chunk ranges through a real `Agent.as_tool()` research specialist, permits bounded parallel and overlapping follow-up calls, and rejects incomplete source coverage. - Enforces shared research call, concurrency, turn, input-token, worker-output, total-output, final-output, citation, and runtime limits. Worker reports contain typed context/contribution/method/result/limitation findings with code-validated chunk/page coordinates. - Replaces the open `list[str]` paper search filter with the Pydantic-validated `PaperArtifactKind` enum. Python callers use `PaperArtifactKind.GLOBAL_SUMMARY` or `PaperArtifactKind.CHUNK_SET`; JSON and YAML retain the corresponding string values while unknown kinds fail validation. - Adds SQLite schema version 3 with explicit source/blob, artifact/member, lineage, and search-projection tables; atomic `put_paper()`; selective embedding reuse; reopen-safe locator resolution; cross-record integrity checks; and a tested `LegacyPaper` compatibility boundary for existing databases. - Moves retrieval-text selection out of canonical knowledge models, preserves existing non-paper search behavior, and documents the Source → Artifact → SearchProjection contract across flow, knowledge, RAG, library, examples, and contributor guidance. The cross-flow placement and reuse policy for Pydantic input/config types is intentionally tracked separately in #121 instead of being decided implicitly by this paper refactor. ## Related Issues Closes #119 - #121 tracks ownership, reuse, naming, supported-variant, schema, and migration rules for typed operation inputs and stage configuration. ## Breaking Changes - `paper_flow()` now returns `PaperFlowResult` and supports PDF-backed arXiv, HTTP, and local inputs in V1; raw text, non-PDF content, and unresolved DOI inputs fail explicitly. - The former primary `Paper` tree is no longer exported or generated. Existing version-2 databases and the bundled compatibility example load that shape as `LegacyPaper`. - `SemanticQuery.artifact_kinds` now validates as `list[PaperArtifactKind]`; valid serialized string values remain `paper_summary` and `paper_chunk_set`. ## Verification - `./scripts/verify.sh` — PASS: ruff format/check, basedpyright with 0 errors and warnings, all 7 import contracts, 335 tests, and 83.69% coverage. - `.venv/bin/python scripts/verify_pdf_rag_e2e.py` — live PASS against exact arXiv revision `1706.03762v7`: manager/research-agent orchestration, complete coverage of 36 chunks, 15 text pages, 19 source assets, 42 chunk-to-asset references, cited summary across 7 pages, summary retrieval, multi-head-attention passages in the top five, close/reopen, repeated searches, and canonical locator resolution. - Focused config, flow, library, and live-verifier tests — PASS: 41 tests cover typed artifact parsing, real agent-tool construction, worker concurrency and budgets, complete-coverage enforcement, persistence, retrieval, and E2E assertions. - Commit and pre-push hooks — PASS: file hygiene, YAML validation, large-file check, ruff, and the deterministic verification harness. - GitHub Actions — required `ci` PASS; the `paper-flow` job PASS with its live step explicitly skipped because the repository has no `OPENAI_API_KEY`; the unrelated `news` job failed on both the initial run and one retry because PR Newswire returned `404` for its RSS endpoint while the discovery and ticker-hint checks passed. ## Checklist - [x] The title uses English Conventional Commit format: `type(scope): summary`. - [x] The related issue or design discussion is linked when applicable. - [x] `./scripts/verify.sh` passes. - [x] Every applicable live-network component smoke test passes, or this PR states why none applies. - [x] Public behavior has focused tests, an example, and documentation where applicable. - [x] The PR contains no unrelated changes. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4ecb2e5 commit 2ecd863

60 files changed

Lines changed: 5317 additions & 1436 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/quantmind-dev/references/develop-components.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ apply throughout.
4343
- Pydantic models, `frozen=True`, `extra="forbid"`.
4444
- Every `BaseKnowledge` subclass **must** require `as_of: datetime`
4545
(financial time-sensitivity is mandatory) and a typed `source: SourceRef`
46-
(no bare strings), and **must** override `embedding_text()`.
46+
(no bare strings).
47+
- Canonical models do not select retrieval text or store vectors. Put
48+
rebuildable text projections in `quantmind.library`.
4749
- Pick one shape: `FlattenKnowledge` (atomic card), `TreeKnowledge`
48-
(hierarchical artifact), or `GraphKnowledge` (placeholder). Whole-document
49-
objects are `TreeKnowledge` even when a flatten card exists alongside
50-
(e.g. `Paper` vs `PaperKnowledgeCard`).
50+
(hierarchical artifact), or `GraphKnowledge` (placeholder) for conventional
51+
knowledge. Source-first paper revisions and independently versioned paper
52+
artifacts use their dedicated frozen models instead of `BaseKnowledge`.
5153

5254
### `quantmind/configs/` — operation cfg + typed inputs
5355

.claude/skills/quantmind-dev/references/develop-components.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ apply throughout.
4343
- Pydantic models, `frozen=True`, `extra="forbid"`.
4444
- Every `BaseKnowledge` subclass **must** require `as_of: datetime`
4545
(financial time-sensitivity is mandatory) and a typed `source: SourceRef`
46-
(no bare strings), and **must** override `embedding_text()`.
46+
(no bare strings).
47+
- Canonical models do not select retrieval text or store vectors. Put
48+
rebuildable text projections in `quantmind.library`.
4749
- Pick one shape: `FlattenKnowledge` (atomic card), `TreeKnowledge`
48-
(hierarchical artifact), or `GraphKnowledge` (placeholder). Whole-document
49-
objects are `TreeKnowledge` even when a flatten card exists alongside
50-
(e.g. `Paper` vs `PaperKnowledgeCard`).
50+
(hierarchical artifact), or `GraphKnowledge` (placeholder) for conventional
51+
knowledge. Source-first paper revisions and independently versioned paper
52+
artifacts use their dedicated frozen models instead of `BaseKnowledge`.
5153

5254
### `quantmind/configs/` — operation cfg + typed inputs
5355

.github/workflows/e2e.yml

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ on:
1919
- 'quantmind/preprocess/fetch/arxiv.py'
2020
- 'quantmind/preprocess/format/pdf.py'
2121
- 'quantmind/rag/**'
22+
- 'quantmind/configs/paper.py'
23+
- 'quantmind/flows/_paper_summary.py'
24+
- 'quantmind/flows/paper.py'
25+
- 'quantmind/knowledge/paper.py'
26+
- 'quantmind/library/**'
2227
- 'scripts/verify_pdf_rag_e2e.py'
2328
- 'pyproject.toml'
2429
schedule:
@@ -38,7 +43,7 @@ jobs:
3843
runs-on: ubuntu-latest
3944
outputs:
4045
news: ${{ steps.filter.outputs.news }}
41-
pdf_rag: ${{ steps.filter.outputs.pdf_rag }}
46+
paper_flow: ${{ steps.filter.outputs.paper_flow }}
4247

4348
steps:
4449
- name: Checkout
@@ -60,9 +65,14 @@ jobs:
6065
- 'quantmind/preprocess/format/html.py'
6166
- 'quantmind/preprocess/fetch/**'
6267
- 'pyproject.toml'
63-
pdf_rag:
68+
paper_flow:
6469
- '.github/workflows/e2e.yml'
6570
- 'scripts/verify_pdf_rag_e2e.py'
71+
- 'quantmind/configs/paper.py'
72+
- 'quantmind/flows/_paper_summary.py'
73+
- 'quantmind/flows/paper.py'
74+
- 'quantmind/knowledge/paper.py'
75+
- 'quantmind/library/**'
6676
- 'quantmind/preprocess/fetch/arxiv.py'
6777
- 'quantmind/preprocess/format/pdf.py'
6878
- 'quantmind/rag/**'
@@ -98,11 +108,13 @@ jobs:
98108
- name: Run live news E2E
99109
run: .venv/bin/python scripts/verify_news_e2e.py
100110

101-
pdf-rag:
111+
paper-flow:
102112
needs: changes
103-
if: github.event_name != 'pull_request' || needs.changes.outputs.pdf_rag == 'true'
113+
if: github.event_name != 'pull_request' || needs.changes.outputs.paper_flow == 'true'
104114
runs-on: ubuntu-latest
105115
timeout-minutes: 10
116+
env:
117+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
106118

107119
steps:
108120
- name: Checkout
@@ -125,5 +137,17 @@ jobs:
125137
- name: Install project runtime dependencies
126138
run: uv pip install --python .venv/bin/python -e .
127139

128-
- name: Run live PDF RAG E2E
140+
- name: Check OpenAI credential
141+
id: openai
142+
shell: bash
143+
run: |
144+
if [[ -n "${OPENAI_API_KEY}" ]]; then
145+
echo "available=true" >> "$GITHUB_OUTPUT"
146+
else
147+
echo "available=false" >> "$GITHUB_OUTPUT"
148+
echo "::notice::Skipping Paper Flow V1 E2E because OPENAI_API_KEY is not configured."
149+
fi
150+
151+
- name: Run live Paper Flow V1 E2E
152+
if: steps.openai.outputs.available == 'true'
129153
run: .venv/bin/python scripts/verify_pdf_rag_e2e.py

README.md

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ We use [uv](https://github.com/astral-sh/uv) for fast and reliable Python packag
156156

157157
Component-specific guides and architecture notes live under [`docs/`](docs/).
158158

159-
#### Run a single paper through `paper_flow`
159+
#### Build an exact paper source, chunks, and cited summary
160160

161161
```python
162162
import asyncio
@@ -167,11 +167,12 @@ from quantmind.flows import paper_flow
167167

168168

169169
async def main() -> None:
170-
paper = await paper_flow(
171-
ArxivIdentifier(id="2401.12345"),
170+
result = await paper_flow(
171+
ArxivIdentifier(id="1706.03762v7"),
172172
cfg=PaperFlowCfg(model="gpt-4o-mini"),
173173
)
174-
print(paper.model_dump_json(indent=2))
174+
print(result.global_summary.summary)
175+
print(result.source_revision.id, result.chunk_set.id)
175176

176177

177178
asyncio.run(main())
@@ -219,17 +220,17 @@ async def main() -> None:
219220
"Pull arXiv 2401.12345 about cross-sectional momentum; use gpt-4o-mini.",
220221
target_flow=paper_flow,
221222
)
222-
paper = await paper_flow(inp, cfg=cfg)
223-
print(paper.model_dump_json(indent=2))
223+
result = await paper_flow(inp, cfg=cfg)
224+
print(result.global_summary.summary)
224225

225226

226227
asyncio.run(main())
227228
```
228229

229-
> **Note**: QuantMind is mid-migration to OpenAI Agents SDK
230-
> (see [#71](https://github.com/LLMQuant/quant-mind/issues/71)). PR5 lands the
231-
> apex layer (`flows/` + `magic.py`); the remaining work is the `mind/`
232-
> memory + store layer scheduled for PR6 and PR7.
230+
Paper Flow V1 is source-first: code preserves the exact PDF revision and
231+
page-aware chunks before accepting a bounded, cited model summary. See the
232+
[complete persist/reopen/search example](examples/flows/paper.py) and
233+
[design contract](contexts/design/flow/paper.md).
233234

234235
---
235236

@@ -238,7 +239,7 @@ asyncio.run(main())
238239
- [x] Better `flow` design for user-friendly usage
239240
- [x] First production level example (Quant Paper Agent)
240241
- [ ] Migrate Agent layer to OpenAI Agents SDK
241-
- [ ] Standardize knowledge format with `knowledge/` (Pydantic-based)
242+
- [x] Standardize knowledge format with `knowledge/` (Pydantic-based)
242243
- [ ] Additional content sources (financial news, blogs, reports)
243244
- [ ] Cross-step working memory (`mind/memory`) for batch document processing
244245

@@ -254,18 +255,10 @@ QuantMind is designed with a larger vision: to become a comprehensive intelligen
254255
The foundation we're building today—starting with papers—will expand to encompass the entire financial information ecosystem.
255256

256257
> [!NOTE]
257-
> **Future Conceptual Example (PR6 brings `FilesystemMemory`):**
258-
>
259-
> ```python
260-
> from quantmind.configs.paper import ArxivIdentifier
261-
> from quantmind.flows import paper_flow
262-
> from quantmind.knowledge import Paper
263-
> from quantmind.mind.memory import FilesystemMemory # PR6
264-
>
265-
> memory = FilesystemMemory("./mem/factor-research/")
266-
> for arxiv_id in arxiv_ids:
267-
> paper: Paper = await paper_flow(ArxivIdentifier(id=arxiv_id), memory=memory)
268-
> ```
258+
> The current source-first paper path produces independently versioned source,
259+
> chunk-set, and cited-summary artifacts. Future agent memory and cross-document
260+
> reasoning can build on `LocalKnowledgeLibrary.search()` without changing
261+
> those canonical artifacts.
269262
270263
This future state represents our commitment to moving beyond simple data aggregation and toward genuine machine intelligence in the financial domain.
271264

contexts/design/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ implementation must preserve.
2424

2525
| Domain | Design |
2626
|---|---|
27-
| Flow | [Paper extraction from input to validated result](flow/paper.md) |
27+
| Flow | [Source-first paper flow](flow/paper.md) |
28+
| Knowledge | [Paper sources, artifacts, citations, and locators](knowledge/paper.md) |
2829
| Flow | [News collection](flow/news.md) |
2930
| Preprocess | [Page-aware multimodal PDF parsing](preprocess/pdf.md) |
3031
| RAG | [Page-aware document chunking and retrieval](rag/document.md) |
3132
| Library | [Local knowledge storage and meaning-based search](library/local.md) |
3233
| Operations | [Public operation naming](operations/naming.md) |
34+
| Operations | [Orchestration and construction altitude](operations/orchestration.md) |
3335

3436
## Organization Rules
3537

0 commit comments

Comments
 (0)