cometric 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 (83) hide show
  1. cometric/__init__.py +50 -0
  2. cometric/cli.py +210 -0
  3. cometric/core/__init__.py +0 -0
  4. cometric/core/api.py +835 -0
  5. cometric/core/engine.py +1386 -0
  6. cometric/core/enums.py +273 -0
  7. cometric/core/fingerprint.py +91 -0
  8. cometric/core/issues.py +68 -0
  9. cometric/core/ledger.py +165 -0
  10. cometric/core/models.py +450 -0
  11. cometric/core/money.py +110 -0
  12. cometric/core/packs.py +446 -0
  13. cometric/core/periods.py +102 -0
  14. cometric/core/predicates.py +139 -0
  15. cometric/core/quantities.py +138 -0
  16. cometric/core/schema.py +97 -0
  17. cometric/core/templates.py +338 -0
  18. cometric/core/tiers.py +78 -0
  19. cometric/core/validation.py +738 -0
  20. cometric/examples/case_a.json +293 -0
  21. cometric/examples/case_b.json +212 -0
  22. cometric/examples/case_c.json +233 -0
  23. cometric/examples/case_d.json +253 -0
  24. cometric/examples/case_e.json +258 -0
  25. cometric/examples/case_f.json +298 -0
  26. cometric/examples/case_g.json +243 -0
  27. cometric/examples/case_h.json +271 -0
  28. cometric/examples/spec_example.json +134 -0
  29. cometric/mcp/__init__.py +0 -0
  30. cometric/mcp/__main__.py +3 -0
  31. cometric/mcp/server.py +470 -0
  32. cometric/packs/alsp/concepts/provider_types.yaml +154 -0
  33. cometric/packs/alsp/concepts/service_categories.yaml +132 -0
  34. cometric/packs/alsp/concepts/unit_templates.yaml +132 -0
  35. cometric/packs/alsp/coverage.yaml +17 -0
  36. cometric/packs/alsp/evidence/claims.yaml +446 -0
  37. cometric/packs/alsp/evidence/sources.yaml +545 -0
  38. cometric/packs/alsp/overlays/levers.yaml +82 -0
  39. cometric/packs/alsp/overlays/mechanisms.yaml +19 -0
  40. cometric/packs/alsp/overlays/metrics.yaml +15 -0
  41. cometric/packs/alsp/overlays/pricing_models.yaml +186 -0
  42. cometric/packs/alsp/pack.yaml +9 -0
  43. cometric/packs/alsp/rules/rules.yaml +303 -0
  44. cometric/packs/base/concepts/automation_structures.yaml +207 -0
  45. cometric/packs/base/concepts/billing_bases.yaml +239 -0
  46. cometric/packs/base/concepts/cost_categories.yaml +494 -0
  47. cometric/packs/base/concepts/dimensions.yaml +404 -0
  48. cometric/packs/base/concepts/fee_components.yaml +105 -0
  49. cometric/packs/base/concepts/layers.yaml +146 -0
  50. cometric/packs/base/concepts/levers.yaml +1449 -0
  51. cometric/packs/base/concepts/mechanisms.yaml +295 -0
  52. cometric/packs/base/concepts/metrics.yaml +1149 -0
  53. cometric/packs/base/concepts/negotiation_exchanges.yaml +226 -0
  54. cometric/packs/base/concepts/offers.yaml +88 -0
  55. cometric/packs/base/concepts/pricing_models.yaml +821 -0
  56. cometric/packs/base/concepts/schema_map.yaml +1100 -0
  57. cometric/packs/base/concepts/selection_framework.yaml +215 -0
  58. cometric/packs/base/concepts/selection_heuristics.yaml +285 -0
  59. cometric/packs/base/coverage.yaml +33 -0
  60. cometric/packs/base/evidence/claims.yaml +442 -0
  61. cometric/packs/base/evidence/sources.yaml +293 -0
  62. cometric/packs/base/excluded.yaml +42 -0
  63. cometric/packs/base/pack.yaml +10 -0
  64. cometric/packs/base/rules/rules.yaml +521 -0
  65. cometric/packs/consulting/concepts/consulting_concepts.yaml +82 -0
  66. cometric/packs/consulting/concepts/provider_types.yaml +35 -0
  67. cometric/packs/consulting/concepts/service_categories.yaml +41 -0
  68. cometric/packs/consulting/concepts/unit_templates.yaml +36 -0
  69. cometric/packs/consulting/coverage.yaml +17 -0
  70. cometric/packs/consulting/overlays/pricing_models.yaml +29 -0
  71. cometric/packs/consulting/pack.yaml +11 -0
  72. cometric/packs/consulting/rules/rules.yaml +45 -0
  73. cometric/packs/consulting/templates/capped_tm.json +132 -0
  74. cometric/packs/consulting/templates/fixed_fee_plus_success.json +163 -0
  75. cometric/packs/consulting/templates/phased_fixed_fee.json +133 -0
  76. cometric/packs/consulting/templates/retainer_hours_drawdown.json +144 -0
  77. cometric/packs/consulting/templates/role_based_tm.json +123 -0
  78. cometric/py.typed +0 -0
  79. cometric-0.1.0.dist-info/METADATA +137 -0
  80. cometric-0.1.0.dist-info/RECORD +83 -0
  81. cometric-0.1.0.dist-info/WHEEL +4 -0
  82. cometric-0.1.0.dist-info/entry_points.txt +2 -0
  83. cometric-0.1.0.dist-info/licenses/LICENSE +202 -0
