abstractnote 0.1.0__py3-none-any.whl

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 (80) hide show
  1. abstractnote/__init__.py +18 -0
  2. abstractnote/cli.py +240 -0
  3. abstractnote/flows/__init__.py +2 -0
  4. abstractnote/flows/build.py +970 -0
  5. abstractnote/flows/bundle.py +62 -0
  6. abstractnote/flows/contract.py +88 -0
  7. abstractnote/flows/graph.py +540 -0
  8. abstractnote/flows/json/note-capture.json +2280 -0
  9. abstractnote/flows/json/note-drain.json +2012 -0
  10. abstractnote/flows/json/note-mine.json +2392 -0
  11. abstractnote/flows/json/note-process.json +1853 -0
  12. abstractnote/flows/json/note-recall.json +1876 -0
  13. abstractnote/flows/json/note-reflect.json +2057 -0
  14. abstractnote/flows/prompts_fallback.py +119 -0
  15. abstractnote/flows/runner.py +320 -0
  16. abstractnote/flows/schemas_fallback.py +175 -0
  17. abstractnote/gateway/__init__.py +24 -0
  18. abstractnote/gateway/appspec.py +10 -0
  19. abstractnote/gateway/router.py +571 -0
  20. abstractnote/gateway/tools.py +74 -0
  21. abstractnote/ids.py +177 -0
  22. abstractnote/importers/__init__.py +142 -0
  23. abstractnote/importers/__main__.py +3 -0
  24. abstractnote/importers/agora.py +203 -0
  25. abstractnote/importers/claude_sessions.py +415 -0
  26. abstractnote/importers/cli.py +113 -0
  27. abstractnote/importers/files.py +72 -0
  28. abstractnote/importers/markdown.py +165 -0
  29. abstractnote/ingest/__init__.py +7 -0
  30. abstractnote/ingest/blobs.py +47 -0
  31. abstractnote/ingest/capture.py +265 -0
  32. abstractnote/ingest/extract.py +118 -0
  33. abstractnote/ingest/normalise.py +95 -0
  34. abstractnote/model.py +229 -0
  35. abstractnote/process/__init__.py +5 -0
  36. abstractnote/process/context.py +218 -0
  37. abstractnote/process/merge.py +60 -0
  38. abstractnote/process/prompts/__init__.py +39 -0
  39. abstractnote/process/prompts/extract.md +30 -0
  40. abstractnote/process/prompts/link.md +32 -0
  41. abstractnote/process/prompts/mine_dossier.md +13 -0
  42. abstractnote/process/prompts/mine_findings.md +13 -0
  43. abstractnote/process/prompts/mine_plan.md +13 -0
  44. abstractnote/process/prompts/quick_filing.md +23 -0
  45. abstractnote/process/prompts/reflect.md +10 -0
  46. abstractnote/process/prompts/synthesize.md +17 -0
  47. abstractnote/process/resolve.py +96 -0
  48. abstractnote/process/revise.py +410 -0
  49. abstractnote/process/schemas.py +377 -0
  50. abstractnote/process/vocabulary.py +68 -0
  51. abstractnote/recall/__init__.py +1 -0
  52. abstractnote/recall/changes.py +191 -0
  53. abstractnote/recall/expand.py +379 -0
  54. abstractnote/recall/insights.py +62 -0
  55. abstractnote/recall/reflect.py +202 -0
  56. abstractnote/recall/review.py +191 -0
  57. abstractnote/recall/search.py +205 -0
  58. abstractnote/recall/shapes.py +52 -0
  59. abstractnote/recall/then_now.py +100 -0
  60. abstractnote/store/__init__.py +12 -0
  61. abstractnote/store/archive.py +166 -0
  62. abstractnote/store/embeddings.py +275 -0
  63. abstractnote/store/kg.py +219 -0
  64. abstractnote/store/notebook.py +829 -0
  65. abstractnote/store/notes.py +1190 -0
  66. abstractnote/store/queue.py +159 -0
  67. abstractnote/store/schema.py +348 -0
  68. abstractnote/store/vector_index.py +88 -0
  69. abstractnote/tools/__init__.py +215 -0
  70. abstractnote/tools/capture.py +121 -0
  71. abstractnote/tools/context.py +19 -0
  72. abstractnote/tools/process.py +22 -0
  73. abstractnote/tools/queue.py +23 -0
  74. abstractnote/tools/recall.py +64 -0
  75. abstractnote-0.1.0.dist-info/METADATA +145 -0
  76. abstractnote-0.1.0.dist-info/RECORD +80 -0
  77. abstractnote-0.1.0.dist-info/WHEEL +5 -0
  78. abstractnote-0.1.0.dist-info/entry_points.txt +2 -0
  79. abstractnote-0.1.0.dist-info/licenses/LICENSE +21 -0
  80. abstractnote-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,18 @@
