Skip to content

Commit 0fe7f1d

Browse files
Merge branch 'dev'
2 parents 3e32476 + 6feb5f1 commit 0fe7f1d

24 files changed

Lines changed: 2461 additions & 204 deletions

ARCHITECTURE.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ wdoc/
3636
│ ├── logseq_markdown.py # Logseq notes
3737
│ ├── word.py # Word documents
3838
│ ├── powerpoint.py # PowerPoint
39-
│ └── json_dict.py # JSON entries
39+
│ ├── json_dict.py # JSON entries
40+
│ ├── zotero.py # Zotero API helpers (used by parse_zotero)
41+
│ └── karakeep.py # Karakeep API helpers (used by parse_karakeep)
4042
├── tasks/ # Task implementations
4143
│ ├── types.py # wdocTask enum
4244
│ ├── query.py # Query: retrieve → evaluate → answer → combine
@@ -95,7 +97,7 @@ wdoc.__init__() ── Configuration & validation
9597

9698
All models are loaded through **LiteLLM**, supporting 100+ providers (OpenAI, Anthropic, Google, Mistral, Ollama, OpenRouter, etc.).
9799

98-
The defaults are duplicated across `wdoc/utils/env.py`, the docs, `README.md`, `SKILL.md`, this file, and `docker/env.example`. Use `./bump_default_models.sh` at the repo root to change them in one shot (dry-run by default, `--apply` to write).
100+
The defaults are duplicated across `wdoc/utils/env.py`, the docs, `README.md`, `wdoc-skill/REFERENCE.md`, this file, and `docker/env.example`. Use `./bump_default_models.sh` at the repo root to change them in one shot (dry-run by default, `--apply` to write).
99101

100102
---
101103

@@ -138,7 +140,7 @@ Key variables:
138140

139141
Each filetype has a dedicated loader in `utils/loaders/`. The dispatcher (`load_one_doc()`) dynamically imports the `load_{filetype}` function.
140142

141-
**Supported types**: `pdf`, `txt`, `word`, `powerpoint`, `epub`, `url`, `youtube`, `youtube_playlist`, `online_pdf`, `online_media`, `local_audio`, `local_video`, `anki`, `logseq_markdown`, `json_dict`, `recursive_paths`, `json_entries`, `toml_entries`, `string`, `ddg` (DuckDuckGo search).
143+
**Supported types**: `pdf`, `txt`, `word`, `powerpoint`, `epub`, `url`, `youtube`, `youtube_playlist`, `online_pdf`, `online_media`, `local_audio`, `local_video`, `anki`, `logseq_markdown`, `json_dict`, `recursive_paths`, `json_entries`, `toml_entries`, `string`, `ddg` (DuckDuckGo search), `zotero`, `karakeep`.
142144

143145
**PDF parsing** is particularly robust: 15 different parser backends are evaluated and the best result is selected automatically.
144146

