continuum-local 0.1.0__tar.gz
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.
- continuum_local-0.1.0/LICENSE +7 -0
- continuum_local-0.1.0/MANIFEST.in +4 -0
- continuum_local-0.1.0/NOTICE +3 -0
- continuum_local-0.1.0/PKG-INFO +53 -0
- continuum_local-0.1.0/README.md +29 -0
- continuum_local-0.1.0/pyproject.toml +31 -0
- continuum_local-0.1.0/setup.cfg +4 -0
- continuum_local-0.1.0/src/continuum_local/__init__.py +4 -0
- continuum_local-0.1.0/src/continuum_local/cli.py +43 -0
- continuum_local-0.1.0/src/continuum_local/resolver.py +138 -0
- continuum_local-0.1.0/src/continuum_local.egg-info/PKG-INFO +53 -0
- continuum_local-0.1.0/src/continuum_local.egg-info/SOURCES.txt +14 -0
- continuum_local-0.1.0/src/continuum_local.egg-info/dependency_links.txt +1 -0
- continuum_local-0.1.0/src/continuum_local.egg-info/entry_points.txt +2 -0
- continuum_local-0.1.0/src/continuum_local.egg-info/requires.txt +1 -0
- continuum_local-0.1.0/src/continuum_local.egg-info/top_level.txt +1 -0
|
@@ -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,29 @@
|
|
|
1
|
+
# Continuum Local (Python)
|
|
2
|
+
|
|
3
|
+
Local (no-backend) semantic resolver for demos/tests.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install continuum-local
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## CLI
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
continuum-local resolve --semantics demo/semantics.yaml --query "revenue" --context '{"team":"marketing"}'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Library
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from continuum_local import load_semantics, resolve, to_semantic_contract
|
|
21
|
+
|
|
22
|
+
doc = load_semantics("demo/semantics.yaml")
|
|
23
|
+
out = resolve(doc, query="revenue", context={"team": "marketing"})
|
|
24
|
+
print(out)
|
|
25
|
+
|
|
26
|
+
if out.get("status") == "resolved":
|
|
27
|
+
print(to_semantic_contract(out, context={"team": "marketing"}))
|
|
28
|
+
```
|
|
29
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "continuum-local"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Local, no-backend resolver for Continuum demos"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {file = "LICENSE"}
|
|
12
|
+
authors = [{name = "Continuum"}]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"pyyaml>=6.0.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Homepage = "https://getcontinuum.ai"
|
|
19
|
+
Documentation = "https://docs.getcontinuum.ai"
|
|
20
|
+
Repository = "https://github.com/get-continuum/continuum"
|
|
21
|
+
Issues = "https://github.com/get-continuum/continuum/issues"
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
continuum-local = "continuum_local.cli:main"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools]
|
|
27
|
+
package-dir = {"" = "src"}
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["src"]
|
|
31
|
+
|
|
@@ -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,14 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
NOTICE
|
|
4
|
+
README.md
|
|
5
|
+
pyproject.toml
|
|
6
|
+
src/continuum_local/__init__.py
|
|
7
|
+
src/continuum_local/cli.py
|
|
8
|
+
src/continuum_local/resolver.py
|
|
9
|
+
src/continuum_local.egg-info/PKG-INFO
|
|
10
|
+
src/continuum_local.egg-info/SOURCES.txt
|
|
11
|
+
src/continuum_local.egg-info/dependency_links.txt
|
|
12
|
+
src/continuum_local.egg-info/entry_points.txt
|
|
13
|
+
src/continuum_local.egg-info/requires.txt
|
|
14
|
+
src/continuum_local.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyyaml>=6.0.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
continuum_local
|