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,62 @@
1
+ """Pack the generated flows into ``abstractnote@<version>.flow`` (AbstractRuntime bundle format)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from pathlib import Path
7
+ from typing import List, Optional
8
+
9
+ from .build import AUTOMATION_DEFAULTS, BUNDLE_ID, ENTRYPOINTS, JSON_DIR, stale
10
+
11
+
12
+ def bundle_version() -> str:
13
+ from abstractnote import __version__
14
+
15
+ return str(__version__)
16
+
17
+
18
+ def pack(out_dir: Path, *, flows_dir: Path = JSON_DIR, version: Optional[str] = None, check_fresh: bool = False) -> Path:
19
+ """Write ``<out_dir>/abstractnote@<version>.flow`` and return its path.
20
+
21
+ The JSON documents on disk are the shipped artifact. ``check_fresh`` (the ``abstractnote
22
+ bundle`` command and the tests) refuses flows that are stale relative to the generator, so a
23
+ bundle someone publishes always matches the code; the gateway packs what is installed.
24
+ """
25
+ from abstractruntime.workflow_bundle import pack_workflow_bundle
26
+
27
+ if check_fresh and flows_dir == JSON_DIR:
28
+ bad = stale(flows_dir)
29
+ if bad:
30
+ raise RuntimeError("the generated flows are stale (" + ", ".join(bad) + "); run python -m abstractnote.flows.build")
31
+ ver = version or bundle_version()
32
+ out_dir = Path(out_dir)
33
+ out_dir.mkdir(parents=True, exist_ok=True)
34
+ out_path = out_dir / f"{BUNDLE_ID}@{ver}.flow"
35
+ pack_workflow_bundle(
36
+ root_flow_json=flows_dir / f"{ENTRYPOINTS[0]}.json",
37
+ out_path=out_path,
38
+ bundle_id=BUNDLE_ID,
39
+ bundle_version=ver,
40
+ flows_dir=flows_dir,
41
+ entrypoints=list(ENTRYPOINTS),
42
+ default_entrypoint=ENTRYPOINTS[0],
43
+ metadata={
44
+ "family": "abstractnote",
45
+ "purpose": "notes",
46
+ "min_runtime": "0.10.2",
47
+ "automation_defaults": {k: v for k, v in AUTOMATION_DEFAULTS.items()},
48
+ },
49
+ )
50
+ return out_path
51
+
52
+
53
+ def main(argv: List[str] | None = None) -> int:
54
+ ap = argparse.ArgumentParser(prog="python -m abstractnote.flows.bundle", description=__doc__.splitlines()[0])
55
+ ap.add_argument("--out", type=Path, default=Path("."), help="directory for the .flow file (default: .)")
56
+ args = ap.parse_args(argv)
57
+ print(pack(args.out, check_fresh=True))
58
+ return 0
59
+
60
+
61
+ if __name__ == "__main__":
62
+ raise SystemExit(main())
@@ -0,0 +1,88 @@
1
+ """The flows <-> note tools seam (review 01, S1).
2
+
3
+ Every ``tool_calls`` node in the generated flows is fed by an arguments code node whose input
4
+ pins are the tool's argument names. ``check_tool_wiring`` compares them with each tool's
5
+ ``ToolDefinition`` (AbstractCore ``@tool``): an argument the tool does not declare would be
6
+ dropped silently by the runtime's kwargs filter, and a required parameter left unwired would
7
+ fail only at run time, so both are reported here.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ from pathlib import Path
14
+ from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional
15
+
16
+ from .build import JSON_DIR, ALL_FLOWS
17
+
18
+
19
+ def load_flows(flows_dir: Path = JSON_DIR) -> Dict[str, Dict[str, Any]]:
20
+ return {fid: json.loads((flows_dir / f"{fid}.json").read_text(encoding="utf-8")) for fid in ALL_FLOWS}
21
+
22
+
23
+ def tool_call_sites(flow: Mapping[str, Any]) -> List[Dict[str, Any]]:
24
+ """One entry per tool_calls node: {flow, node, tools, arguments, wired}."""
25
+ nodes = {n["id"]: n for n in flow.get("nodes") or []}
26
+ incoming: Dict[str, Dict[str, str]] = {}
27
+ for e in flow.get("edges") or []:
28
+ incoming.setdefault(e["target"], {})[e["targetHandle"]] = e["source"]
29
+ sites = []
30
+ for nid, n in nodes.items():
31
+ d = n.get("data") or {}
32
+ if d.get("nodeType") != "tool_calls":
33
+ continue
34
+ tools = list((d.get("pinDefaults") or {}).get("allowed_tools") or [])
35
+ src_id = incoming.get(nid, {}).get("tool_calls")
36
+ src = nodes.get(src_id or "")
37
+ site: Dict[str, Any] = {"flow": flow.get("id"), "node": nid, "tools": tools, "arguments": None, "wired": set()}
38
+ if src is not None and (src.get("data") or {}).get("nodeType") == "code":
39
+ sd = src["data"]
40
+ args = [p["id"] for p in sd.get("inputs") or [] if p.get("type") != "execution" and p["id"] != "permissions"]
41
+ defaults = set((sd.get("pinDefaults") or {}).keys())
42
+ wired = {a for a in args if a in incoming.get(src["id"], {}) or a in defaults}
43
+ site["arguments"] = args
44
+ site["wired"] = wired
45
+ sites.append(site)
46
+ return sites
47
+
48
+
49
+ def tool_parameters(tools: Iterable[Callable[..., Any]] | Mapping[str, Callable[..., Any]]) -> Dict[str, Dict[str, Any]]:
50
+ """{tool name: ToolDefinition.parameters} from @tool-decorated callables."""
51
+ items = tools.values() if isinstance(tools, Mapping) else tools
52
+ out: Dict[str, Dict[str, Any]] = {}
53
+ for t in items:
54
+ td = getattr(t, "_tool_definition", None)
55
+ if td is None:
56
+ raise TypeError(f"{getattr(t, '__name__', t)!r} is not an AbstractCore @tool (no _tool_definition)")
57
+ out[str(td.name)] = dict(td.parameters or {})
58
+ return out
59
+
60
+
61
+ def check_tool_wiring(flows: Mapping[str, Mapping[str, Any]], parameters: Mapping[str, Mapping[str, Any]],
62
+ *, only: Optional[set] = None) -> List[str]:
63
+ """Problems between the flows' tool calls and the tools' declared parameters (empty = ok)."""
64
+ problems: List[str] = []
65
+ for flow in flows.values():
66
+ for site in tool_call_sites(flow):
67
+ where = f"{site['flow']}.{site['node']}"
68
+ if len(site["tools"]) != 1:
69
+ problems.append(f"{where}: expected exactly one allowed tool, got {site['tools']}")
70
+ continue
71
+ name = site["tools"][0]
72
+ if only is not None and name not in only:
73
+ continue
74
+ if site["arguments"] is None:
75
+ problems.append(f"{where}: tool_calls is not fed by an arguments code node")
76
+ continue
77
+ params = parameters.get(name)
78
+ if params is None:
79
+ problems.append(f"{where}: tool {name} does not exist")
80
+ continue
81
+ for a in site["arguments"]:
82
+ if a not in params:
83
+ problems.append(f"{where}: {name} has no parameter {a!r}")
84
+ for p, spec in params.items():
85
+ required = not (isinstance(spec, dict) and "default" in spec)
86
+ if required and p not in site["wired"]:
87
+ problems.append(f"{where}: required parameter {name}.{p} is not wired")
88
+ return problems