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.
- abstractnote/__init__.py +18 -0
- abstractnote/cli.py +240 -0
- abstractnote/flows/__init__.py +2 -0
- abstractnote/flows/build.py +970 -0
- abstractnote/flows/bundle.py +62 -0
- abstractnote/flows/contract.py +88 -0
- abstractnote/flows/graph.py +540 -0
- abstractnote/flows/json/note-capture.json +2280 -0
- abstractnote/flows/json/note-drain.json +2012 -0
- abstractnote/flows/json/note-mine.json +2392 -0
- abstractnote/flows/json/note-process.json +1853 -0
- abstractnote/flows/json/note-recall.json +1876 -0
- abstractnote/flows/json/note-reflect.json +2057 -0
- abstractnote/flows/prompts_fallback.py +119 -0
- abstractnote/flows/runner.py +320 -0
- abstractnote/flows/schemas_fallback.py +175 -0
- abstractnote/gateway/__init__.py +24 -0
- abstractnote/gateway/appspec.py +10 -0
- abstractnote/gateway/router.py +571 -0
- abstractnote/gateway/tools.py +74 -0
- abstractnote/ids.py +177 -0
- abstractnote/importers/__init__.py +142 -0
- abstractnote/importers/__main__.py +3 -0
- abstractnote/importers/agora.py +203 -0
- abstractnote/importers/claude_sessions.py +415 -0
- abstractnote/importers/cli.py +113 -0
- abstractnote/importers/files.py +72 -0
- abstractnote/importers/markdown.py +165 -0
- abstractnote/ingest/__init__.py +7 -0
- abstractnote/ingest/blobs.py +47 -0
- abstractnote/ingest/capture.py +265 -0
- abstractnote/ingest/extract.py +118 -0
- abstractnote/ingest/normalise.py +95 -0
- abstractnote/model.py +229 -0
- abstractnote/process/__init__.py +5 -0
- abstractnote/process/context.py +218 -0
- abstractnote/process/merge.py +60 -0
- abstractnote/process/prompts/__init__.py +39 -0
- abstractnote/process/prompts/extract.md +30 -0
- abstractnote/process/prompts/link.md +32 -0
- abstractnote/process/prompts/mine_dossier.md +13 -0
- abstractnote/process/prompts/mine_findings.md +13 -0
- abstractnote/process/prompts/mine_plan.md +13 -0
- abstractnote/process/prompts/quick_filing.md +23 -0
- abstractnote/process/prompts/reflect.md +10 -0
- abstractnote/process/prompts/synthesize.md +17 -0
- abstractnote/process/resolve.py +96 -0
- abstractnote/process/revise.py +410 -0
- abstractnote/process/schemas.py +377 -0
- abstractnote/process/vocabulary.py +68 -0
- abstractnote/recall/__init__.py +1 -0
- abstractnote/recall/changes.py +191 -0
- abstractnote/recall/expand.py +379 -0
- abstractnote/recall/insights.py +62 -0
- abstractnote/recall/reflect.py +202 -0
- abstractnote/recall/review.py +191 -0
- abstractnote/recall/search.py +205 -0
- abstractnote/recall/shapes.py +52 -0
- abstractnote/recall/then_now.py +100 -0
- abstractnote/store/__init__.py +12 -0
- abstractnote/store/archive.py +166 -0
- abstractnote/store/embeddings.py +275 -0
- abstractnote/store/kg.py +219 -0
- abstractnote/store/notebook.py +829 -0
- abstractnote/store/notes.py +1190 -0
- abstractnote/store/queue.py +159 -0
- abstractnote/store/schema.py +348 -0
- abstractnote/store/vector_index.py +88 -0
- abstractnote/tools/__init__.py +215 -0
- abstractnote/tools/capture.py +121 -0
- abstractnote/tools/context.py +19 -0
- abstractnote/tools/process.py +22 -0
- abstractnote/tools/queue.py +23 -0
- abstractnote/tools/recall.py +64 -0
- abstractnote-0.1.0.dist-info/METADATA +145 -0
- abstractnote-0.1.0.dist-info/RECORD +80 -0
- abstractnote-0.1.0.dist-info/WHEEL +5 -0
- abstractnote-0.1.0.dist-info/entry_points.txt +2 -0
- abstractnote-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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
|