beanhand 0.7.2__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.
Files changed (83) hide show
  1. beanhand-0.7.2/AGENTS.md +111 -0
  2. beanhand-0.7.2/COPYING +674 -0
  3. beanhand-0.7.2/MANIFEST.in +10 -0
  4. beanhand-0.7.2/Makefile +41 -0
  5. beanhand-0.7.2/PKG-INFO +368 -0
  6. beanhand-0.7.2/README.md +351 -0
  7. beanhand-0.7.2/beanhand/__init__.py +0 -0
  8. beanhand-0.7.2/beanhand/client/__init__.py +0 -0
  9. beanhand-0.7.2/beanhand/client/beancount_loader.py +574 -0
  10. beanhand-0.7.2/beanhand/client/beanfiles.py +507 -0
  11. beanhand-0.7.2/beanhand/client/cli.py +84 -0
  12. beanhand-0.7.2/beanhand/client/commands/__init__.py +0 -0
  13. beanhand-0.7.2/beanhand/client/commands/associate.py +384 -0
  14. beanhand-0.7.2/beanhand/client/commands/fetch.py +24 -0
  15. beanhand-0.7.2/beanhand/client/commands/importcmd.py +218 -0
  16. beanhand-0.7.2/beanhand/client/commands/ingest.py +171 -0
  17. beanhand-0.7.2/beanhand/client/commands/listaccts.py +30 -0
  18. beanhand-0.7.2/beanhand/client/commands/listcmds.py +38 -0
  19. beanhand-0.7.2/beanhand/client/commands/organize.py +64 -0
  20. beanhand-0.7.2/beanhand/client/commands/process.py +39 -0
  21. beanhand-0.7.2/beanhand/client/commands/refine.py +446 -0
  22. beanhand-0.7.2/beanhand/client/commands/remove.py +21 -0
  23. beanhand-0.7.2/beanhand/client/config.py +180 -0
  24. beanhand-0.7.2/beanhand/client/display.py +13 -0
  25. beanhand-0.7.2/beanhand/client/server.py +230 -0
  26. beanhand-0.7.2/beanhand/server/RECEIPT_CONVERSION_PROMPT.md +52 -0
  27. beanhand-0.7.2/beanhand/server/RECEIPT_INFO_PROMPT.md +5 -0
  28. beanhand-0.7.2/beanhand/server/RECEIPT_MATCH_PROMPT.md +20 -0
  29. beanhand-0.7.2/beanhand/server/TRANSACTION_REFINEMENT_PROMPT.md +94 -0
  30. beanhand-0.7.2/beanhand/server/__init__.py +0 -0
  31. beanhand-0.7.2/beanhand/server/cli.py +79 -0
  32. beanhand-0.7.2/beanhand/server/commands/__init__.py +0 -0
  33. beanhand-0.7.2/beanhand/server/commands/associate.py +133 -0
  34. beanhand-0.7.2/beanhand/server/commands/fetch.py +51 -0
  35. beanhand-0.7.2/beanhand/server/commands/listcmds.py +70 -0
  36. beanhand-0.7.2/beanhand/server/commands/process.py +107 -0
  37. beanhand-0.7.2/beanhand/server/commands/refine.py +137 -0
  38. beanhand-0.7.2/beanhand/server/commands/remove.py +42 -0
  39. beanhand-0.7.2/beanhand/server/config.py +255 -0
  40. beanhand-0.7.2/beanhand/server/llm.py +115 -0
  41. beanhand-0.7.2/beanhand/server/pdf.py +81 -0
  42. beanhand-0.7.2/beanhand/server/storage.py +140 -0
  43. beanhand-0.7.2/beanhand/structs.py +101 -0
  44. beanhand-0.7.2/beanhand/tests/example.beancount +71 -0
  45. beanhand-0.7.2/beanhand/tests/test_account_list_server.py +212 -0
  46. beanhand-0.7.2/beanhand/tests/test_beancount_lock.py +283 -0
  47. beanhand-0.7.2/beanhand/tests/test_do_refine.py +287 -0
  48. beanhand-0.7.2/beanhand/tests/test_do_refine_server.py +86 -0
  49. beanhand-0.7.2/beanhand/tests/test_fetch_mtime.py +134 -0
  50. beanhand-0.7.2/beanhand/tests/test_file_modification.py +91 -0
  51. beanhand-0.7.2/beanhand/tests/test_import_result.py +512 -0
  52. beanhand-0.7.2/beanhand/tests/test_load_live_accounts.py +472 -0
  53. beanhand-0.7.2/beanhand/tests/test_local_receipt_backend.py +370 -0
  54. beanhand-0.7.2/beanhand/tests/test_refine_helpers.py +131 -0
  55. beanhand-0.7.2/beanhand/tests/test_refine_targets.py +183 -0
  56. beanhand-0.7.2/beanhand/tests/test_split_at_transaction_by_line_number.py +894 -0
  57. beanhand-0.7.2/beanhand/tests/test_update_document_metadata.py +271 -0
  58. beanhand-0.7.2/beanhand.egg-info/PKG-INFO +368 -0
  59. beanhand-0.7.2/beanhand.egg-info/SOURCES.txt +81 -0
  60. beanhand-0.7.2/beanhand.egg-info/dependency_links.txt +1 -0
  61. beanhand-0.7.2/beanhand.egg-info/entry_points.txt +4 -0
  62. beanhand-0.7.2/beanhand.egg-info/requires.txt +7 -0
  63. beanhand-0.7.2/beanhand.egg-info/top_level.txt +1 -0
  64. beanhand-0.7.2/docs/Commands.md +133 -0
  65. beanhand-0.7.2/docs/Features.md +112 -0
  66. beanhand-0.7.2/docs/Roadmap.md +70 -0
  67. beanhand-0.7.2/docs/specs/Associating receipts with transactions.md +157 -0
  68. beanhand-0.7.2/docs/specs/Dynamic account list from Beancount data.md +307 -0
  69. beanhand-0.7.2/docs/specs/Ingesting receipts and creating transactions.md +283 -0
  70. beanhand-0.7.2/docs/specs/Refine existing Beancount transactions.md +306 -0
  71. beanhand-0.7.2/docs/specs/Refine multi-range target specification.md +159 -0
  72. beanhand-0.7.2/docs/specs/Rendering of PDF documents to images.md +115 -0
  73. beanhand-0.7.2/mypy.ini +10 -0
  74. beanhand-0.7.2/pyproject.toml +20 -0
  75. beanhand-0.7.2/qubes-rpc/beanhand.Fetch +3 -0
  76. beanhand-0.7.2/qubes-rpc/beanhand.HelpAssociateReceipt +3 -0
  77. beanhand-0.7.2/qubes-rpc/beanhand.ListUnassociated +3 -0
  78. beanhand-0.7.2/qubes-rpc/beanhand.ListUningested +3 -0
  79. beanhand-0.7.2/qubes-rpc/beanhand.Process +3 -0
  80. beanhand-0.7.2/qubes-rpc/beanhand.Refine +3 -0
  81. beanhand-0.7.2/qubes-rpc/beanhand.Remove +3 -0
  82. beanhand-0.7.2/setup.cfg +4 -0
  83. beanhand-0.7.2/tox.ini +15 -0