1
+ """AbstractNote: one endpoint for your notes; a temporal semantic knowledge graph of your life.
2
+
3
+ ``Notebook.open(dir)`` opens a notebook directory; ``abstractnote.tools.bind(dir)``
4
+ returns the note tools for flows and agents.
5
+ """
6
+
7
+ __version__ = "0.1.0"
8
+
9
+
10
+ def __getattr__(name: str): # lazy: importing abstractnote stays cheap
11
+ if name == "Notebook":
12
+ from .store.notebook import Notebook
13
+
14
+ return Notebook
15
+ raise AttributeError(name)
16
+
17
+
18
+ __all__ = ["Notebook", "__version__"]
abstractnote/cli.py ADDED
@@ -0,0 +1,240 @@
1
+ """The ``abstractnote`` command line (docs/api.md, "CLI").
2
+
3
+ Every command works on a notebook directory with ``--notebook DIR`` (inside a
4
+ gateway the notebook is ``<gateway data dir>/abstractnote``, or
5
+ ``<data dir>/users/<tenant>/<runtime>/abstractnote`` for a user plane).
6
+ ``--gateway URL --token T`` client mode is not available yet and says so.
7
+ Output is JSON on stdout. Exit codes: 0 done, 2 bad input, 3 a part of
8
+ AbstractNote that is not installed or not available yet.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import json
15
+ import sys
16
+ from pathlib import Path
17
+ from typing import Any, Dict, List, Optional
18
+
19
+ EXIT_OK, EXIT_INPUT, EXIT_GAP = 0, 2, 3
20
+
21
+
22
+ class Gap(RuntimeError):
23
+ """A piece the command needs is not available (another package part, or client mode)."""
24
+
25
+
26
+ def _emit(data: Any) -> None:
27
+ print(json.dumps(data, indent=2, ensure_ascii=False, default=str))
28
+
29
+
30
+ def _common(p: argparse.ArgumentParser) -> None:
31
+ p.add_argument("--notebook", type=Path, help="notebook directory (created when missing)")
32
+ p.add_argument("--gateway", help="gateway URL (client mode)")
33
+ p.add_argument("--token", help="gateway token, passed directly")
34
+ p.add_argument("--embed-base-url", help="OpenAI-compatible embeddings URL (for example http://127.0.0.1:1234/v1). "
35
+ "A notebook is pinned to its embedding model when it is created (by the first command that "
36
+ "opens it, capture included; without these flags: the built-in hashing embedder); every later "
37
+ "command must give the same model, or run rebuild --reembed to change it")
38
+ p.add_argument("--embed-model", help="embedding model id at that URL (pinned at notebook creation, see --embed-base-url)")
39
+
40
+
41
+ def build_parser() -> argparse.ArgumentParser:
42
+ ap = argparse.ArgumentParser(prog="abstractnote", description="AbstractNote: one endpoint for your notes.")
43
+ sub = ap.add_subparsers(dest="command", required=True)
44
+
45
+ p = sub.add_parser("capture", help="save a note now")
46
+ _common(p)
47
+ p.add_argument("text", nargs="?", default="")
48
+ p.add_argument("--file", action="append", default=[], type=Path, help="attach a file (repeatable)")
49
+ p.add_argument("--at", help="when it happened (ISO 8601; default now)")
50
+ p.add_argument("--title")
51
+ p.add_argument("--priority", type=int, choices=[0, 1, 2, 3])
52
+ p.add_argument("--key", help="idempotency key (a retry with the same key saves nothing new)")
53
+
54
+ p = sub.add_parser("search", help="search the notes")
55
+ _common(p)
56
+ p.add_argument("query")
57
+ p.add_argument("--effort", choices=["quick", "standard", "deep"], default="quick")
58
+ p.add_argument("--since")
59
+ p.add_argument("--until")
60
+ p.add_argument("--as-of")
61
+ p.add_argument("--limit", type=int, default=20)
62
+ p.add_argument("--provider", help="model provider for standard and deep effort")
63
+ p.add_argument("--model", help="model for standard and deep effort")
64
+
65
+ p = sub.add_parser("entity", help="an entity card")
66
+ _common(p)
67
+ p.add_argument("entity_id")
68
+ p.add_argument("--at")
69
+
70
+ p = sub.add_parser("day", help="what happened on a day")
71
+ _common(p)
72
+ p.add_argument("date", help="YYYY-MM-DD")
73
+ p.add_argument("--tz")
74
+
75
+ p = sub.add_parser("status", help="notebook counts, queue and versions")
76
+ _common(p)
77
+
78
+ p = sub.add_parser("changes", help="what changed since a cursor or a time")
79
+ _common(p)
80
+ p.add_argument("--since")
81
+ p.add_argument("--limit", type=int, default=50)
82
+
83
+ p = sub.add_parser("rebuild", help="replay revisions into fresh index and triple stores")
84
+ _common(p)
85
+ p.add_argument("--reembed", action="store_true", help="recompute every vector with the given embedder")
86
+
87
+ p = sub.add_parser("process", help="process queued notes now")
88
+ _common(p)
89
+ p.add_argument("--local", action="store_true", help="run the flows here (headless)")
90
+ p.add_argument("--provider")
91
+ p.add_argument("--model")
92
+ p.add_argument("--max-items", type=int, default=20)
93
+ p.add_argument("--budget-s", type=float, default=90.0)
94
+
95
+ p = sub.add_parser("bundle", help="pack the abstractnote .flow bundle")
96
+ p.add_argument("--out", type=Path, default=Path("."))
97
+
98
+ p = sub.add_parser("processor", help="processor automation")
99
+ _common(p)
100
+ p.add_argument("action", choices=["ensure"])
101
+
102
+ sub.add_parser("import", help="import a corpus (claude-sessions, agora, markdown, files)", add_help=False)
103
+ return ap
104
+
105
+
106
+ def _embedder(args: argparse.Namespace) -> Any:
107
+ if getattr(args, "embed_base_url", None) or getattr(args, "embed_model", None):
108
+ if not (args.embed_base_url and args.embed_model):
109
+ raise ValueError("Give both --embed-base-url and --embed-model.")
110
+ from .store.embeddings import openai_compat_embedder
111
+
112
+ return openai_compat_embedder(args.embed_base_url, args.embed_model)
113
+ return None
114
+
115
+
116
+ def _client_mode(args: argparse.Namespace) -> None:
117
+ if getattr(args, "gateway", None):
118
+ raise NotImplementedError(
119
+ "Gateway client mode is not available in this version; run the command on the machine that holds the "
120
+ "notebook with --notebook DIR.")
121
+
122
+
123
+ def _open(args: argparse.Namespace) -> Any:
124
+ _client_mode(args)
125
+ if not getattr(args, "notebook", None):
126
+ raise ValueError("Say which notebook with --notebook DIR (a gateway keeps it in <data dir>/abstractnote).")
127
+ from .store.notebook import Notebook
128
+
129
+ return Notebook.open(args.notebook, embedder=_embedder(args))
130
+
131
+
132
+ def _cmd_capture(args: argparse.Namespace) -> Any:
133
+ files = []
134
+ for path in args.file:
135
+ if not path.is_file():
136
+ raise ValueError(f"There is no file at {path}.")
137
+ files.append({"data": path.read_bytes(), "filename": path.name})
138
+ with _open(args) as nb:
139
+ return nb.capture(args.text, title=args.title, occurred_at=args.at, priority=args.priority,
140
+ idempotency_key=args.key, attachments=files, source={"client": "cli"}).model_dump()
141
+
142
+
143
+ def _runner(args: argparse.Namespace) -> Any:
144
+ try:
145
+ from .flows.runner import NoteRunner
146
+ except ImportError as exc:
147
+ raise Gap(f"The flows are not installed with this AbstractNote ({exc}).") from None
148
+ if not (args.provider and args.model):
149
+ raise ValueError("Give --provider and --model for the model that processes or answers.")
150
+ return NoteRunner.local(args.notebook, provider=args.provider, model=args.model, embedder=_embedder(args))
151
+
152
+
153
+ def _cmd_search(args: argparse.Namespace) -> Any:
154
+ if args.effort == "quick":
155
+ with _open(args) as nb:
156
+ return nb.search(args.query, since=args.since, until=args.until, as_of=args.as_of, limit=args.limit)
157
+ _client_mode(args)
158
+ if not args.notebook:
159
+ raise ValueError("Say which notebook with --notebook DIR.")
160
+ return _runner(args).run_recall(args.query, effort=args.effort, since=args.since, until=args.until,
161
+ as_of=args.as_of)
162
+
163
+
164
+ def _cmd_process(args: argparse.Namespace) -> Any:
165
+ _client_mode(args)
166
+ if not args.local:
167
+ raise NotImplementedError("Processing through a gateway is not available here; use --local with "
168
+ "--provider and --model, or press Process now in the web app.")
169
+ if not args.notebook:
170
+ raise ValueError("Say which notebook with --notebook DIR.")
171
+ return _runner(args).run_drain(max_items=args.max_items, budget_s=args.budget_s)
172
+
173
+
174
+ def _cmd_bundle(args: argparse.Namespace) -> Any:
175
+ try:
176
+ from .flows.bundle import pack
177
+ except ImportError as exc:
178
+ raise Gap(f"The flows are not installed with this AbstractNote ({exc}).") from None
179
+ try:
180
+ return {"bundle": str(pack(args.out, check_fresh=True))}
181
+ except RuntimeError as exc: # stale generated flows: the flows must be rebuilt first
182
+ raise Gap(str(exc)) from None
183
+
184
+
185
+ def _cmd_import(argv: List[str]) -> int:
186
+ try:
187
+ from .importers.cli import main as import_main
188
+ except ImportError as exc:
189
+ print(f"The importers are not installed with this AbstractNote ({exc}).", file=sys.stderr)
190
+ return EXIT_GAP
191
+ return int(import_main(argv, prog="abstractnote import") or 0)
192
+
193
+
194
+ def main(argv: Optional[List[str]] = None) -> int:
195
+ argv = list(sys.argv[1:] if argv is None else argv)
196
+ if argv[:1] == ["import"]:
197
+ return _cmd_import(argv[1:])
198
+ args = build_parser().parse_args(argv)
199
+ try:
200
+ if args.command == "capture":
201
+ out = _cmd_capture(args)
202
+ elif args.command == "search":
203
+ out = _cmd_search(args)
204
+ elif args.command == "entity":
205
+ with _open(args) as nb:
206
+ out = nb.entity(args.entity_id, at=args.at)
207
+ elif args.command == "day":
208
+ with _open(args) as nb:
209
+ out = nb.day(args.date, args.tz)
210
+ elif args.command == "status":
211
+ with _open(args) as nb:
212
+ out = nb.status()
213
+ elif args.command == "changes":
214
+ with _open(args) as nb:
215
+ out = nb.changes(args.since, args.limit)
216
+ elif args.command == "rebuild":
217
+ with _open(args) as nb:
218
+ out = nb.rebuild(reembed=args.reembed, embedder=_embedder(args))
219
+ elif args.command == "process":
220
+ out = _cmd_process(args)
221
+ elif args.command == "bundle":
222
+ out = _cmd_bundle(args)
223
+ elif args.command == "processor":
224
+ _client_mode(args)
225
+ raise NotImplementedError("Creating the processor needs a gateway: open the web app, or use "
226
+ "POST /api/gateway/note/processor/ensure with your token.")
227
+ else: # pragma: no cover - argparse rejects unknown commands
228
+ raise ValueError(f"Unknown command {args.command}.")
229
+ except (NotImplementedError, Gap) as exc:
230
+ print(str(exc), file=sys.stderr)
231
+ return EXIT_GAP
232
+ except ValueError as exc:
233
+ print(str(exc), file=sys.stderr)
234
+ return EXIT_INPUT
235
+ _emit(out)
236
+ return EXIT_OK
237
+
238
+
239
+ if __name__ == "__main__": # pragma: no cover
240
+ raise SystemExit(main())
@@ -0,0 +1,2 @@
1
+ """AbstractNote flows: the VisualFlow generator (``build``), the bundle packer (``bundle``)
2
+ and the headless runner (``runner``)."""