CLAUDE.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Each persona can be customized via `WDOC_{NAME}_INSTRUCTIONS` env vars.
6666
Any new setting (CLI argument or environment variable) **must** be documented in:
6767
- `wdoc/docs/help.md` — describe the setting, its type, default value, and accepted values.
6868
- `wdoc/docs/examples.md` — add usage examples where appropriate.
69+
- `wdoc-skill/REFERENCE.md`: the comprehensive user-facing reference; mirror the new setting in the relevant table (see [Keeping the `wdoc-skill/` reference in Sync](#keeping-the-wdoc-skill-reference-in-sync)).
6970

7071
New settings should be either:
7172
- A **CLI argument** (defined in `wdoc.py`'s main class), or
@@ -82,15 +83,21 @@ When adding new CLI arguments or loader-specific parameters, update these dicts
8283

8384
## Adding Support for a New Filetype
8485

86+
Adding a filetype is intentionally self-contained: it touches a new loader module plus a couple of registration points, never the core RAG pipeline. The recent `zotero` and `karakeep` types are good references for the two routes available:
87+
- **Standalone loader** (most types): write a `load_*` function that returns `List[Document]` and register it (steps below).
88+
- **Recursive fan-out** (`zotero`, `karakeep`, `recursive_paths`, ...): resolve a selector/path into many sub-documents and delegate parsing to *existing* loaders via DocDicts, so you reuse the existing parsers instead of writing new parsing code. Register the function in `recursive_types_func_mapping` in `wdoc/utils/load_recursive.py`.
89+
90+
Standalone loader steps:
91+
8592
1. **Create a loader** in `wdoc/utils/loaders/` — add a file (e.g. `myformat.py`) containing a function `load_myformat(path, file_hash, ...) -> List[Document]`. Use the `@debug_return_empty` and `@optional_strip_unexp_args` decorators (see `txt.py` for a minimal example).
8693
2. **Register the filetype** — add `"myformat"` to the `LOADABLE_FILETYPE` list in `wdoc/utils/loaders/__init__.py`.
8794
3. **Add loader-specific args** (if any) to `filetype_arg_types` in `wdoc/utils/misc.py`.
88-
4. **Document it** — add the filetype and its arguments to `wdoc/docs/help.md`, and add examples to `wdoc/docs/examples.md`.
95+
4. **Document it** — add the filetype and its arguments to `wdoc/docs/help.md`, add examples to `wdoc/docs/examples.md`, add the filetype + its loader args to the tables in `wdoc-skill/REFERENCE.md`, and a shell example to `wdoc-skill/EXAMPLES.md` (see [Keeping the `wdoc-skill/` reference in Sync](#keeping-the-wdoc-skill-reference-in-sync)).
8996
5. **Auto-detection** (optional) — if the filetype corresponds to a file extension, add a mapping in the auto-detection logic so `--filetype=auto` can infer it.
9097

9198
## Bumping the Default Models
9299

93-
The two default LLM identifiers (`WDOC_DEFAULT_MODEL` and `WDOC_DEFAULT_QUERY_EVAL_MODEL`) are duplicated across `wdoc/utils/env.py`, `wdoc/docs/help.md`, `SKILL.md`, `README.md`, `ARCHITECTURE.md`, and `docker/env.example`. Use the repo-root helper instead of editing each file by hand:
100+
The two default LLM identifiers (`WDOC_DEFAULT_MODEL` and `WDOC_DEFAULT_QUERY_EVAL_MODEL`) are duplicated across `wdoc/utils/env.py`, `wdoc/docs/help.md`, `wdoc-skill/REFERENCE.md`, `README.md`, `ARCHITECTURE.md`, and `docker/env.example`. Use the repo-root helper instead of editing each file by hand:
94101

95102
```bash
96103
./bump_default_models.sh <NEW_STRONG_MODEL> <NEW_EVAL_MODEL> # dry-run preview
@@ -99,6 +106,22 @@ The two default LLM identifiers (`WDOC_DEFAULT_MODEL` and `WDOC_DEFAULT_QUERY_EV
99106

100107
`env.py` is the source of truth for the current values. The script replaces both the full id (`provider/path/name`) and the basename, and re-syncs the `KEY=VALUE` lines in `docker/env.example` independently (so it recovers from prior drift). It never commits; review with `git diff` and commit yourself.
101108

109+
## Keeping the `wdoc-skill/` reference in Sync
110+
111+
The user-facing reference lives in the `wdoc-skill/` directory (a Claude Code skill), split into three files:
112+
- `wdoc-skill/SKILL.md`: concise orientation (frontmatter with trigger description, quick start, the four tasks, core mechanics). Keep it short.
113+
- `wdoc-skill/REFERENCE.md`: the comprehensive tables (Installation, all CLI argument groups, Filetypes, Recursive Filetypes, Loader-Specific Arguments, Filtering, Environment Variables, and the full Python API).
114+
- `wdoc-skill/EXAMPLES.md`: copy-pasteable shell and Python examples.
115+
116+
These are **not** auto-generated, so they drift unless updated by hand. Whenever you change user-visible behavior, keep them in sync:
117+
- New CLI argument, env var, or loader-specific argument: add it to the relevant table in `REFERENCE.md` (Global/Model/Embedding/Query/Summary Arguments, Environment Variables, or Loader-Specific Arguments).
118+
- New filetype: add a row to the Filetypes or Recursive Filetypes table in `REFERENCE.md`, plus its loader args, and ideally a shell example in `EXAMPLES.md`.
119+
- Changed default, accepted values, or behavior of an existing setting: update the matching row in `REFERENCE.md`.
120+
- New install extra: update the Installation section in `REFERENCE.md` (and the quick start in `SKILL.md` if relevant).
121+
- On a version bump, update the `> Written for **wdoc vX.Y.Z**` header line at the top of all three files.
122+
123+
Do not use em-dashes anywhere in these files. `wdoc/docs/help.md` and `wdoc/docs/examples.md` are the source the reference mirrors; if you edited those, mirror the change here too.
124+
102125
## Building Documentation
103126

104127
To regenerate the Sphinx autodoc files, run from the repo root:

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Created by a psychiatry resident who needed a way to get a definitive answer fro
3535
* **Web Search**: Preliminary web search support using [DuckDuckGo](https://en.wikipedia.org/wiki/DuckDuckGo) (via the [ddgs](https://pypi.org/project/ddgs/) library)
3636

3737
### Table of contents
38-
- [Comprehensive reference (SKILL.md)](#comprehensive-reference)
38+
- [Comprehensive reference (wdoc-skill/)](#comprehensive-reference)
3939
- [Explanatory diagrams](#explanatory-diagrams)
4040
- [Ultra short guide for people in a hurry](#ultra-short-guide-for-people-in-a-hurry)
4141
- [Features](#features)
@@ -51,7 +51,7 @@ Created by a psychiatry resident who needed a way to get a definitive answer fro
5151

5252
## Comprehensive reference
5353

54-
A single-page comprehensive reference covering every CLI argument, environment variable, filetype, and the full Python API can be found in **[SKILL.md](./SKILL.md)**.
54+
A comprehensive reference covering every CLI argument, environment variable, filetype, and the full Python API lives in the **[`wdoc-skill/`](./wdoc-skill/)** directory, packaged as a Claude Code skill. It has three files: [`SKILL.md`](./wdoc-skill/SKILL.md) (a quick orientation), [`REFERENCE.md`](./wdoc-skill/REFERENCE.md) (the full argument, env var, filetype, and Python API tables), and [`EXAMPLES.md`](./wdoc-skill/EXAMPLES.md) (copy-pasteable shell and Python recipes).
5555

5656
## Explanatory diagrams
5757

@@ -182,6 +182,8 @@ uvx wdoc[full] --path=$link --task=summarize --filetype="online_pdf"
182182
* **recursive_paths**: turns a path, a regex pattern and a filetype into all the files found recurisvely, and treated a the specified filetype (for example many PDFs or lots of HTML files etc).
183183
* **toml_entries**: read a .toml file. An example can be found in `docs/toml_entries_example.toml`.
184184
* **youtube playlists**: get the link for each video then process as **youtube**
185+
* **zotero**: load documents straight from a [Zotero](https://www.zotero.org/) library via [pyzotero](https://github.com/urschrei/pyzotero). The `--path` selects a collection, tag, item keys, saved search or the whole library, and each attachment is fed to the regular loaders. Requires the `zotero` extra. See [help.md](https://github.com/thiswillbeyourgithub/wdoc/docs/help.md) for the selector syntax and options.
186+
* **karakeep**: load bookmarks straight from a [Karakeep](https://karakeep.app/) instance via [karakeep-python-api](https://github.com/thiswillbeyourgithub/karakeep_python_api). The `--path` selects a list, tag, search, bookmark ids or the whole library, and each bookmark's stored content (crawled html, text, or a stored pdf asset) is fed to the regular loaders without re-fetching the live url. Requires the `karakeep` extra. See [help.md](https://github.com/thiswillbeyourgithub/wdoc/docs/help.md) for the selector syntax and options.
185187

186188
## Walkthrough and examples
187189

@@ -208,9 +210,12 @@ Refer to [examples.md](https://github.com/thiswillbeyourgithub/wdoc/blob/main/wd
208210
* `wdoc[full]` is a shortcut that includes all the loader extras above (excluding `fasttext` and `pdftotext`, which need special handling). If unsure, use `wdoc[full]` and don't worry about filetypes.
209211
* If you have problems with pdftotext or fasttext, try `uvx wdoc[full,pdftotext,fasttext]`.
210212
* If you plan on contributing, you will also need `wdoc[dev]` for the commit hooks.
211-
* **Claude Code users**: to give Claude Code knowledge of `wdoc`'s CLI and Python API, install the [SKILL.md](./SKILL.md) reference file:
213+
* **Claude Code users**: to give Claude Code knowledge of `wdoc`'s CLI and Python API, install the [`wdoc-skill/`](./wdoc-skill/) skill (three files: `SKILL.md`, `REFERENCE.md`, `EXAMPLES.md`):
212214
```bash
213-
mkdir -p ~/.claude/skills/wdoc && wget -O ~/.claude/skills/wdoc/SKILL.md https://raw.githubusercontent.com/thiswillbeyourgithub/wdoc/main/SKILL.md
215+
mkdir -p ~/.claude/skills/wdoc-skill
216+
for f in SKILL.md REFERENCE.md EXAMPLES.md; do
217+
wget -O ~/.claude/skills/wdoc-skill/$f https://raw.githubusercontent.com/thiswillbeyourgithub/wdoc/main/wdoc-skill/$f
218+
done
214219
```
215220
2. Add the API key for the backend you want as an environment variable: for example `export ANTHROPIC_API_KEY="***my_key***"`
216221
3. Launch is as easy as using `uvx wdoc --task=query --path=MYDOC [ARGS]`

bump_default_models.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ usage() {
3838
Usage: bump_default_models.sh <NEW_STRONG_MODEL> <NEW_EVAL_MODEL> [--apply]
3939
4040
Reads current defaults from wdoc/utils/env.py and replaces them in:
41-
wdoc/utils/env.py, wdoc/docs/help.md, SKILL.md, README.md, ARCHITECTURE.md
41+
wdoc/utils/env.py, wdoc/docs/help.md, wdoc-skill/REFERENCE.md, README.md, ARCHITECTURE.md
4242
4343
Both the full id ("provider/.../name") and the basename are replaced.
4444
A post-flight scan warns about any leftover mention of the old names or
@@ -124,7 +124,7 @@ echo
124124
FILES=(
125125
"wdoc/utils/env.py"
126126
"wdoc/docs/help.md"
127-
"SKILL.md"
127+
"wdoc-skill/REFERENCE.md"
128128
"README.md"
129129
"ARCHITECTURE.md"
130130
)

bumpver.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
[bumpver]
3-
current_version = "5.1.0"
3+
current_version = "5.1.1"
44
version_pattern = "MAJOR.MINOR.PATCH"
55
commit_message = "bump version {old_version} -> {new_version}"
66
tag_message = "{new_version}"

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def setup(app):
3838
project = "wdoc"
3939
copyright = "2025, thiswillbeyourgithub"
4040
author = "thiswillbeyourgithub"
41-
release = "5.1.0"
41+
release = "5.1.1"
4242

4343
# -- General configuration ---------------------------------------------------
4444
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

setup.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def run(self):
129129

130130
setup(
131131
name="wdoc",
132-
version="5.1.0",
132+
version="5.1.1",
133133
description="A perfect AI powered RAG for document query and summary. Supports ~all LLM and ~all filetypes (url, pdf, epub, youtube (incl playlist), audio, anki, md, docx, pptx, or any combination!)",
134134
long_description=long_description,
135135
long_description_content_type="text/markdown",
@@ -205,7 +205,7 @@ def run(self):
205205
"blake3>=1.0.8", # faster than sha256
206206
"pandas >= 2.3.3",
207207
"trio >= 0.31.0", # for some reason older versions of trio, when present are used and cause issues on python 3.11: https://github.com/python-trio/trio/issues/2317
208-
"unstructured >= 0.18.15", # base package only, used by pdf loader for clean_extra_whitespace. The heavy [all-docs] extra is in [office].
208+
"unstructured >= 0.18.15,<0.18.31", # base package only, used by pdf loader for clean_extra_whitespace. The heavy [all-docs] extra is in [office].
209209
# PDF loading (default, since pdf is the most common filetype)
210210
"openparse[ml] >= 0.5.7", # pdf with table support
211211
"pdfminer.six >= 20231228",
@@ -261,9 +261,17 @@ def run(self):
261261
# sudo apt install build-essential libpoppler-cpp-dev pkg-config python3-dev
262262
"pdftotext >= 2.2.2",
263263
],
264+
"zotero": [
265+
# load documents straight from a Zotero library (local API or web API)
266+
"pyzotero >= 1.6.0",
267+
],
268+
"karakeep": [
269+
# load bookmarks straight from a Karakeep instance via its API
270+
"karakeep-python-api >= 1.8.0",
271+
],
264272
"full": [
265273
# aggregates all loader extras (self-reference requires pip >= 21.2)
266-
"wdoc[youtube,audio,anki,office,logseq]",
274+
"wdoc[youtube,audio,anki,office,logseq,zotero,karakeep]",
267275
],
268276
"dev": [
269277
"ruff >= 0.14.1",

tests/run_all_tests.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ _ALL_TEST_MODELS=(
1515
"${WDOC_TEST_DEFAULT_MODEL:-openrouter/deepseek/deepseek-v4-pro}"
1616
"${WDOC_TEST_DEFAULT_EVAL_MODEL:-openrouter/deepseek/deepseek-v4-flash}"
1717
"${WDOC_TEST_DEFAULT_EMBED_MODEL:-openai/text-embedding-3-small}"
18+
"${WDOC_TEST_MISTRAL_EMBED_MODEL:-mistral/mistral-embed}"
1819
)
1920
_check_provider_key() {
2021
local prefix="$1" keyvar="$2" m
@@ -35,6 +36,28 @@ if [[ -z "${WDOC_WHISPER_API_KEY:-}" ]]; then
3536
echo "ERROR: WDOC_WHISPER_API_KEY env var is not set but is required to test whisper transcription. Set WDOC_WHISPER_API_KEY before running tests." >&2
3637
exit 1
3738
fi
39+
# Zotero loader: the api test needs real credentials (or a reachable local
40+
# Zotero) plus a small selector to fan out. Mirror the whisper check so a
41+
# missing setup crashes early instead of silently skipping the test.
42+
if [[ -z "${ZOTERO_API_KEY:-}" ]]; then
43+
echo "ERROR: ZOTERO_API_KEY env var is not set but is required to test the zotero loader. Set ZOTERO_API_KEY (and ZOTERO_LIBRARY_ID) before running tests." >&2
44+
exit 1
45+
fi
46+
if [[ -z "${ZOTERO_COLLECTION_NAME:-}" ]]; then
47+
echo "ERROR: ZOTERO_COLLECTION_NAME env var is not set but is required to exercise the zotero loader api test. Set it to the name of a (preferably small) collection in your library." >&2
48+
exit 1
49+
fi
50+
# Karakeep loader: the api test creates its own temporary bookmark and deletes
51+
# it, so it only needs real credentials (endpoint + api key). Mirror the zotero
52+
# check so a missing setup crashes early instead of silently skipping the test.
53+
if [[ -z "${KARAKEEP_PYTHON_API_KEY:-}" ]]; then
54+
echo "ERROR: KARAKEEP_PYTHON_API_KEY env var is not set but is required to test the karakeep loader. Set KARAKEEP_PYTHON_API_KEY (and KARAKEEP_PYTHON_API_ENDPOINT) before running tests." >&2
55+
exit 1
56+
fi
57+
if [[ -z "${KARAKEEP_PYTHON_API_ENDPOINT:-}" ]]; then
58+
echo "ERROR: KARAKEEP_PYTHON_API_ENDPOINT env var is not set but is required to test the karakeep loader. Set it to your instance URL including /api/v1/." >&2
59+
exit 1
60+
fi
3861

3962
# cleanup previous
4063
[[ "$(type deactivate)" == "deactivate is a shell function"* ]] && deactivate
@@ -70,6 +93,22 @@ echo "\nTesting wdoc (basic)"
7093
$PYTHON_EXEC -m pytest -n auto --disable-warnings --show-capture=no --code-highlight=yes --tb=short -m basic ../test_wdoc.py
7194
echo "Done with wdoc (basic)"
7295

96+
echo "\nTesting zotero (basic)"
97+
$PYTHON_EXEC -m pytest -n auto --disable-warnings --show-capture=no --code-highlight=yes --tb=short -m basic ../test_zotero.py
98+
echo "Done with zotero (basic)"
99+
100+
echo "\nTesting zotero (api)"
101+
$PYTHON_EXEC -m pytest --disable-warnings --show-capture=no --code-highlight=yes --tb=short -m api ../test_zotero.py
102+
echo "Done with zotero (api)"
103+
104+
echo "\nTesting karakeep (basic)"
105+
$PYTHON_EXEC -m pytest -n auto --disable-warnings --show-capture=no --code-highlight=yes --tb=short -m basic ../test_karakeep.py
106+
echo "Done with karakeep (basic)"
107+
108+
echo "\nTesting karakeep (api)"
109+
$PYTHON_EXEC -m pytest --disable-warnings --show-capture=no --code-highlight=yes --tb=short -m api ../test_karakeep.py
110+
echo "Done with karakeep (api)"
111+
73112
echo "\nTesting vectorstores (api)"
74113
$PYTHON_EXEC -m pytest --disable-warnings --show-capture=no --code-highlight=yes --tb=short -m api ../test_vectorstores.py
75114
echo "Done with vectorstores (api)"

0 commit comments

Comments
 (0)