cometric/__init__.py ADDED
@@ -0,0 +1,50 @@
1
+ """Cometric: Commercial Intelligence Engine.
2
+
3
+ Model commercial arrangements, simulate engagement economics, and search for structures that
4
+ balance profitability, risk, and client value. All arithmetic is deterministic decimal code.
5
+ """
6
+
7
+ from .core.api import (
8
+ break_even,
9
+ calculate_periods,
10
+ check_commercial_constraints,
11
+ compare_arrangements,
12
+ derive_risk_exposure,
13
+ explain_fee,
14
+ price_floor,
15
+ search_structures,
16
+ )
17
+ from .core.engine import ENGINE_VERSION
18
+ from .core.models import SCHEMA_VERSION
19
+ from .core.packs import describe_pack, list_packs, list_vocabulary, lookup_concept
20
+ from .core.schema import describe_schema
21
+ from .core.templates import get_template, list_templates
22
+ from .core.validation import validate_engagement
23
+
24
+ try:
25
+ from importlib.metadata import PackageNotFoundError, version
26
+
27
+ __version__ = version("cometric")
28
+ except PackageNotFoundError: # running from a source tree without installation
29
+ __version__ = "0.0.0"
30
+
31
+ __all__ = [
32
+ "ENGINE_VERSION",
33
+ "SCHEMA_VERSION",
34
+ "break_even",
35
+ "calculate_periods",
36
+ "check_commercial_constraints",
37
+ "compare_arrangements",
38
+ "derive_risk_exposure",
39
+ "describe_pack",
40
+ "describe_schema",
41
+ "explain_fee",
42
+ "get_template",
43
+ "list_packs",
44
+ "list_templates",
45
+ "list_vocabulary",
46
+ "lookup_concept",
47
+ "price_floor",
48
+ "search_structures",
49
+ "validate_engagement",
50
+ ]
cometric/cli.py ADDED
@@ -0,0 +1,210 @@
1
+ """Command-line interface. Every command prints JSON; inputs are JSON or YAML files.
2
+
3
+ A file may hold a bare object, or a casebook fixture with `engagement` and `scenarios` keys.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ import json
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ import yaml
15
+
16
+ from . import __version__
17
+ from .core import api, packs, schema, templates
18
+ from .core.validation import validate_engagement
19
+
20
+
21
+ def _read(path: str) -> Any:
22
+ text = Path(path).read_text()
23
+ return yaml.safe_load(text) if path.endswith((".yaml", ".yml")) else json.loads(text)
24
+
25
+
26
+ def _engagement(path: str) -> Any:
27
+ doc = _read(path)
28
+ return doc["engagement"] if isinstance(doc, dict) and "engagement" in doc else doc
29
+
30
+
31
+ def _scenarios(path: str | None, fallback: str, ids: list[str] | None = None) -> list[Any]:
32
+ doc = _read(path or fallback)
33
+ if isinstance(doc, dict) and "scenarios" in doc:
34
+ found = doc["scenarios"]
35
+ elif isinstance(doc, list):
36
+ found = doc
37
+ else:
38
+ found = [doc]
39
+ if ids:
40
+ found = [s for s in found if s.get("id") in ids]
41
+ return found
42
+
43
+
44
+ def _policy(value: str | None, engagement_file: str) -> Any:
45
+ if value is None:
46
+ return None
47
+ if Path(value).exists():
48
+ return _read(value)
49
+ doc = _read(engagement_file)
50
+ if isinstance(doc, dict) and value in (doc.get("cost_policies") or {}):
51
+ return doc["cost_policies"][value]
52
+ return value
53
+
54
+
55
+ def _print(obj: Any) -> None:
56
+ json.dump(obj, sys.stdout, indent=2, default=str)
57
+ sys.stdout.write("\n")
58
+
59
+
60
+ def main(argv: list[str] | None = None) -> int:
61
+ p = argparse.ArgumentParser(prog="cometric", description="Commercial Intelligence Engine")
62
+ p.add_argument("--version", action="version", version=__version__)
63
+ sub = p.add_subparsers(dest="cmd", required=True)
64
+
65
+ v = sub.add_parser("validate", help="validate an engagement")
66
+ v.add_argument("engagement")
67
+
68
+ c = sub.add_parser("calculate", help="monthly fees, costs, and metrics")
69
+ c.add_argument("engagement")
70
+ c.add_argument("--scenarios", help="file with scenario(s); defaults to the engagement file")
71
+ c.add_argument("--scenario", action="append", help="scenario id to run (repeatable)")
72
+ c.add_argument("--cost-policy", help="file, a policy name inside a fixture, or 'reference'")
73
+ c.add_argument("--detail", default="summary", choices=["summary", "periods", "line_items"])
74
+
75
+ e = sub.add_parser("explain", help="line-item trace for one period")
76
+ e.add_argument("engagement")
77
+ e.add_argument("--period", required=True)
78
+ e.add_argument("--scenarios")
79
+ e.add_argument("--scenario")
80
+ e.add_argument("--component")
81
+ e.add_argument("--cost-policy")
82
+
83
+ cmp_ = sub.add_parser("compare", help="compare engagements across the same scenarios")
84
+ cmp_.add_argument("engagements", nargs="+")
85
+ cmp_.add_argument("--scenarios", required=True)
86
+ cmp_.add_argument("--cost-policy", required=True)
87
+
88
+ s = sub.add_parser("search", help="structure search from a search_spec file")
89
+ s.add_argument("spec")
90
+
91
+ f = sub.add_parser("floor", help="price floor at each scenario volume")
92
+ f.add_argument("engagement")
93
+ f.add_argument("--scenarios")
94
+ f.add_argument("--cost-policy", required=True)
95
+ f.add_argument("--target-margin", required=True)
96
+ f.add_argument("--risk-premium", default="0")
97
+
98
+ b = sub.add_parser("break-even", help="driver volume at zero contribution")
99
+ b.add_argument("engagement")
100
+ b.add_argument("--driver", required=True)
101
+ b.add_argument("--scenarios")
102
+ b.add_argument("--scenario")
103
+ b.add_argument("--cost-policy", required=True)
104
+
105
+ sch = sub.add_parser("schema", help="JSON Schema for a model")
106
+ sch.add_argument("model", choices=schema.model_names())
107
+
108
+ sub.add_parser("packs", help="list installed packs")
109
+ pv = sub.add_parser("validate-pack", help="validate a pack's data files")
110
+ pv.add_argument("pack")
111
+ lk = sub.add_parser("lookup", help="look up a concept by id or text")
112
+ lk.add_argument("query")
113
+ lk.add_argument("--pack")
114
+
115
+ sub.add_parser("templates", help="list templates")
116
+ t = sub.add_parser("template", help="print a template")
117
+ t.add_argument("template_id")
118
+
119
+ m = sub.add_parser("mcp", help="run the MCP server")
120
+ m.add_argument("--transport", default="stdio", choices=["stdio", "http"])
121
+ m.add_argument("--host", default="127.0.0.1")
122
+ m.add_argument("--port", type=int, default=8765)
123
+
124
+ a = p.parse_args(argv)
125
+
126
+ if a.cmd == "validate":
127
+ out = validate_engagement(_engagement(a.engagement))
128
+ _print(out)
129
+ return 0 if out["valid"] else 1
130
+ if a.cmd == "calculate":
131
+ eng = _engagement(a.engagement)
132
+ results = [
133
+ api.calculate_periods(eng, sc, _policy(a.cost_policy, a.engagement), a.detail)
134
+ for sc in _scenarios(a.scenarios, a.engagement, a.scenario)
135
+ ]
136
+ _print(results[0] if len(results) == 1 else results)
137
+ return 0 if all(r["status"] in ("ok", "infeasible") for r in results) else 1
138
+ if a.cmd == "explain":
139
+ sc = _scenarios(a.scenarios, a.engagement, [a.scenario] if a.scenario else None)[0]
140
+ _print(
141
+ api.explain_fee(
142
+ _engagement(a.engagement),
143
+ sc,
144
+ _policy(a.cost_policy, a.engagement),
145
+ a.period,
146
+ a.component,
147
+ )
148
+ )
149
+ return 0
150
+ if a.cmd == "compare":
151
+ cands = [{"id": Path(x).stem, "engagement": _engagement(x)} for x in a.engagements]
152
+ _print(
153
+ api.compare_arrangements(
154
+ cands,
155
+ _scenarios(a.scenarios, a.scenarios),
156
+ _policy(a.cost_policy, a.engagements[0]),
157
+ )
158
+ )
159
+ return 0
160
+ if a.cmd == "search":
161
+ _print(api.search_structures(_read(a.spec)))
162
+ return 0
163
+ if a.cmd == "floor":
164
+ _print(
165
+ api.price_floor(
166
+ _engagement(a.engagement),
167
+ _scenarios(a.scenarios, a.engagement),
168
+ _policy(a.cost_policy, a.engagement),
169
+ a.target_margin,
170
+ a.risk_premium,
171
+ )
172
+ )
173
+ return 0
174
+ if a.cmd == "break-even":
175
+ sc = _scenarios(a.scenarios, a.engagement, [a.scenario] if a.scenario else None)[0]
176
+ _print(
177
+ api.break_even(
178
+ _engagement(a.engagement), sc, _policy(a.cost_policy, a.engagement), a.driver
179
+ )
180
+ )
181
+ return 0
182
+ if a.cmd == "schema":
183
+ _print(schema.describe_schema(a.model))
184
+ return 0
185
+ if a.cmd == "packs":
186
+ _print(packs.list_packs())
187
+ return 0
188
+ if a.cmd == "validate-pack":
189
+ problems = packs.validate_pack(a.pack)
190
+ _print({"pack": a.pack, "valid": not problems, "problems": problems})
191
+ return 0 if not problems else 1
192
+ if a.cmd == "lookup":
193
+ _print(packs.lookup_concept(a.query, a.pack))
194
+ return 0
195
+ if a.cmd == "templates":
196
+ _print(templates.list_templates())
197
+ return 0
198
+ if a.cmd == "template":
199
+ _print(templates.get_template(a.template_id))
200
+ return 0
201
+ if a.cmd == "mcp":
202
+ from .mcp.server import main as mcp_main
203
+
204
+ mcp_main("stdio" if a.transport == "stdio" else "http", a.host, a.port)
205
+ return 0
206
+ return 2
207
+
208
+
209
+ if __name__ == "__main__":
210
+ raise SystemExit(main())
File without changes