sourcesql 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.
sourcesql/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ """sourcesQL: bounded research datasets, independent of provider and host."""
2
+ __version__ = "0.1.0"
Binary file
sourcesql/bridge.py ADDED
@@ -0,0 +1,87 @@
1
+ """Bounded JSON-lines interface for the existing research service; stdout is machine-only."""
2
+
3
+ from __future__ import annotations
4
+ from concurrent.futures import ThreadPoolExecutor
5
+ import os
6
+ from pathlib import Path
7
+ import sys
8
+ import threading
9
+ from sourcesql.contracts import SourcesQLError, canonical
10
+ from sourcesql.contracts import digest
11
+ from sourcesql.engine import Engine, strict_json
12
+
13
+
14
+ def main():
15
+ profiles = {}
16
+ path = os.environ.get("SOURCESQL_PROFILES_FILE")
17
+ if path:
18
+ profiles = strict_json(Path(path).read_bytes())
19
+ principal = os.environ.get("SOURCESQL_PRINCIPAL", "local-owner")
20
+ store_root = (
21
+ Path(os.environ.get("SOURCESQL_DATA_DIR", "local/engine"))
22
+ / "principals"
23
+ / digest(principal)[:32]
24
+ )
25
+ engine = Engine(
26
+ store_root,
27
+ profiles=profiles,
28
+ allow_private=os.environ.get("SOURCESQL_ALLOW_PRIVATE", "1") == "1",
29
+ )
30
+ methods = {
31
+ "capabilities": engine.capabilities,
32
+ "query": engine.query,
33
+ "explain": engine.explain,
34
+ "read_result": engine.read_result,
35
+ "import_table": engine.import_table,
36
+ "export": engine.export,
37
+ "import": engine.import_bundle,
38
+ "run_status": engine.run_status,
39
+ "cancel": engine.cancel,
40
+ }
41
+ output_lock = threading.Lock()
42
+
43
+ def respond(line):
44
+ request_id = None
45
+ try:
46
+ if len(line) > 4 * 1024 * 1024:
47
+ raise SourcesQLError("budget_exhausted", "Bridge input exceeds byte limit")
48
+ request = strict_json(line)
49
+ request_id = request.get("id")
50
+ if (
51
+ not isinstance(request, dict)
52
+ or set(request) - {"id", "method", "params"}
53
+ or request.get("method") not in methods
54
+ ):
55
+ raise SourcesQLError("invalid_request", "Invalid bridge request")
56
+ params = request.get("params", {})
57
+ if not isinstance(params, dict):
58
+ raise SourcesQLError("invalid_request", "Invalid method parameters")
59
+ result = methods[request["method"]](**params)
60
+ response = {"id": request_id, "result": result}
61
+ except SourcesQLError as error:
62
+ response = {
63
+ "id": request_id,
64
+ "error": {"code": error.code, "message": error.safe_message, "stage": error.stage},
65
+ }
66
+ except Exception:
67
+ response = {
68
+ "id": request_id,
69
+ "error": {"code": "invalid_request", "message": "Request failed safely"},
70
+ }
71
+ with output_lock:
72
+ sys.stdout.buffer.write(canonical(response) + b"\n")
73
+ sys.stdout.buffer.flush()
74
+
75
+ with ThreadPoolExecutor(max_workers=4) as executor:
76
+ while line := sys.stdin.buffer.readline(4 * 1024 * 1024 + 1):
77
+ if not line.endswith(b"\n"):
78
+ while chunk := sys.stdin.buffer.readline(4 * 1024 * 1024 + 1):
79
+ if chunk.endswith(b"\n"):
80
+ break
81
+ respond(b"{}")
82
+ else:
83
+ executor.submit(respond, line)
84
+
85
+
86
+ if __name__ == "__main__":
87
+ main()
sourcesql/cli.py ADDED
@@ -0,0 +1,93 @@
1
+ """Headless developer entry point; fixture mode by default."""
2
+
3
+ import argparse
4
+ import os
5
+ from pathlib import Path
6
+ import sys
7
+ from sourcesql import __version__
8
+ from sourcesql.contracts import SourcesQLError, canonical
9
+ from sourcesql.engine import Engine, strict_json
10
+
11
+
12
+ def main(argv=None):
13
+ argv = list(sys.argv[1:] if argv is None else argv)
14
+ parser = argparse.ArgumentParser(prog="sourcesql")
15
+ parser.add_argument("--version", action="version", version=f"sourcesQL {__version__}")
16
+ parser.add_argument("--store", default=os.environ.get("SOURCESQL_DATA_DIR", "local/engine"))
17
+ commands = parser.add_subparsers(dest="command", required=True)
18
+ setup = commands.add_parser("setup", help="Prepare or resume an adopter-owned deployment")
19
+ setup.add_argument("setup_target", choices=["personal-cloud"])
20
+ setup.add_argument("arguments", nargs=argparse.REMAINDER)
21
+ commands.add_parser("capabilities").add_argument("--json", action="store_true")
22
+ for command in ["query", "explain"]:
23
+ p = commands.add_parser(command)
24
+ p.add_argument("--file", type=Path)
25
+ p.add_argument("--sql")
26
+ p.add_argument("--request", type=Path)
27
+ p.add_argument("--profile", default="fixture")
28
+ p.add_argument("--json", action="store_true")
29
+ p = commands.add_parser("read")
30
+ p.add_argument("result_id")
31
+ p.add_argument("--offset", type=int, default=0)
32
+ p.add_argument("--limit", type=int, default=100)
33
+ p = commands.add_parser("export")
34
+ p.add_argument("--result", required=True)
35
+ p.add_argument("--output", type=Path, required=True)
36
+ p = commands.add_parser("import")
37
+ p.add_argument("file", type=Path)
38
+ p.add_argument("--no-network", action="store_true")
39
+ p = commands.add_parser("demo")
40
+ p.add_argument("--profile", choices=["fixture"], default="fixture")
41
+ p.add_argument("--output", type=Path, default=Path("artifacts/demo"))
42
+ args = parser.parse_args(argv)
43
+ if args.command == "setup":
44
+ from sourcesql.onboarding import run_personal_cloud
45
+
46
+ return run_personal_cloud(args.arguments)
47
+ try:
48
+ profile_path = os.environ.get("SOURCESQL_PROFILES_FILE")
49
+ engine = Engine(
50
+ args.store,
51
+ profiles=strict_json(Path(profile_path).read_bytes()) if profile_path else None,
52
+ )
53
+ if args.command == "capabilities":
54
+ result = engine.capabilities()
55
+ elif args.command in ["query", "explain"]:
56
+ request = (
57
+ strict_json(args.request.read_bytes())
58
+ if args.request
59
+ else {
60
+ "query": args.file.read_text() if args.file else args.sql,
61
+ "profile": args.profile,
62
+ }
63
+ )
64
+ result = getattr(engine, args.command)(**request)
65
+ elif args.command == "read":
66
+ result = engine.read_result(args.result_id, args.offset, args.limit)
67
+ elif args.command == "export":
68
+ result = engine.export(args.result)
69
+ args.output.parent.mkdir(parents=True, exist_ok=True)
70
+ args.output.write_bytes(canonical(result))
71
+ elif args.command == "import":
72
+ result = engine.import_bundle(strict_json(args.file.read_bytes()))
73
+ else:
74
+ result = engine.query(
75
+ query="SELECT url, SEM_EXTRACT(content, 'project name explicitly stated') AS project, SEM_EXTRACT(content, 'planned capacity in MW, number only') AS planned_mw FROM WEB_SEARCH('fictional Aurora Birch campuses') WHERE SEM_MATCH(content, 'describes a specific data-center campus')",
76
+ profile="fixture",
77
+ )
78
+ args.output.mkdir(parents=True, exist_ok=True)
79
+ (args.output / "result.json").write_bytes(canonical(result))
80
+ (args.output / "bundle.json").write_bytes(canonical(engine.export(result["result_id"])))
81
+ print(canonical(result).decode())
82
+ except SourcesQLError as error:
83
+ print(
84
+ canonical(
85
+ {"error": {"code": error.code, "message": error.safe_message, "stage": error.stage}}
86
+ ).decode(),
87
+ file=sys.stderr,
88
+ )
89
+ raise SystemExit(1)
90
+
91
+
92
+ if __name__ == "__main__":
93
+ raise SystemExit(main())
sourcesql/contracts.py ADDED
@@ -0,0 +1,109 @@
1
+ """Shared engine/provider/planner contracts. Coordinator-owned."""
2
+
3
+ from __future__ import annotations
4
+ from dataclasses import dataclass, field
5
+ from typing import Any, Callable
6
+ import hashlib
7
+ import json
8
+ import threading
9
+
10
+
11
+ class SourcesQLError(Exception):
12
+ def __init__(self, code: str, message: str, stage: str = "admission"):
13
+ super().__init__(message)
14
+ self.code, self.safe_message, self.stage = code, message, stage
15
+
16
+
17
+ def canonical(value: Any) -> bytes:
18
+ return json.dumps(
19
+ value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False
20
+ ).encode()
21
+
22
+
23
+ def digest(value: Any) -> str:
24
+ return hashlib.sha256(canonical(value)).hexdigest()
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class Column:
29
+ key: str
30
+ logical_type: str = "VARCHAR"
31
+ nullable: bool = True
32
+
33
+ def to_dict(self) -> dict[str, Any]:
34
+ return {
35
+ "key": self.key,
36
+ "label": self.key,
37
+ "logical_type": self.logical_type,
38
+ "nullable": self.nullable,
39
+ }
40
+
41
+
42
+ @dataclass
43
+ class Table:
44
+ columns: list[Column]
45
+ rows: list[dict[str, Any]]
46
+
47
+ def to_dict(self) -> dict[str, Any]:
48
+ return {"columns": [c.to_dict() for c in self.columns], "rows": self.rows}
49
+
50
+
51
+ @dataclass
52
+ class SemanticOutcome:
53
+ value: Any
54
+ status: str
55
+ support: list[dict[str, Any]] = field(default_factory=list)
56
+ usage: dict[str, Any] = field(default_factory=dict)
57
+
58
+
59
+ DEFAULT_LIMITS = {
60
+ "max_search_queries": 8,
61
+ "max_search_requests": 16,
62
+ "max_candidates": 200,
63
+ "max_fetches": 40,
64
+ "max_semantic_documents": 40,
65
+ "max_model_requests": 96,
66
+ "max_concurrency": 4,
67
+ "dispatch_deadline_seconds": 180,
68
+ "max_output_rows": 1000,
69
+ "max_output_bytes": 4194304,
70
+ "max_input_tokens": 200000,
71
+ "max_output_tokens": 16000,
72
+ "max_cost_microusd": 0,
73
+ }
74
+
75
+
76
+ class Budget:
77
+ """One shared allowance for the whole run. Provider attempts reserve before dispatch."""
78
+
79
+ def __init__(
80
+ self,
81
+ limits: dict[str, Any] | None = None,
82
+ on_reserve: Callable[[str, int], None] | None = None,
83
+ ):
84
+ self.limits = dict(DEFAULT_LIMITS)
85
+ for key, value in (limits or {}).items():
86
+ if key not in self.limits or type(value) is not int or value < 0:
87
+ raise SourcesQLError("invalid_request", "Invalid budget")
88
+ if key != "max_cost_microusd" and value > DEFAULT_LIMITS[key]:
89
+ raise SourcesQLError("invalid_request", "Requested budget exceeds v0.1 ceiling")
90
+ self.limits[key] = value
91
+ self.counts: dict[str, int] = {}
92
+ self.lock = threading.Lock()
93
+ self.cancelled = False
94
+ self.on_reserve = on_reserve
95
+
96
+ def reserve(self, counter: str, amount: int = 1, limit: str | None = None) -> None:
97
+ with self.lock:
98
+ if self.cancelled:
99
+ raise SourcesQLError("cancelled", "Run cancelled", "dispatch")
100
+ cap = self.limits.get(limit or "max_" + counter)
101
+ value = self.counts.get(counter, 0) + amount
102
+ if cap is not None and value > cap:
103
+ raise SourcesQLError("budget_exhausted", "Work allowance exhausted", "dispatch")
104
+ self.counts[counter] = value
105
+ observer = self.on_reserve
106
+ # Persistence/authorization observers finish before the caller can dispatch.
107
+ # Exceptions propagate; the reserved allowance remains conservatively consumed.
108
+ if observer is not None:
109
+ observer(counter, amount)