continuum-local 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.
@@ -0,0 +1,4 @@
1
+ from .resolver import load_semantics, resolve, to_semantic_contract
2
+
3
+ __all__ = ["load_semantics", "resolve", "to_semantic_contract"]
4
+
continuum_local/cli.py ADDED
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ from typing import Any, Dict
6
+
7
+ from .resolver import load_semantics, resolve, to_semantic_contract
8
+
9
+
10
+ def _json_arg(raw: str) -> Dict[str, Any]:
11
+ raw = (raw or "").strip()
12
+ if not raw:
13
+ return {}
14
+ obj = json.loads(raw)
15
+ if not isinstance(obj, dict):
16
+ raise ValueError("context must be a JSON object")
17
+ return obj
18
+
19
+
20
+ def main() -> None:
21
+ p = argparse.ArgumentParser(prog="continuum-local")
22
+ sub = p.add_subparsers(dest="cmd", required=True)
23
+
24
+ r = sub.add_parser("resolve", help="Resolve query against local semantics")
25
+ r.add_argument("--semantics", required=True, help="YAML/JSON semantics file path")
26
+ r.add_argument("--query", required=True, help="Natural language query")
27
+ r.add_argument("--context", default="{}", help="JSON object (e.g. '{\"team\":\"marketing\"}')")
28
+
29
+ args = p.parse_args()
30
+ if args.cmd == "resolve":
31
+ doc = load_semantics(args.semantics)
32
+ ctx = _json_arg(args.context)
33
+ out = resolve(doc, query=args.query, context=ctx)
34
+ print(json.dumps(out, indent=2, sort_keys=True))
35
+ if out.get("status") == "resolved":
36
+ contract = to_semantic_contract(out, context=ctx)
37
+ print("\n--- semantic_contract ---")
38
+ print(json.dumps(contract, indent=2, sort_keys=True))
39
+
40
+
41
+ if __name__ == "__main__":
42
+ main()
43
+
@@ -0,0 +1,138 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional, Tuple, Union
7
+
8
+ import yaml
9
+
10
+ SemanticsDoc = Dict[str, Any]
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class Candidate:
15
+ metric_id: str
16
+ canonical_name: str
17
+ description: str
18
+ tags: Tuple[str, ...] = ()
19
+
20
+
21
+ def load_semantics(path: Union[str, Path]) -> SemanticsDoc:
22
+ """
23
+ Load a tiny YAML/JSON semantics file.
24
+
25
+ Expected shape (minimal):
26
+ metrics:
27
+ - metric_id: revenue
28
+ canonical_name: Revenue
29
+ description: Net revenue excluding refunds
30
+ tags: [finance, marketing]
31
+ """
32
+ p = Path(path)
33
+ raw = p.read_text(encoding="utf-8")
34
+ if p.suffix.lower() in {".json"}:
35
+ doc = json.loads(raw)
36
+ else:
37
+ doc = yaml.safe_load(raw)
38
+ if not isinstance(doc, dict):
39
+ raise ValueError("semantics file must be an object")
40
+ return doc
41
+
42
+
43
+ def _candidates(doc: SemanticsDoc) -> List[Candidate]:
44
+ out: List[Candidate] = []
45
+ for m in (doc.get("metrics") or []):
46
+ if not isinstance(m, dict):
47
+ continue
48
+ metric_id = str(m.get("metric_id") or "").strip()
49
+ if not metric_id:
50
+ continue
51
+ canonical_name = str(m.get("canonical_name") or metric_id)
52
+ description = str(m.get("description") or canonical_name)
53
+ tags = tuple(str(t) for t in (m.get("tags") or []) if str(t).strip())
54
+ out.append(Candidate(metric_id=metric_id, canonical_name=canonical_name, description=description, tags=tags))
55
+ return out
56
+
57
+
58
+ def resolve(doc: SemanticsDoc, *, query: str, context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
59
+ """
60
+ Deterministic, no-ML resolver.
61
+
62
+ Rules:
63
+ - normalize query to tokens
64
+ - score candidates by substring/token overlap
65
+ - boost if context['team'] matches candidate tag
66
+ - return {status: resolved|ambiguous|no_match}
67
+ """
68
+ q = (query or "").strip().lower()
69
+ ctx = context or {}
70
+ team = str(ctx.get("team") or "").strip().lower()
71
+
72
+ cands = _candidates(doc)
73
+ if not q or not cands:
74
+ return {"status": "no_match", "reason": "empty query or no candidates"}
75
+
76
+ tokens = [t for t in q.replace("/", " ").replace("_", " ").split() if t]
77
+
78
+ scored: List[Tuple[float, Candidate]] = []
79
+ for c in cands:
80
+ hay = " ".join([c.metric_id, c.canonical_name, c.description]).lower()
81
+ score = 0.0
82
+ if q in hay:
83
+ score += 2.0
84
+ for t in tokens:
85
+ if t in hay:
86
+ score += 1.0
87
+ if team and team in {t.lower() for t in c.tags}:
88
+ score += 1.5
89
+ scored.append((score, c))
90
+
91
+ scored.sort(key=lambda x: (-x[0], x[1].metric_id))
92
+ best_score = scored[0][0]
93
+ if best_score <= 0:
94
+ return {"status": "no_match", "reason": "no overlap with any metric"}
95
+
96
+ top = [c for s, c in scored if s == best_score]
97
+ if len(top) == 1:
98
+ return {
99
+ "status": "resolved",
100
+ "resolved_metric": {
101
+ "metric_id": top[0].metric_id,
102
+ "canonical_name": top[0].canonical_name,
103
+ "description": top[0].description,
104
+ },
105
+ "confidence": min(1.0, 0.6 + 0.1 * best_score),
106
+ "reason": "deterministic match",
107
+ }
108
+
109
+ candidates = top[:5]
110
+ return {
111
+ "status": "ambiguous",
112
+ "candidates": [
113
+ {"metric_id": c.metric_id, "canonical_name": c.canonical_name, "description": c.description}
114
+ for c in candidates
115
+ ],
116
+ "confidence": 0.55,
117
+ "reason": "multiple metrics match equally well",
118
+ }
119
+
120
+
121
+ def to_semantic_contract(resolution: Dict[str, Any], *, context: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
122
+ """
123
+ Convert a resolved metric into a minimal, stable 'SemanticContract' dict.
124
+ """
125
+ ctx = context or {}
126
+ if resolution.get("status") != "resolved":
127
+ raise ValueError("resolution must be status=resolved to build a contract")
128
+
129
+ m = resolution.get("resolved_metric") or {}
130
+ metric_id = m.get("metric_id")
131
+ definition = {"display": m.get("canonical_name") or metric_id, "description": m.get("description") or ""}
132
+ return {
133
+ "identity": {"metric_id": metric_id},
134
+ "definition": definition,
135
+ "context": ctx,
136
+ "source": {"system": "continuum_local"},
137
+ }
138
+
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: continuum-local
3
+ Version: 0.1.0
4
+ Summary: Local, no-backend resolver for Continuum demos
5
+ Author: Continuum
6
+ License: Apache License
7
+ Version 2.0, January 2004
8
+ http://www.apache.org/licenses/
9
+
10
+ This package is licensed under the Apache License, Version 2.0.
11
+ See the repository root `LICENSE` for the full text.
12
+
13
+
14
+ Project-URL: Homepage, https://getcontinuum.ai
15
+ Project-URL: Documentation, https://docs.getcontinuum.ai
16
+ Project-URL: Repository, https://github.com/get-continuum/continuum
17
+ Project-URL: Issues, https://github.com/get-continuum/continuum/issues
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ License-File: NOTICE
22
+ Requires-Dist: pyyaml>=6.0.0
23
+ Dynamic: license-file
24
+
25
+ # Continuum Local (Python)
26
+
27
+ Local (no-backend) semantic resolver for demos/tests.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pip install continuum-local
33
+ ```
34
+
35
+ ## CLI
36
+
37
+ ```bash
38
+ continuum-local resolve --semantics demo/semantics.yaml --query "revenue" --context '{"team":"marketing"}'
39
+ ```
40
+
41
+ ## Library
42
+
43
+ ```python
44
+ from continuum_local import load_semantics, resolve, to_semantic_contract
45
+
46
+ doc = load_semantics("demo/semantics.yaml")
47
+ out = resolve(doc, query="revenue", context={"team": "marketing"})
48
+ print(out)
49
+
50
+ if out.get("status") == "resolved":
51
+ print(to_semantic_contract(out, context={"team": "marketing"}))
52
+ ```
53
+
@@ -0,0 +1,10 @@
1
+ continuum_local/__init__.py,sha256=AOVj1L5wrMkL9Q7DpTf7YXCHz9v2Oih_obREZvhSfUg,134
2
+ continuum_local/cli.py,sha256=3lw5ljOAPAnH7vrM9CYk6sU7l7tVNrOR0HhPHs6khGA,1397
3
+ continuum_local/resolver.py,sha256=fS6ddw1Hkd-NPwjFUC262DVEAaCPNlb8apTmFl_BE2M,4378
4
+ continuum_local-0.1.0.dist-info/licenses/LICENSE,sha256=NvsPqSrcQawz2VDnrHb8f-ztZ-gNQSl_9YV5DL5wUEY,192
5
+ continuum_local-0.1.0.dist-info/licenses/NOTICE,sha256=xZNJ82mX2vdADocownkFWzlek_Q1QjXxnyTel_wblOc,40
6
+ continuum_local-0.1.0.dist-info/METADATA,sha256=66FNvCLh1QQD9TKNesaZpfuYKeU7z-12v0VfQ2M26CQ,1385
7
+ continuum_local-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
+ continuum_local-0.1.0.dist-info/entry_points.txt,sha256=ygfmZzk2rYzigfpbInaJDpfqX8jLgnw4RavFB1Ac1KQ,61
9
+ continuum_local-0.1.0.dist-info/top_level.txt,sha256=n5nzvPD_96ybFgOV3V09DUZ7MyQ4tHC9A9FBl-RzSAQ,16
10
+ continuum_local-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ continuum-local = continuum_local.cli:main
@@ -0,0 +1,7 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ This package is licensed under the Apache License, Version 2.0.
6
+ See the repository root `LICENSE` for the full text.
7
+
@@ -0,0 +1,3 @@
1
+ Continuum OSS
2
+ Copyright 2026 Continuum
3
+
@@ -0,0 +1 @@
1
+ continuum_local