@@ -0,0 +1,111 @@
1
+ # AGENTS.md — beanhand
2
+
3
+ ## Structure
4
+
5
+ ```
6
+ beanhand/
7
+ ├── docs/*.md # general documentation of the program, features, commands, and use
8
+ └── docs/specs/… # specs for features in development or developed
9
+ │── pyproject.toml # Python project definition and configuration ifle
10
+
11
+ ├── beanhand/structs.py # request/response TypedDicts shared by client and server
12
+
13
+ ├── beanhand/server/ # Runs on server VM which has access to receipts and LLM
14
+ │ ├── cli.py # beanhand-server entry point: build_parser() + dispatch table
15
+ │ ├── commands/ # one module per beanhand.* subcommand
16
+ │ │ ├── listcmds.py # beanhand.ListUningested / beanhand.ListUnassociated
17
+ │ │ ├── process.py # beanhand.Process
18
+ │ │ ├── fetch.py # beanhand.Fetch
19
+ │ │ ├── associate.py # beanhand.HelpAssociateReceipt
20
+ │ │ ├── remove.py # beanhand.Remove
21
+ │ │ └── refine.py # beanhand.Refine
22
+ │ ├── config.py # server-side configuration
23
+ │ ├── llm.py # shared LLM/streaming helpers (file_to_image_parts, …)
24
+ │ ├── storage.py # WebDAV client helpers
25
+ │ ├── pdf.py # PDF→PNG conversion for receipt images
26
+ │ └── *_PROMPT.md # prompts for LLMs
27
+
28
+ └── beanhand/client/ # Runs on client VM which has Beancount data
29
+ ├── cli.py # beanhand entry point: build_parser() + dispatch table
30
+ ├── commands/ # one module per beanhand subcommand
31
+ │ ├── listcmds.py # beanhand list-uningested / list-unassociated
32
+ │ ├── ingest.py # beanhand ingest
33
+ │ ├── importcmd.py # beanhand import
34
+ │ ├── associate.py # beanhand associate
35
+ │ ├── refine.py # beanhand refine
36
+ │ ├── process.py # beanhand process
37
+ │ ├── fetch.py # beanhand fetch
38
+ │ ├── remove.py # beanhand remove
39
+ │ └── organize.py # beanhand organize
40
+ ├── config.py # client-side configuration
41
+ ├── beancount_loader.py # loads Beancount data (queries / candidate contexts)
42
+ ├── beanfiles.py # raw Beancount file ops: tx splitting, doc metadata, receipt organization
43
+ ├── server.py # qrexec/subprocess transport (RemoteVM) + LLM streaming capture
44
+ └── display.py # colored unified-diff printing
45
+ ```
46
+
47
+ Tox (`tox --current-env`) is the test framework; it runs doctests, pytest, Ruff and MyPy.
48
+ You can invoke the entire suite of tests using command `make qa`. If you are iterating
49
+ through code changes, first run `pytest -vv` in the project directory to verify much more
50
+ quickly which tests are failing. When those tests are passing, make use of `make qa`
51
+ to catch further problems with the code.
52
+
53
+ ## How to run
54
+
55
+ **beanhand-server** — runs on the VM that has receipt files + LLM access. CLI subcommands:
56
+ - `beanhand-server beanhand.ListUningested` / `beanhand.ListUnassociated` list receipts as JSON
57
+ - `beanhand-server beanhand.Process <filename>` processes one receipt via OpenAI-compatible API (produces JSONL output)
58
+ - `beanhand-server beanhand.Refine` refines a transaction; request arrives as plain JSON on stdin (no positional argument, produces JSONL output)
59
+ - `beanhand-server beanhand.HelpAssociateReceipt <filename>` matches a receipt against candidate transactions (candidates arrive on stdin)
60
+
61
+ **beanhand** — runs on the VM with Beancount data. CLI subcommands:
62
+ - `beanhand list-uningested` / `list-unassociated` → print receipt filenames (one per line)
63
+ - `beanhand process <file>` → streams LLM response, prints parsed Beancount tx to stdout
64
+ - `beanhand refine <file_path> <target>...` → refine one or more transactions using their linked documents; each target is a 1-based line number (N), an inclusive line range (A-B), or an open range to the end of the file (A-end); targets must be strictly ascending and non-overlapping (see docs/specs/Refine multi-range target specification.md)
65
+ - `beanhand ingest` / `import <filename>` / `associate` / `fetch` / `remove` / `organize`
66
+
67
+ Default config: `~/.config/beanhand.json`. Both clients also support `--config <path>` and `$BEANHAND_CONFIG`.
68
+
69
+ **Local testing**: set `"target_vm": null` in client config so the client spawns the server as a subprocess
70
+ (arguments hex-encoded just as if the server were running in a separate VM).
71
+
72
+ ## Configuration (`~/.config/beanhand.json`)
73
+
74
+ Both programs read from the same config file by default `~/.config/beanhand.json` (but see below for more).
75
+
76
+ Refer to `README.md` for configuration details and values.
77
+
78
+ ## Server-client transport
79
+
80
+ For security reasons, the software is split into two parts:
81
+
82
+ 1. The client: runs on the virtual machine dedicated to accounting, where all the
83
+ Beancount files reside.
84
+ 2. The server: runs on the virtual machine that has the receipts, and also access
85
+ to an OpenAI-compatible LLM API that will process the receipts and turn them into
86
+ Beancount-formatted transactions.
87
+
88
+ - **Same host** (`target_vm: null`): client spawns server via subprocess, passes hex-encoded subcommand + args.
89
+ - **Different VM** (qrexec): service endpoint is `<subcommand>+<hex-encoded-args>`. The RPC handler lives at `/etc/qubes/rpc/beanhand.*` on the server VM.
90
+
91
+ When server and client are on the same machine (client's `config.json` says `target_vm: null`), then
92
+ client spawns server as subprocess and passes subcommand + command line argument directly, albeit encoding
93
+ argument as hex before invocation.
94
+
95
+ When server is on another VM, qrexec communication is used, and the service call endpoint becomes
96
+ the subcommand joined with a plus sign to the hex-encoded argument (if needed by the call).
97
+
98
+ Client has the ability to send stdin to server, and server can respond via stdout.
99
+
100
+ ## Key files (do not change without checking spec)
101
+
102
+ - `RECEIPT_CONVERSION_PROMPT.md` — tested LLM prompt for receipt→Beancount conversion. Do not modify without verifying against docs/specs.
103
+ - `RECEIPT_INFO_PROMPT.md` — also do not change, it's manually tested.
104
+ - `RECEIPT_MATCH_PROMPT.md` — same. Do not change.
105
+
106
+ ## Gotchas
107
+
108
+ - Config is a singleton per process: calling `Configuration.load()` a second time returns the first result silently. If testing different configs, use separate processes or `--config`.
109
+ - `BeancountConfiguration` takes an exclusive advisory lock on `main_file` as soon as it is instantiated (and `Configuration.load()` instantiates it). The lock is held until `unlock()` or process exit, so data-modifying subcommands queue up. Consequences: `main_file` must exist at config load time (a missing file raises `FileNotFoundError`); tests that build multiple configs pointing at the same file in one process will block; and two concurrent `beanhand` invocations against the same file serialize.
110
+ - Server emits JSONL with no buffering delay (flushes every 10 chunks). Over qrexec this can be slow; client handles line-by-line reading.
111
+ - `beanhand refine` refines one or more transactions: you can indicate which transactions to refine by referring to a line number (selects the transaction containing it) or a range A-B (selects every transaction that *begins* on a line within the inclusive span); the range may also be written A-end, where `end` means the end of the file; a line number may point at any line *within* a transaction; lines / ranges must be strictly ascending and non-overlapping. It writes the file once, at the end, only if at least one accepted refinement changed it.