okf-loremaster 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- okf_loremaster-0.1.0/.env.example +158 -0
- okf_loremaster-0.1.0/.github/workflows/ci.yml +86 -0
- okf_loremaster-0.1.0/.github/workflows/release.yml +152 -0
- okf_loremaster-0.1.0/.gitignore +53 -0
- okf_loremaster-0.1.0/CHANGELOG.md +44 -0
- okf_loremaster-0.1.0/LICENSE +201 -0
- okf_loremaster-0.1.0/PKG-INFO +799 -0
- okf_loremaster-0.1.0/README.md +754 -0
- okf_loremaster-0.1.0/assets/okf-loremaster-logo.png +0 -0
- okf_loremaster-0.1.0/cspell.json +65 -0
- okf_loremaster-0.1.0/pyproject.toml +119 -0
- okf_loremaster-0.1.0/scripts/check_dist.py +103 -0
- okf_loremaster-0.1.0/scripts/record_fixtures.py +137 -0
- okf_loremaster-0.1.0/src/okf_loremaster/__init__.py +9 -0
- okf_loremaster-0.1.0/src/okf_loremaster/cli.py +483 -0
- okf_loremaster-0.1.0/src/okf_loremaster/clients/__init__.py +162 -0
- okf_loremaster-0.1.0/src/okf_loremaster/clients/_http.py +457 -0
- okf_loremaster-0.1.0/src/okf_loremaster/clients/bioc.py +141 -0
- okf_loremaster-0.1.0/src/okf_loremaster/clients/cassette.py +132 -0
- okf_loremaster-0.1.0/src/okf_loremaster/clients/eutils.py +552 -0
- okf_loremaster-0.1.0/src/okf_loremaster/clients/icite.py +161 -0
- okf_loremaster-0.1.0/src/okf_loremaster/clients/pubtator.py +116 -0
- okf_loremaster-0.1.0/src/okf_loremaster/config.py +303 -0
- okf_loremaster-0.1.0/src/okf_loremaster/curation.py +270 -0
- okf_loremaster-0.1.0/src/okf_loremaster/emitters/__init__.py +35 -0
- okf_loremaster-0.1.0/src/okf_loremaster/emitters/okf.py +1243 -0
- okf_loremaster-0.1.0/src/okf_loremaster/emitters/vectors.py +700 -0
- okf_loremaster-0.1.0/src/okf_loremaster/events.py +149 -0
- okf_loremaster-0.1.0/src/okf_loremaster/extraction_cache.py +108 -0
- okf_loremaster-0.1.0/src/okf_loremaster/finalize.py +62 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/__init__.py +7 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/build.py +446 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/__init__.py +39 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/charter.py +168 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/curate.py +471 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/dedupe.py +97 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/emit_okf.py +182 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/extract.py +270 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/fulltext.py +215 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/index_vectors.py +82 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/rank.py +154 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/reconcile.py +272 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/review.py +59 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/screen.py +222 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/search.py +320 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/nodes/validate.py +65 -0
- okf_loremaster-0.1.0/src/okf_loremaster/graph/state.py +255 -0
- okf_loremaster-0.1.0/src/okf_loremaster/llm/__init__.py +7 -0
- okf_loremaster-0.1.0/src/okf_loremaster/llm/estimate.py +383 -0
- okf_loremaster-0.1.0/src/okf_loremaster/llm/fake.py +107 -0
- okf_loremaster-0.1.0/src/okf_loremaster/llm/router.py +628 -0
- okf_loremaster-0.1.0/src/okf_loremaster/okf/__init__.py +97 -0
- okf_loremaster-0.1.0/src/okf_loremaster/okf/frontmatter.py +250 -0
- okf_loremaster-0.1.0/src/okf_loremaster/okf/layout.py +177 -0
- okf_loremaster-0.1.0/src/okf_loremaster/okf/markdown.py +49 -0
- okf_loremaster-0.1.0/src/okf_loremaster/okf/reader.py +347 -0
- okf_loremaster-0.1.0/src/okf_loremaster/okf/validate.py +535 -0
- okf_loremaster-0.1.0/src/okf_loremaster/prompts.py +456 -0
- okf_loremaster-0.1.0/src/okf_loremaster/queries.py +368 -0
- okf_loremaster-0.1.0/src/okf_loremaster/ranking.py +419 -0
- okf_loremaster-0.1.0/src/okf_loremaster/recurrence.py +401 -0
- okf_loremaster-0.1.0/src/okf_loremaster/retention.py +107 -0
- okf_loremaster-0.1.0/src/okf_loremaster/review.py +109 -0
- okf_loremaster-0.1.0/src/okf_loremaster/run.py +824 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/__init__.py +175 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/candidates.py +252 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/charter.py +246 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/common.py +253 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/concept.py +462 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/evidence.py +126 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/limits.py +207 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/manifest.py +150 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/parse.py +281 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/recurrence.py +148 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/screening.py +120 -0
- okf_loremaster-0.1.0/src/okf_loremaster/schemas/strength.py +52 -0
- okf_loremaster-0.1.0/src/okf_loremaster/selftest.py +153 -0
- okf_loremaster-0.1.0/src/okf_loremaster/strength.py +323 -0
- okf_loremaster-0.1.0/src/okf_loremaster/ui/__init__.py +7 -0
- okf_loremaster-0.1.0/src/okf_loremaster/ui/jsonl.py +56 -0
- okf_loremaster-0.1.0/src/okf_loremaster/ui/pauses.py +310 -0
- okf_loremaster-0.1.0/src/okf_loremaster/ui/plain.py +235 -0
- okf_loremaster-0.1.0/src/okf_loremaster/ui/review.py +186 -0
- okf_loremaster-0.1.0/src/okf_loremaster/ui/summary.py +186 -0
- okf_loremaster-0.1.0/src/okf_loremaster/ui/tui.py +659 -0
- okf_loremaster-0.1.0/src/okf_loremaster/verification.py +560 -0
- okf_loremaster-0.1.0/tests/conftest.py +134 -0
- okf_loremaster-0.1.0/tests/fake_llm.py +268 -0
- okf_loremaster-0.1.0/tests/fake_ncbi.py +540 -0
- okf_loremaster-0.1.0/tests/fixtures/ncbi.jsonl +8 -0
- okf_loremaster-0.1.0/tests/graph_runs.py +249 -0
- okf_loremaster-0.1.0/tests/test_afce_contract.py +292 -0
- okf_loremaster-0.1.0/tests/test_cassette.py +116 -0
- okf_loremaster-0.1.0/tests/test_cli.py +277 -0
- okf_loremaster-0.1.0/tests/test_clients.py +448 -0
- okf_loremaster-0.1.0/tests/test_config.py +123 -0
- okf_loremaster-0.1.0/tests/test_curation.py +325 -0
- okf_loremaster-0.1.0/tests/test_domain_agnostic.py +255 -0
- okf_loremaster-0.1.0/tests/test_dry_run.py +549 -0
- okf_loremaster-0.1.0/tests/test_emit_okf.py +782 -0
- okf_loremaster-0.1.0/tests/test_events.py +63 -0
- okf_loremaster-0.1.0/tests/test_extraction.py +709 -0
- okf_loremaster-0.1.0/tests/test_http.py +396 -0
- okf_loremaster-0.1.0/tests/test_okf_validate.py +375 -0
- okf_loremaster-0.1.0/tests/test_queries.py +210 -0
- okf_loremaster-0.1.0/tests/test_ranking.py +301 -0
- okf_loremaster-0.1.0/tests/test_recurrence.py +445 -0
- okf_loremaster-0.1.0/tests/test_resume.py +510 -0
- okf_loremaster-0.1.0/tests/test_retention.py +140 -0
- okf_loremaster-0.1.0/tests/test_router_cost.py +490 -0
- okf_loremaster-0.1.0/tests/test_schemas.py +949 -0
- okf_loremaster-0.1.0/tests/test_screening.py +495 -0
- okf_loremaster-0.1.0/tests/test_strength.py +396 -0
- okf_loremaster-0.1.0/tests/test_tui.py +611 -0
- okf_loremaster-0.1.0/tests/test_ui_plain.py +162 -0
- okf_loremaster-0.1.0/tests/test_vectors.py +512 -0
- okf_loremaster-0.1.0/tests/test_verification.py +581 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# OKF Loremaster — copy to .env and fill in. Never put real values in this file.
|
|
2
|
+
# Every variable is read through okf_loremaster.config; nothing is read from os.environ directly.
|
|
3
|
+
|
|
4
|
+
# ---------------------------------------------------------------------------
|
|
5
|
+
# LLM routing (required)
|
|
6
|
+
# ---------------------------------------------------------------------------
|
|
7
|
+
# Model strings are passed verbatim to LiteLLM, so any LiteLLM-supported
|
|
8
|
+
# provider works. Three roles; bind them to whatever tier you like.
|
|
9
|
+
# FAST cheapest and quickest; screening, one abstract at a time
|
|
10
|
+
# BALANCED mid-priced; query planning, curation, and extraction
|
|
11
|
+
# REASONING most capable and most expensive; the charter, and nothing else
|
|
12
|
+
#
|
|
13
|
+
# Extraction sits on BALANCED because it is the only node that makes a call per
|
|
14
|
+
# paper — two hundred of them against every other node's handful — so whatever it
|
|
15
|
+
# is bound to sets the price of a run. What guards the output there is code, not
|
|
16
|
+
# model tier: every number is checked against the source afterward and every quote
|
|
17
|
+
# is sliced out of it. Bind REASONING to something cheaper and you will barely
|
|
18
|
+
# notice; bind BALANCED to something expensive and you will.
|
|
19
|
+
OKF_LOREMASTER_MODEL_FAST=claude-haiku-4-5
|
|
20
|
+
OKF_LOREMASTER_MODEL_BALANCED=claude-sonnet-5
|
|
21
|
+
OKF_LOREMASTER_MODEL_REASONING=claude-opus-5
|
|
22
|
+
|
|
23
|
+
ANTHROPIC_API_KEY=
|
|
24
|
+
# Optional: gateway or Azure-style base URL, e.g.
|
|
25
|
+
# ANTHROPIC_BASE_URL=https://<resource>.services.ai.azure.com/anthropic/
|
|
26
|
+
ANTHROPIC_BASE_URL=
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# Cost accounting
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# LiteLLM's completion_cost() returns 0.0 for models it does not know, which is
|
|
32
|
+
# indistinguishable from a free call. Models reached through a gateway or an
|
|
33
|
+
# Azure deployment name are commonly absent from its price map. Set these to get
|
|
34
|
+
# real USD figures; leave them unset and unpriced calls are reported as tokens
|
|
35
|
+
# with "cost unavailable" — never $0.00.
|
|
36
|
+
# Units: USD per 1M tokens.
|
|
37
|
+
# OKF_LOREMASTER_PRICE_FAST_IN=
|
|
38
|
+
# OKF_LOREMASTER_PRICE_FAST_OUT=
|
|
39
|
+
# OKF_LOREMASTER_PRICE_BALANCED_IN=
|
|
40
|
+
# OKF_LOREMASTER_PRICE_BALANCED_OUT=
|
|
41
|
+
# OKF_LOREMASTER_PRICE_REASONING_IN=
|
|
42
|
+
# OKF_LOREMASTER_PRICE_REASONING_OUT=
|
|
43
|
+
|
|
44
|
+
# Soft budget in USD. The run warns and pauses at this threshold; it does not abort.
|
|
45
|
+
# OKF_LOREMASTER_MAX_USD=5.00
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# Model concurrency and retries
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
# How many calls per tier may be in flight at once. Screening submits every pooled
|
|
51
|
+
# paper at once and these are what meter it, so FAST is the one that matters. The
|
|
52
|
+
# binding limit is usually tokens per minute rather than requests: if a run reports
|
|
53
|
+
# screening calls failing with RateLimitError, lower FAST before anything else.
|
|
54
|
+
# OKF_LOREMASTER_CONCURRENCY_FAST=4
|
|
55
|
+
# BALANCED is what sets a run's wall-clock: extraction is one call per kept paper, so a
|
|
56
|
+
# 200-paper bundle makes 200 of them. At 2 that is hours. Lower it only if extraction
|
|
57
|
+
# starts reporting RateLimitError.
|
|
58
|
+
# OKF_LOREMASTER_CONCURRENCY_BALANCED=3
|
|
59
|
+
# OKF_LOREMASTER_CONCURRENCY_REASONING=3
|
|
60
|
+
|
|
61
|
+
# Attempts per call, not retries on top of the first — warnings count to one less.
|
|
62
|
+
# A rate limit clears on a 60-second window, so this has to be enough to outlast one.
|
|
63
|
+
# OKF_LOREMASTER_MAX_RETRIES=6
|
|
64
|
+
|
|
65
|
+
# Seconds before a single model call is abandoned. An extraction reads 6,000 tokens of
|
|
66
|
+
# source and writes several thousand back, which is minutes rather than seconds; set too
|
|
67
|
+
# low, the call times out on its own success and the paper is lost.
|
|
68
|
+
# OKF_LOREMASTER_REQUEST_TIMEOUT=300
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# NCBI E-utilities
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
# Optional but strongly recommended: raises the rate limit from 3 to 10 req/s.
|
|
74
|
+
# Free from https://account.ncbi.nlm.nih.gov/settings/
|
|
75
|
+
# We run at 2.5 or 8 req/s respectively — under the ceiling on purpose, because
|
|
76
|
+
# the limit is enforced per IP and a shared address may already be carrying
|
|
77
|
+
# traffic we cannot see. E-utilities, BioC and PubTator share one budget.
|
|
78
|
+
OKF_LOREMASTER_NCBI_API_KEY=
|
|
79
|
+
# Required. NCBI asks for a contact address on every request and throttles
|
|
80
|
+
# traffic that omits it. A build refuses to start without it.
|
|
81
|
+
OKF_LOREMASTER_NCBI_EMAIL=
|
|
82
|
+
OKF_LOREMASTER_NCBI_TOOL=okf-loremaster
|
|
83
|
+
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
# HTTP
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# OKF_LOREMASTER_HTTP_TIMEOUT=30
|
|
88
|
+
# OKF_LOREMASTER_HTTP_MAX_RETRIES=4
|
|
89
|
+
# Responses are cached on disk, keyed by the request with credentials stripped,
|
|
90
|
+
# so rotating an API key does not orphan the cache. Bibliographic records are
|
|
91
|
+
# effectively immutable, hence the long default.
|
|
92
|
+
# OKF_LOREMASTER_HTTP_CACHE_ENABLED=true
|
|
93
|
+
# OKF_LOREMASTER_HTTP_CACHE_TTL_DAYS=30
|
|
94
|
+
#
|
|
95
|
+
# A CA bundle to verify TLS against, instead of the default trust store. Needed on
|
|
96
|
+
# a network whose proxy terminates TLS and presents its own certificate: the run
|
|
97
|
+
# then reports a certificate failure against a host that is perfectly healthy, and
|
|
98
|
+
# it can hit some hosts and not others depending on what the proxy allowlists.
|
|
99
|
+
# Ask your IT group for the organization's root CA in PEM form. There is
|
|
100
|
+
# deliberately no option to skip verification — the provenance a bundle claims
|
|
101
|
+
# rests on the bytes having come from who they say they came from.
|
|
102
|
+
# OKF_LOREMASTER_CA_BUNDLE=/path/to/corporate-ca.pem
|
|
103
|
+
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
# Embeddings (only used by the optional vector index)
|
|
106
|
+
# ---------------------------------------------------------------------------
|
|
107
|
+
# Must be a locally-runnable model: downstream consumers reject remote embedders
|
|
108
|
+
# on attach. Pinned by revision so a rebuild reproduces the same vectors.
|
|
109
|
+
# OKF_LOREMASTER_EMBED_MODEL=pritamdeka/S-PubMedBert-MS-MARCO
|
|
110
|
+
# OKF_LOREMASTER_EMBED_REVISION=
|
|
111
|
+
|
|
112
|
+
# Shared Hugging Face cache, so the embedding model downloads once per machine
|
|
113
|
+
# rather than once per environment. Use an absolute path — a literal ~ is not
|
|
114
|
+
# expanded here. Keep it OUTSIDE OneDrive: the hub cache symlinks snapshots/
|
|
115
|
+
# into blobs/, which sync clients mangle.
|
|
116
|
+
HF_HOME=/Users/<you>/.cache/huggingface
|
|
117
|
+
|
|
118
|
+
# ---------------------------------------------------------------------------
|
|
119
|
+
# Review
|
|
120
|
+
# ---------------------------------------------------------------------------
|
|
121
|
+
# Who a `--review` sign-off is attributed to. Recorded in every document as
|
|
122
|
+
# verified: [{by: "human:<id>", ...}], which is what lifts the bundle above the
|
|
123
|
+
# unverified trust tier — so it has to name someone who can be asked about it.
|
|
124
|
+
# Unset falls back to the OS login name; set it when that is a service account
|
|
125
|
+
# or a shared box.
|
|
126
|
+
# OKF_LOREMASTER_REVIEWER_ID=
|
|
127
|
+
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
# Paths
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
# HTTP response cache and run checkpoints. Defaults to a platform cache dir.
|
|
132
|
+
# OKF_LOREMASTER_CACHE_DIR=
|
|
133
|
+
# Where runs are written. Defaults to ./bundles relative to the working dir.
|
|
134
|
+
# Every run lands here: `-o <name>` takes a name, not a path, and it is resolved
|
|
135
|
+
# against this. An absolute -o overrides it.
|
|
136
|
+
# OKF_LOREMASTER_OUTPUT_DIR=
|
|
137
|
+
#
|
|
138
|
+
# How many past runs keep their checkpoints. A build writes 100 to 350 MB of them
|
|
139
|
+
# — the whole state is saved once per node, and by the later ones it holds full
|
|
140
|
+
# texts and extractions — so without this the cache directory grows forever. Each
|
|
141
|
+
# build drops all but this many, newest first; 0 keeps everything. Only affects
|
|
142
|
+
# what `--resume` can still pick up. Bundles are never touched.
|
|
143
|
+
# OKF_LOREMASTER_CHECKPOINT_KEEP_RUNS=5
|
|
144
|
+
#
|
|
145
|
+
# Ceilings in MB on the three things that accumulate. Applied at both ends of a
|
|
146
|
+
# build, oldest entries first, so between builds each store is under its ceiling and
|
|
147
|
+
# only a run in flight is over. Nothing is reclaimed unless you build. 0 turns one
|
|
148
|
+
# off. `okf-loremaster runs` prints each size against its cap.
|
|
149
|
+
#
|
|
150
|
+
# Checkpoints are scratch, and the count above is meant to be what binds: five runs
|
|
151
|
+
# measure around 900 MB, so this is set clear of that rather than quietly cutting how
|
|
152
|
+
# many are resumable. The other two are not scratch — they are keyed by the request
|
|
153
|
+
# and shared by every bundle, which is what makes a rebuild fast and re-reading a
|
|
154
|
+
# paper free — so they sit where an ordinary year of use will not reach them. Raise
|
|
155
|
+
# the last one before the others if you rerun often.
|
|
156
|
+
# OKF_LOREMASTER_CHECKPOINT_MAX_MB=2048
|
|
157
|
+
# OKF_LOREMASTER_HTTP_CACHE_MAX_MB=1024
|
|
158
|
+
# OKF_LOREMASTER_EXTRACTION_CACHE_MAX_MB=512
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
python-version: ["3.11", "3.12"]
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- uses: actions/setup-python@v5
|
|
27
|
+
with:
|
|
28
|
+
python-version: ${{ matrix.python-version }}
|
|
29
|
+
cache: pip
|
|
30
|
+
cache-dependency-path: pyproject.toml
|
|
31
|
+
|
|
32
|
+
# `[all]` rather than the base install: the Chroma and Textual tests
|
|
33
|
+
# `importorskip` their dependency, so a lean install would report them as passing
|
|
34
|
+
# when nothing ran. The torch download is the price of actually covering them.
|
|
35
|
+
- name: Install
|
|
36
|
+
run: pip install -e ".[all,dev]"
|
|
37
|
+
|
|
38
|
+
# `ruff check` only. The project does not use `ruff format` and is not formatted to
|
|
39
|
+
# it — adding the check here would fail on 48 files that are deliberately hand-set.
|
|
40
|
+
- name: Lint
|
|
41
|
+
run: ruff check src/ tests/
|
|
42
|
+
|
|
43
|
+
# Only on the version mypy is configured for. `python_version = "3.11"` in
|
|
44
|
+
# `pyproject.toml` is the point of the check — the floor is what has to typecheck —
|
|
45
|
+
# but under 3.12 mypy reads that interpreter's numpy stubs, which use `type`
|
|
46
|
+
# statements, and rejects 3.12 syntax it was told to treat as 3.11.
|
|
47
|
+
- name: Types
|
|
48
|
+
if: matrix.python-version == '3.11'
|
|
49
|
+
run: mypy src/
|
|
50
|
+
|
|
51
|
+
# No network reachable from the suite by design — conftest blocks the httpx
|
|
52
|
+
# transports outright, so a test that starts calling out fails rather than
|
|
53
|
+
# depending on NCBI being up.
|
|
54
|
+
- name: Tests
|
|
55
|
+
run: pytest -q
|
|
56
|
+
|
|
57
|
+
package:
|
|
58
|
+
name: Build and inspect the distributions
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
steps:
|
|
61
|
+
# The whole history and all tags: `check_dist.py` compares the sdist against
|
|
62
|
+
# `git ls-files`, which needs a real repository rather than an export.
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
|
|
65
|
+
- uses: actions/setup-python@v5
|
|
66
|
+
with:
|
|
67
|
+
python-version: "3.11"
|
|
68
|
+
|
|
69
|
+
- name: Install build tooling
|
|
70
|
+
run: pip install build twine
|
|
71
|
+
|
|
72
|
+
- name: Build
|
|
73
|
+
run: python -m build
|
|
74
|
+
|
|
75
|
+
# Fails if the sdist contains a path git does not track, if any `.env` appears,
|
|
76
|
+
# or if the packaged `.env` template is missing from the wheel.
|
|
77
|
+
- name: Nothing untracked ships
|
|
78
|
+
run: python scripts/check_dist.py dist/
|
|
79
|
+
|
|
80
|
+
- name: Metadata renders on PyPI
|
|
81
|
+
run: twine check --strict dist/*
|
|
82
|
+
|
|
83
|
+
- uses: actions/upload-artifact@v4
|
|
84
|
+
with:
|
|
85
|
+
name: distributions
|
|
86
|
+
path: dist/
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Publishing is driven by the tag, and the tag alone decides where it lands:
|
|
2
|
+
#
|
|
3
|
+
# v0.1.0rc1, v0.1.0a2, v0.1.0b1 → TestPyPI (rehearsal; the page can be looked at)
|
|
4
|
+
# v0.1.0 → PyPI (permanent; a version is never reusable)
|
|
5
|
+
#
|
|
6
|
+
# Uploads use PyPI Trusted Publishing (OpenID Connect), so there is no API token in the
|
|
7
|
+
# repository to leak or rotate. Each index needs a publisher registered once, against this
|
|
8
|
+
# repository, this workflow filename, and the environment named below.
|
|
9
|
+
name: Release
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
tags: ["v*"]
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
build:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
outputs:
|
|
23
|
+
prerelease: ${{ steps.classify.outputs.prerelease }}
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.11"
|
|
30
|
+
|
|
31
|
+
- name: Install build tooling
|
|
32
|
+
run: pip install build twine
|
|
33
|
+
|
|
34
|
+
# The version is read from `src/okf_loremaster/__init__.py`, so a tag that disagrees
|
|
35
|
+
# with it would publish something named differently from what the CLI reports.
|
|
36
|
+
- name: Tag matches the package version
|
|
37
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
38
|
+
run: |
|
|
39
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
40
|
+
pkg="$(python -c 'import re,pathlib; print(re.search(r"__version__ = \"([^\"]+)\"", pathlib.Path("src/okf_loremaster/__init__.py").read_text()).group(1))')"
|
|
41
|
+
if [ "$tag" != "$pkg" ]; then
|
|
42
|
+
echo "tag $GITHUB_REF_NAME says $tag, __version__ says $pkg — retag or bump" >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
echo "both say $pkg"
|
|
46
|
+
|
|
47
|
+
- name: Classify the tag
|
|
48
|
+
id: classify
|
|
49
|
+
run: |
|
|
50
|
+
if printf '%s' "${GITHUB_REF_NAME}" | grep -qE '(a|b|rc)[0-9]+$'; then
|
|
51
|
+
echo "prerelease=true" >> "$GITHUB_OUTPUT"
|
|
52
|
+
echo "${GITHUB_REF_NAME} is a pre-release — TestPyPI"
|
|
53
|
+
else
|
|
54
|
+
echo "prerelease=false" >> "$GITHUB_OUTPUT"
|
|
55
|
+
echo "${GITHUB_REF_NAME} is a final release — PyPI"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
- name: Build
|
|
59
|
+
run: python -m build
|
|
60
|
+
|
|
61
|
+
- name: Nothing untracked ships
|
|
62
|
+
run: python scripts/check_dist.py dist/
|
|
63
|
+
|
|
64
|
+
- name: Metadata renders on PyPI
|
|
65
|
+
run: twine check --strict dist/*
|
|
66
|
+
|
|
67
|
+
- uses: actions/upload-artifact@v4
|
|
68
|
+
with:
|
|
69
|
+
name: distributions
|
|
70
|
+
path: dist/
|
|
71
|
+
|
|
72
|
+
# Installs the built wheel in a clean environment with no checkout on the path, which is
|
|
73
|
+
# the only way to catch a file that the repository has and the wheel does not. `init` is
|
|
74
|
+
# the command that fails that way: it needs the packaged .env template.
|
|
75
|
+
smoke-test:
|
|
76
|
+
needs: build
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
strategy:
|
|
79
|
+
matrix:
|
|
80
|
+
python-version: ["3.11", "3.12"]
|
|
81
|
+
steps:
|
|
82
|
+
- uses: actions/setup-python@v5
|
|
83
|
+
with:
|
|
84
|
+
python-version: ${{ matrix.python-version }}
|
|
85
|
+
|
|
86
|
+
- uses: actions/download-artifact@v4
|
|
87
|
+
with:
|
|
88
|
+
name: distributions
|
|
89
|
+
path: dist/
|
|
90
|
+
|
|
91
|
+
- name: Install the wheel alone
|
|
92
|
+
run: pip install dist/*.whl
|
|
93
|
+
|
|
94
|
+
- name: It runs, and writes a template from somewhere other than a checkout
|
|
95
|
+
run: |
|
|
96
|
+
mkdir -p /tmp/elsewhere && cd /tmp/elsewhere
|
|
97
|
+
okf-loremaster --version
|
|
98
|
+
okf-loremaster --help > /dev/null
|
|
99
|
+
okf-loremaster init || true
|
|
100
|
+
test -s .env || { echo "init wrote no .env — the packaged template is missing" >&2; exit 1; }
|
|
101
|
+
grep -q '^ANTHROPIC_API_KEY=$' .env || { echo ".env template is not blank" >&2; exit 1; }
|
|
102
|
+
echo "init wrote a blank .env from the packaged template"
|
|
103
|
+
|
|
104
|
+
testpypi:
|
|
105
|
+
needs: [build, smoke-test]
|
|
106
|
+
if: needs.build.outputs.prerelease == 'true'
|
|
107
|
+
runs-on: ubuntu-latest
|
|
108
|
+
environment:
|
|
109
|
+
name: testpypi
|
|
110
|
+
url: https://test.pypi.org/p/okf-loremaster
|
|
111
|
+
permissions:
|
|
112
|
+
id-token: write
|
|
113
|
+
steps:
|
|
114
|
+
- uses: actions/download-artifact@v4
|
|
115
|
+
with:
|
|
116
|
+
name: distributions
|
|
117
|
+
path: dist/
|
|
118
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
119
|
+
with:
|
|
120
|
+
repository-url: https://test.pypi.org/legacy/
|
|
121
|
+
|
|
122
|
+
pypi:
|
|
123
|
+
needs: [build, smoke-test]
|
|
124
|
+
if: needs.build.outputs.prerelease == 'false' && startsWith(github.ref, 'refs/tags/')
|
|
125
|
+
runs-on: ubuntu-latest
|
|
126
|
+
environment:
|
|
127
|
+
name: pypi
|
|
128
|
+
url: https://pypi.org/p/okf-loremaster
|
|
129
|
+
permissions:
|
|
130
|
+
id-token: write
|
|
131
|
+
steps:
|
|
132
|
+
- uses: actions/download-artifact@v4
|
|
133
|
+
with:
|
|
134
|
+
name: distributions
|
|
135
|
+
path: dist/
|
|
136
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
137
|
+
|
|
138
|
+
# Last, so a GitHub release only ever describes something that actually published.
|
|
139
|
+
github-release:
|
|
140
|
+
needs: pypi
|
|
141
|
+
runs-on: ubuntu-latest
|
|
142
|
+
permissions:
|
|
143
|
+
contents: write
|
|
144
|
+
steps:
|
|
145
|
+
- uses: actions/download-artifact@v4
|
|
146
|
+
with:
|
|
147
|
+
name: distributions
|
|
148
|
+
path: dist/
|
|
149
|
+
- uses: softprops/action-gh-release@v2
|
|
150
|
+
with:
|
|
151
|
+
files: dist/*
|
|
152
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Secrets. `.env.local` and `.env.production` matched neither of the first two
|
|
2
|
+
# patterns — `.env` is exact and `*.env` needs something before the dot — so the third
|
|
3
|
+
# is the one that closes it. The exception has to come last to survive them all.
|
|
4
|
+
.env
|
|
5
|
+
*.env
|
|
6
|
+
.env.*
|
|
7
|
+
!.env.example
|
|
8
|
+
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
*.egg-info/
|
|
15
|
+
build/
|
|
16
|
+
dist/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
|
|
20
|
+
# Build outputs: bundles are regenerable and large.
|
|
21
|
+
bundles/
|
|
22
|
+
*.chroma/
|
|
23
|
+
.okf-loremaster-cache/
|
|
24
|
+
|
|
25
|
+
# Run artifacts. Checkpoints normally live under the cache directory, outside the
|
|
26
|
+
# repo — these catch a cache dir pointed back inside it.
|
|
27
|
+
*.sqlite
|
|
28
|
+
*.sqlite-journal
|
|
29
|
+
|
|
30
|
+
# A charter is one run's input, not the package's. Anchored to the root so a
|
|
31
|
+
# fixture or example charter under tests/ still ships.
|
|
32
|
+
/charter.yaml
|
|
33
|
+
/*.charter.yaml
|
|
34
|
+
|
|
35
|
+
# Internal working notes: the agent operating manual and the datestamped build log.
|
|
36
|
+
# Kept local — they are about how this was built, not about how it is used.
|
|
37
|
+
/CLAUDE.md
|
|
38
|
+
/Build_Progress.md
|
|
39
|
+
|
|
40
|
+
# Agent tooling state: local permissions, session files, transcripts. Machine- and
|
|
41
|
+
# operator-specific, and none of it describes the package.
|
|
42
|
+
.claude/
|
|
43
|
+
|
|
44
|
+
# A virtualenv inside the repo is against policy — the conda env is kept outside so it
|
|
45
|
+
# does not bloat cloud sync — but a stray one committed is thousands of files, so it is
|
|
46
|
+
# ignored rather than trusted not to appear.
|
|
47
|
+
.venv/
|
|
48
|
+
venv/
|
|
49
|
+
|
|
50
|
+
.DS_Store
|
|
51
|
+
.vscode/
|
|
52
|
+
.idea/
|
|
53
|
+
.ipynb_checkpoints/
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to OKF Loremaster. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
4
|
+
versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
|
|
6
|
+
Version 0.x means the OKF bundle layout and the CLI may still change between minor releases.
|
|
7
|
+
The bundle contract downstream reads is the part to watch — it is called out here when it moves.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
## [0.1.0] — 2026-08-14
|
|
12
|
+
|
|
13
|
+
First public release.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Build a task-scoped biomedical literature bundle from PubMed/PMC in Open Knowledge Format
|
|
18
|
+
v0.2, from a plain-language research question: `okf-loremaster build "..."`.
|
|
19
|
+
- A thirteen-node graph — `charter → search → dedupe → rank → screen → curate → fulltext →
|
|
20
|
+
extract → reconcile → review → emit_okf → validate → index_vectors`. Five agents make the
|
|
21
|
+
judgment calls; HTTP, dedup, ranking, MMR, license logic, validation and indexing are code.
|
|
22
|
+
- Per-paper markdown carrying predictor rows with effect sizes, operationalization, timing,
|
|
23
|
+
evidence type, null findings and vocabulary hints, plus a `predictors.md` index that points
|
|
24
|
+
into them and a `search.md` that reproduces the search.
|
|
25
|
+
- Deterministic numeric verification: an effect the source text does not contain becomes
|
|
26
|
+
`effect=None` with a downgraded confidence and a logged warning, and the run continues.
|
|
27
|
+
- A computed `strength` per row from study design, adjustment and sample-size scale — never
|
|
28
|
+
asked of the model.
|
|
29
|
+
- Optional Chroma vector index over the finished bundle (`[vectors]`), built by walking what
|
|
30
|
+
was emitted rather than by a second extraction pass.
|
|
31
|
+
- Resume from a checkpoint (`--resume`), a `runs` listing, a Textual TUI (`[tui]`), JSONL
|
|
32
|
+
event output, and cost reporting that says "cost unavailable" rather than `$0.00` for a
|
|
33
|
+
model LiteLLM cannot price.
|
|
34
|
+
- `okf-loremaster init` writes an annotated `.env` and reports what is still unset.
|
|
35
|
+
|
|
36
|
+
### Notes
|
|
37
|
+
|
|
38
|
+
- Python 3.11 and 3.12.
|
|
39
|
+
- NCBI access is E-utilities, BioC, PubTator and iCite only; no page is ever scraped.
|
|
40
|
+
- Apache-2.0 covers this code, not the bundles it builds. Each emitted document records the
|
|
41
|
+
`license` its publisher reported, and `export_safe` says whether it may leave.
|
|
42
|
+
|
|
43
|
+
[Unreleased]: https://github.com/PFreda-Lab/okf-loremaster/compare/v0.1.0...HEAD
|
|
44
|
+
[0.1.0]: https://github.com/PFreda-Lab/okf-loremaster/releases/tag/v0.1.0
|