mcp-efficiency-engine 0.1.0 → 0.1.2

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 (191) hide show
  1. package/.githooks/post-commit +20 -0
  2. package/.github/workflows/autodocs-sync.yml +2 -15
  3. package/.github/workflows/release-npm.yml +111 -0
  4. package/FINAL_USAGE_GUIDE.md +78 -0
  5. package/README.md +231 -0
  6. package/autodocs/README.md +6 -0
  7. package/bin/install-host.js +26 -1
  8. package/observability/metrics.md +33 -0
  9. package/observability/scoring-rubric.md +20 -0
  10. package/orchestrator/wiki/graph_consolidator.py +60 -1
  11. package/orchestrator/wiki/incremental_engine.py +23 -1
  12. package/orchestrator/wiki/plugin_manager.py +87 -22
  13. package/package.json +10 -2
  14. package/requirements.txt +2 -0
  15. package/scripts/intake/resolve-routing.py +124 -87
  16. package/scripts/intake/validate-database-routing.py +13 -4
  17. package/scripts/intake/validate-rag-routing.py +4 -3
  18. package/scripts/learning/learning-loop-report.py +28 -10
  19. package/scripts/ops/bye.ps1 +30 -0
  20. package/scripts/ops/hi.ps1 +13 -0
  21. package/scripts/ops/llm-token-probe.py +103 -39
  22. package/scripts/ops/post-commit-refresh.ps1 +110 -0
  23. package/scripts/ops/publish-langsmith-kpis.py +302 -0
  24. package/scripts/ops/telemetry-benchmark.py +184 -0
  25. package/scripts/setup/install-project-hooks.ps1 +33 -0
  26. package/scripts/setup/setup-prerequisites.ps1 +42 -15
  27. package/scripts/wiki/compiler_main.py +108 -76
  28. package/scripts/wiki/providers/repo_content_provider.py +51 -0
  29. package/scripts/wiki/validator.py +40 -15
  30. package/telemetry/__init__.py +4 -0
  31. package/telemetry/bootstrap.py +47 -0
  32. package/telemetry/collector/engine.py +261 -0
  33. package/telemetry/config.json +20 -0
  34. package/telemetry/config.py +131 -0
  35. package/telemetry/context/propagation.py +85 -0
  36. package/telemetry/events/catalog.py +24 -0
  37. package/telemetry/events/factory.py +42 -0
  38. package/telemetry/exporters/base.py +16 -0
  39. package/telemetry/exporters/console/exporter.py +30 -0
  40. package/telemetry/exporters/json/exporter.py +34 -0
  41. package/telemetry/exporters/langsmith/exporter.py +415 -0
  42. package/telemetry/metrics/aggregator.py +60 -0
  43. package/telemetry/scoring/efficiency.py +68 -0
  44. package/telemetry/tracing/span.py +43 -0
  45. package/observability/evals/chat-token-usage-report.json +0 -64
  46. package/observability/evals/chat-token-usage-report.md +0 -33
  47. package/observability/evals/iteration-value-report.json +0 -76
  48. package/observability/evals/iteration-value-report.md +0 -39
  49. package/observability/evals/learning-loop-report.json +0 -268
  50. package/observability/evals/learning-loop-report.md +0 -37
  51. package/observability/evals/repo-intake-eval-cases.md +0 -23
  52. package/observability/evals/routing-eval-cases.json +0 -68
  53. package/observability/evals/routing-eval-cases.md +0 -25
  54. package/observability/evals/routing-eval-report.json +0 -72
  55. package/observability/logs/copilot-usage-ingest-report.json +0 -13
  56. package/observability/logs/iteration-metrics.jsonl +0 -75
  57. package/observability/logs/learning-feedback.jsonl +0 -152
  58. package/observability/logs/routing-decisions.jsonl +0 -333
  59. package/observability/logs/session/agent-pipeline-preflight-20260701-004146.log +0 -8
  60. package/observability/logs/session/agent-pipeline-preflight-20260701-005554.log +0 -8
  61. package/observability/logs/session/agent-pipeline-preflight-20260701-010351.log +0 -8
  62. package/observability/logs/session/agent-pipeline-preflight-20260704-145829.log +0 -8
  63. package/observability/logs/session/agent-pipeline-preflight-20260704-150021.log +0 -8
  64. package/observability/logs/session/agent-pipeline-preflight-20260704-154328.log +0 -8
  65. package/observability/logs/session/bye-20260629-221513.json +0 -39
  66. package/observability/logs/session/bye-20260629-221545.json +0 -59
  67. package/observability/logs/session/bye-20260629-222007.json +0 -52
  68. package/observability/logs/session/bye-20260629-222133.json +0 -59
  69. package/observability/logs/session/bye-20260629-222412.json +0 -45
  70. package/observability/logs/session/bye-20260629-223224.json +0 -52
  71. package/observability/logs/session/bye-20260629-224050.json +0 -59
  72. package/observability/logs/session/bye-20260630-105732.json +0 -24
  73. package/observability/logs/session/bye-20260630-211536.json +0 -87
  74. package/observability/logs/session/bye-20260630-223423.json +0 -87
  75. package/observability/logs/session/bye-20260630-232534.json +0 -87
  76. package/observability/logs/session/bye-20260701-003638.json +0 -126
  77. package/observability/logs/session/bye-20260701-003849.json +0 -126
  78. package/observability/logs/session/bye-20260701-004315.json +0 -138
  79. package/observability/logs/session/bye-20260701-004559.json +0 -76
  80. package/observability/logs/session/bye-20260701-005913.json +0 -138
  81. package/observability/logs/session/bye-20260701-010753.json +0 -138
  82. package/observability/logs/session/bye-20260703-194106.json +0 -24
  83. package/observability/logs/session/bye-20260703-233443.json +0 -76
  84. package/observability/logs/session/bye-20260703-233653.json +0 -76
  85. package/observability/logs/session/bye-20260704-150317.json +0 -152
  86. package/observability/logs/session/bye-20260704-150929.json +0 -75
  87. package/observability/logs/session/bye-20260704-151050.json +0 -75
  88. package/observability/logs/session/bye-20260705-115458.json +0 -165
  89. package/observability/logs/session/codegraph-status-20260701-004228.log +0 -39
  90. package/observability/logs/session/codegraph-status-20260701-004310.log +0 -39
  91. package/observability/logs/session/codegraph-status-20260701-005637.log +0 -39
  92. package/observability/logs/session/codegraph-status-20260701-005908.log +0 -39
  93. package/observability/logs/session/codegraph-status-20260701-010444.log +0 -39
  94. package/observability/logs/session/codegraph-status-20260701-010747.log +0 -39
  95. package/observability/logs/session/codegraph-status-20260704-150137.log +0 -26
  96. package/observability/logs/session/codegraph-status-20260704-150313.log +0 -28
  97. package/observability/logs/session/codegraph-status-20260704-154445.log +0 -26
  98. package/observability/logs/session/codegraph-status-20260705-115454.log +0 -27
  99. package/observability/logs/session/codegraph-sync-20260701-004211.log +0 -88
  100. package/observability/logs/session/codegraph-sync-20260701-004241.log +0 -82
  101. package/observability/logs/session/codegraph-sync-20260701-005620.log +0 -89
  102. package/observability/logs/session/codegraph-sync-20260701-005840.log +0 -88
  103. package/observability/logs/session/codegraph-sync-20260701-010419.log +0 -93
  104. package/observability/logs/session/codegraph-sync-20260701-010609.log +0 -90
  105. package/observability/logs/session/codegraph-sync-20260704-145856.log +0 -70
  106. package/observability/logs/session/codegraph-sync-20260704-150047.log +0 -53
  107. package/observability/logs/session/codegraph-sync-20260704-150204.log +0 -44
  108. package/observability/logs/session/codegraph-sync-20260704-154353.log +0 -26
  109. package/observability/logs/session/codegraph-sync-20260705-115337.log +0 -32
  110. package/observability/logs/session/hi-20260629-221459.json +0 -38
  111. package/observability/logs/session/hi-20260629-221945.json +0 -59
  112. package/observability/logs/session/hi-20260629-222108.json +0 -66
  113. package/observability/logs/session/hi-20260629-222352.json +0 -45
  114. package/observability/logs/session/hi-20260629-225719.json +0 -59
  115. package/observability/logs/session/hi-20260629-233722.json +0 -52
  116. package/observability/logs/session/hi-20260629-233913.json +0 -198
  117. package/observability/logs/session/hi-20260630-110156.json +0 -177
  118. package/observability/logs/session/hi-20260630-191243.json +0 -184
  119. package/observability/logs/session/hi-20260630-230050.json +0 -234
  120. package/observability/logs/session/hi-20260630-230523.json +0 -227
  121. package/observability/logs/session/hi-20260630-230855.json +0 -227
  122. package/observability/logs/session/hi-20260630-231638.json +0 -227
  123. package/observability/logs/session/hi-20260630-231756.json +0 -219
  124. package/observability/logs/session/hi-20260701-002842.json +0 -234
  125. package/observability/logs/session/hi-20260701-003458.json +0 -282
  126. package/observability/logs/session/hi-20260701-004233.json +0 -290
  127. package/observability/logs/session/hi-20260701-004554.json +0 -236
  128. package/observability/logs/session/hi-20260701-005641.json +0 -290
  129. package/observability/logs/session/hi-20260701-010450.json +0 -290
  130. package/observability/logs/session/hi-20260703-233854.json +0 -312
  131. package/observability/logs/session/hi-20260703-234448.json +0 -318
  132. package/observability/logs/session/hi-20260704-145907.json +0 -353
  133. package/observability/logs/session/hi-20260704-150141.json +0 -392
  134. package/observability/logs/session/hi-20260704-154448.json +0 -392
  135. package/observability/logs/session/iteration-value-refresh-20260701-004309.log +0 -3
  136. package/observability/logs/session/iteration-value-refresh-20260701-005907.log +0 -3
  137. package/observability/logs/session/iteration-value-refresh-20260701-010747.log +0 -3
  138. package/observability/logs/session/iteration-value-refresh-20260704-150312.log +0 -3
  139. package/observability/logs/session/iteration-value-refresh-20260705-115453.log +0 -3
  140. package/observability/logs/session/learning-loop-refresh-20260701-004309.log +0 -3
  141. package/observability/logs/session/learning-loop-refresh-20260701-005906.log +0 -3
  142. package/observability/logs/session/learning-loop-refresh-20260701-010745.log +0 -3
  143. package/observability/logs/session/learning-loop-refresh-20260704-150309.log +0 -3
  144. package/observability/logs/session/learning-loop-refresh-20260705-115450.log +0 -3
  145. package/observability/logs/session/project-notes-refresh-20260701-004227.log +0 -1
  146. package/observability/logs/session/project-notes-refresh-20260701-004308.log +0 -1
  147. package/observability/logs/session/project-notes-refresh-20260701-005636.log +0 -1
  148. package/observability/logs/session/project-notes-refresh-20260701-005906.log +0 -1
  149. package/observability/logs/session/project-notes-refresh-20260701-010443.log +0 -1
  150. package/observability/logs/session/project-notes-refresh-20260701-010744.log +0 -1
  151. package/observability/logs/session/project-notes-refresh-20260704-150133.log +0 -1
  152. package/observability/logs/session/project-notes-refresh-20260704-150309.log +0 -1
  153. package/observability/logs/session/project-notes-refresh-20260704-154442.log +0 -1
  154. package/observability/logs/session/project-notes-refresh-20260705-115449.log +0 -1
  155. package/observability/logs/session/repo-intake-20260701-004218.log +0 -6
  156. package/observability/logs/session/repo-intake-20260701-005626.log +0 -6
  157. package/observability/logs/session/repo-intake-20260701-010433.log +0 -6
  158. package/observability/logs/session/repo-intake-20260704-150053.log +0 -6
  159. package/observability/logs/session/repo-intake-20260704-154404.log +0 -6
  160. package/observability/logs/session/repo-intake-refresh-20260701-004247.log +0 -6
  161. package/observability/logs/session/repo-intake-refresh-20260701-005847.log +0 -6
  162. package/observability/logs/session/repo-intake-refresh-20260701-010615.log +0 -6
  163. package/observability/logs/session/repo-intake-refresh-20260704-150209.log +0 -6
  164. package/observability/logs/session/repo-intake-refresh-20260705-115342.log +0 -6
  165. package/observability/logs/session/repo-registry-validation-20260701-004234.log +0 -2
  166. package/observability/logs/session/repo-registry-validation-20260701-004556.log +0 -2
  167. package/observability/logs/session/repo-registry-validation-20260701-005833.log +0 -2
  168. package/observability/logs/session/repo-registry-validation-20260701-010601.log +0 -2
  169. package/observability/logs/session/repo-registry-validation-20260704-150155.log +0 -2
  170. package/observability/logs/session/repo-registry-validation-20260705-115323.log +0 -2
  171. package/observability/logs/session/repomix-refresh-20260701-003823.log +0 -4236
  172. package/observability/logs/session/repomix-refresh-20260701-004251.log +0 -4275
  173. package/observability/logs/session/repomix-refresh-20260701-005850.log +0 -4248
  174. package/observability/logs/session/repomix-refresh-20260701-010619.log +0 -6531
  175. package/observability/logs/session/repomix-refresh-20260704-150239.log +0 -652
  176. package/observability/logs/session/repomix-refresh-20260705-115419.log +0 -178
  177. package/observability/logs/session/routing-evals-20260701-004221.log +0 -2
  178. package/observability/logs/session/routing-evals-20260701-004235.log +0 -2
  179. package/observability/logs/session/routing-evals-20260701-005629.log +0 -2
  180. package/observability/logs/session/routing-evals-20260701-005834.log +0 -2
  181. package/observability/logs/session/routing-evals-20260701-010437.log +0 -2
  182. package/observability/logs/session/routing-evals-20260701-010603.log +0 -2
  183. package/observability/logs/session/routing-evals-20260704-150125.log +0 -2
  184. package/observability/logs/session/routing-evals-20260704-150157.log +0 -2
  185. package/observability/logs/session/routing-evals-20260704-154434.log +0 -2
  186. package/observability/logs/session/routing-evals-20260705-115327.log +0 -2
  187. package/observability/logs/session/sibling-discovery-refresh-20260701-004250.log +0 -3
  188. package/observability/logs/session/sibling-discovery-refresh-20260701-005850.log +0 -3
  189. package/observability/logs/session/sibling-discovery-refresh-20260701-010618.log +0 -3
  190. package/observability/logs/session/sibling-discovery-refresh-20260704-150238.log +0 -3
  191. package/observability/logs/session/sibling-discovery-refresh-20260705-115418.log +0 -3
@@ -2,11 +2,18 @@ from __future__ import annotations
2
2
 
3
3
  import argparse
4
4
  import json
5
+ import sys
5
6
  from collections import defaultdict
6
7
  from datetime import datetime, timezone
7
8
  from pathlib import Path
8
9
  from typing import Any
9
10
 
11
+ _REPO_ROOT = Path(__file__).resolve().parents[2]
12
+ if str(_REPO_ROOT) not in sys.path:
13
+ sys.path.insert(0, str(_REPO_ROOT))
14
+
15
+ from telemetry import build_telemetry_collector
16
+
10
17
 
11
18
  def utc_now() -> str:
12
19
  return datetime.now(timezone.utc).isoformat()
@@ -247,24 +254,35 @@ def main() -> int:
247
254
  args = parser.parse_args()
248
255
 
249
256
  repo_root = Path(__file__).resolve().parents[2]
257
+ collector = build_telemetry_collector(repo_root)
250
258
  in_path = (repo_root / args.input).resolve()
251
259
  feedback_path = (repo_root / args.feedback).resolve()
252
260
  out_json = (repo_root / args.out_json).resolve()
253
261
  out_md = (repo_root / args.out_md).resolve()
254
262
 
255
- events = parse_events(in_path)
256
- feedback = parse_feedback(feedback_path)
257
- report = build_report(events, str(in_path), feedback, str(feedback_path))
263
+ with collector.start_execution(operation="learning-loop-report", session_id="learning"):
264
+ with collector.start_span(name="learning.report.build", kind="INTERNAL"):
265
+ events = parse_events(in_path)
266
+ feedback = parse_feedback(feedback_path)
267
+ report = build_report(events, str(in_path), feedback, str(feedback_path))
268
+
269
+ out_json.parent.mkdir(parents=True, exist_ok=True)
270
+ out_md.parent.mkdir(parents=True, exist_ok=True)
271
+
272
+ out_json.write_text(json.dumps(report, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
273
+ out_md.write_text(report_to_markdown(report), encoding="utf-8")
258
274
 
259
- out_json.parent.mkdir(parents=True, exist_ok=True)
260
- out_md.parent.mkdir(parents=True, exist_ok=True)
275
+ kpis = report.get("kpis", {}) if isinstance(report.get("kpis", {}), dict) else {}
276
+ collector.record_metric("tool_invocations", float(report.get("total_events", 0)), unit="count")
277
+ collector.record_metric("fallback_rate", float(kpis.get("fallback_rate", 0.0)), unit="ratio")
278
+ collector.record_metric("grounded_rate", float(kpis.get("grounded_rate", 0.0)), unit="ratio")
279
+ collector.record_metric("confidence_avg", float(kpis.get("confidence_avg", 0.0)), unit="score")
261
280
 
262
- out_json.write_text(json.dumps(report, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
263
- out_md.write_text(report_to_markdown(report), encoding="utf-8")
281
+ print(f"Learning events processed: {len(events)}")
282
+ print(f"Report JSON: {out_json}")
283
+ print(f"Report MD: {out_md}")
264
284
 
265
- print(f"Learning events processed: {len(events)}")
266
- print(f"Report JSON: {out_json}")
267
- print(f"Report MD: {out_md}")
285
+ collector.shutdown()
268
286
  return 0
269
287
 
270
288
 
@@ -73,6 +73,26 @@ function Test-CommandAvailable {
73
73
  return [bool](Get-Command $Name -ErrorAction SilentlyContinue)
74
74
  }
75
75
 
76
+ function Ensure-SetupPrerequisites {
77
+ if ($script:SetupAttempted) {
78
+ if (-not $script:SetupSucceeded) {
79
+ throw 'setup-prerequisites was already attempted and failed earlier in this run'
80
+ }
81
+ return $true
82
+ }
83
+
84
+ $script:SetupAttempted = $true
85
+ Write-Host '[info] Missing prerequisite detected. Running setup-prerequisites automatically...' -ForegroundColor DarkYellow
86
+ & pwsh -NoProfile -ExecutionPolicy Bypass -File .\scripts\setup\setup-prerequisites.ps1 -PortableMode
87
+ if ($LASTEXITCODE -ne 0) {
88
+ $script:SetupSucceeded = $false
89
+ throw "setup-prerequisites failed with exit code $LASTEXITCODE"
90
+ }
91
+
92
+ $script:SetupSucceeded = $true
93
+ return $true
94
+ }
95
+
76
96
  function Invoke-ObservabilityRetentionCleanup {
77
97
  param(
78
98
  [string]$RepoRoot,
@@ -320,6 +340,8 @@ Set-Location $repoRoot
320
340
 
321
341
  $script:Steps = @()
322
342
  $script:HasFailures = $false
343
+ $script:SetupAttempted = $false
344
+ $script:SetupSucceeded = $false
323
345
  $script:StepLogs = [ordered]@{}
324
346
  $script:RetentionCleanupReport = [ordered]@{
325
347
  applied = $false
@@ -367,6 +389,10 @@ else {
367
389
 
368
390
  if (-not $SkipGraphRefresh) {
369
391
  Invoke-Step -Name 'Refresh codegraph index sync' -Action {
392
+ if (-not (Get-Command codegraph -ErrorAction SilentlyContinue)) {
393
+ [void](Ensure-SetupPrerequisites)
394
+ }
395
+
370
396
  if (-not (Get-Command codegraph -ErrorAction SilentlyContinue)) {
371
397
  throw 'codegraph command not found'
372
398
  }
@@ -548,6 +574,10 @@ else {
548
574
 
549
575
  if (-not $SkipCodegraphStatus) {
550
576
  Invoke-Step -Name 'Check codegraph status' -Action {
577
+ if (-not (Get-Command codegraph -ErrorAction SilentlyContinue)) {
578
+ [void](Ensure-SetupPrerequisites)
579
+ }
580
+
551
581
  if (-not (Get-Command codegraph -ErrorAction SilentlyContinue)) {
552
582
  throw 'codegraph command not found'
553
583
  }
@@ -835,6 +835,19 @@ else {
835
835
 
836
836
  if (-not $SkipMcpStartupChecks) {
837
837
  Invoke-Step -Name 'Validate MCP servers start/listen capability' -Action {
838
+ $requiredCommands = @(
839
+ 'token-saver-mcp',
840
+ 'codebase-memory-mcp',
841
+ 'codegraph',
842
+ 'npx'
843
+ )
844
+
845
+ $missingCommands = @($requiredCommands | Where-Object { -not (Test-CommandAvailable -Name $_) })
846
+ if ($missingCommands.Count -gt 0) {
847
+ Write-Host ("[info] Missing MCP prerequisites detected ({0}). Running setup-prerequisites automatically..." -f ($missingCommands -join ', ')) -ForegroundColor DarkYellow
848
+ [void](Ensure-SetupPrerequisites)
849
+ }
850
+
838
851
  $py = Get-PythonCommand
839
852
  $pyCmd = $py[0]
840
853
  $pyParts = @()
@@ -17,9 +17,16 @@ from __future__ import annotations
17
17
 
18
18
  import argparse
19
19
  import os
20
+ import sys
20
21
  from pathlib import Path
21
22
  from typing import List
22
23
 
24
+ _REPO_ROOT = Path(__file__).resolve().parents[2]
25
+ if str(_REPO_ROOT) not in sys.path:
26
+ sys.path.insert(0, str(_REPO_ROOT))
27
+
28
+ from telemetry import build_telemetry_collector
29
+
23
30
  try:
24
31
  from openai import AzureOpenAI
25
32
  except Exception:
@@ -81,7 +88,10 @@ def main() -> int:
81
88
  args = parser.parse_args()
82
89
 
83
90
  repo_root = Path(args.repo_root)
91
+ collector = build_telemetry_collector(_REPO_ROOT)
84
92
  if not repo_root.exists():
93
+ collector.record_event("WarningGenerated", {"warning": "repo_root_missing", "repo_root": str(repo_root)}, level="WARNING")
94
+ collector.shutdown()
85
95
  print("prompt_tokens=0")
86
96
  print("completion_tokens=0")
87
97
  print("total_tokens=0")
@@ -99,6 +109,17 @@ def main() -> int:
99
109
  model = os.getenv("OPENAI_CHAT_MODEL", "gpt-4o-mini")
100
110
 
101
111
  if AzureOpenAI is None or not api_key or not endpoint:
112
+ collector.record_event(
113
+ "WarningGenerated",
114
+ {
115
+ "warning": "missing_credentials_or_openai_pkg",
116
+ "has_openai_pkg": AzureOpenAI is not None,
117
+ "has_api_key": bool(api_key),
118
+ "has_endpoint": bool(endpoint),
119
+ },
120
+ level="WARNING",
121
+ )
122
+ collector.shutdown()
102
123
  print("prompt_tokens=0")
103
124
  print("completion_tokens=0")
104
125
  print("total_tokens=0")
@@ -119,45 +140,88 @@ def main() -> int:
119
140
  "Devuelve un resumen tecnico corto con riesgos y evidencia."
120
141
  )
121
142
 
122
- try:
123
- response = client.chat.completions.create(
124
- model=model,
125
- messages=[
126
- {"role": "system", "content": system_prompt},
127
- {"role": "user", "content": user_prompt},
128
- ],
129
- temperature=0,
130
- max_completion_tokens=400,
131
- )
132
-
133
- usage = response.usage
134
- prompt_tokens = int(getattr(usage, "prompt_tokens", 0) or 0)
135
- completion_tokens = int(getattr(usage, "completion_tokens", 0) or 0)
136
- total_tokens = int(getattr(usage, "total_tokens", prompt_tokens + completion_tokens) or 0)
137
-
138
- price_in = to_float(os.getenv("LLM_PRICE_INPUT_USD_PER_1K"), 0.0)
139
- price_out = to_float(os.getenv("LLM_PRICE_OUTPUT_USD_PER_1K"), 0.0)
140
- if price_in > 0 or price_out > 0:
141
- cost = (prompt_tokens / 1000.0) * price_in + (completion_tokens / 1000.0) * price_out
142
- else:
143
- # fallback simple when split pricing is unknown
144
- flat = to_float(os.getenv("LLM_PRICE_USD_PER_1K_TOKENS"), 0.0)
145
- cost = (total_tokens / 1000.0) * flat if flat > 0 else 0.0
146
-
147
- print(f"prompt_tokens={prompt_tokens}")
148
- print(f"completion_tokens={completion_tokens}")
149
- print(f"total_tokens={total_tokens}")
150
- print(f"total_cost_usd={cost:.6f}")
151
- print("probe_status=ok")
152
- return 0
153
-
154
- except Exception as ex:
155
- print("prompt_tokens=0")
156
- print("completion_tokens=0")
157
- print("total_tokens=0")
158
- print("total_cost_usd=0")
159
- print(f"probe_status=error:{str(ex).replace(os.linesep, ' ')[:200]}")
160
- return 0
143
+ with collector.start_execution(
144
+ operation="llm-token-probe",
145
+ session_id="ops",
146
+ provider="azure-openai",
147
+ model=model,
148
+ ):
149
+ with collector.start_span(name="provider.azure_openai_call", kind="CLIENT", attributes={"profile": args.profile}):
150
+ collector.record_event(
151
+ "LLMRequest",
152
+ {
153
+ "provider": "azure-openai",
154
+ "model": model,
155
+ "profile": args.profile,
156
+ "selected_files": len(files),
157
+ },
158
+ )
159
+ try:
160
+ response = client.chat.completions.create(
161
+ model=model,
162
+ messages=[
163
+ {"role": "system", "content": system_prompt},
164
+ {"role": "user", "content": user_prompt},
165
+ ],
166
+ temperature=0,
167
+ max_completion_tokens=400,
168
+ )
169
+
170
+ usage = response.usage
171
+ prompt_tokens = int(getattr(usage, "prompt_tokens", 0) or 0)
172
+ completion_tokens = int(getattr(usage, "completion_tokens", 0) or 0)
173
+ total_tokens = int(getattr(usage, "total_tokens", prompt_tokens + completion_tokens) or 0)
174
+
175
+ price_in = to_float(os.getenv("LLM_PRICE_INPUT_USD_PER_1K"), 0.0)
176
+ price_out = to_float(os.getenv("LLM_PRICE_OUTPUT_USD_PER_1K"), 0.0)
177
+ if price_in > 0 or price_out > 0:
178
+ cost = (prompt_tokens / 1000.0) * price_in + (completion_tokens / 1000.0) * price_out
179
+ else:
180
+ # fallback simple when split pricing is unknown
181
+ flat = to_float(os.getenv("LLM_PRICE_USD_PER_1K_TOKENS"), 0.0)
182
+ cost = (total_tokens / 1000.0) * flat if flat > 0 else 0.0
183
+
184
+ collector.record_usage(
185
+ input_tokens=prompt_tokens,
186
+ output_tokens=completion_tokens,
187
+ estimated_cost_usd=cost,
188
+ )
189
+ collector.record_event(
190
+ "LLMResponse",
191
+ {
192
+ "provider": "azure-openai",
193
+ "model": model,
194
+ "prompt_tokens": prompt_tokens,
195
+ "completion_tokens": completion_tokens,
196
+ "total_tokens": total_tokens,
197
+ "estimated_cost_usd": round(cost, 6),
198
+ },
199
+ )
200
+
201
+ print(f"prompt_tokens={prompt_tokens}")
202
+ print(f"completion_tokens={completion_tokens}")
203
+ print(f"total_tokens={total_tokens}")
204
+ print(f"total_cost_usd={cost:.6f}")
205
+ print("probe_status=ok")
206
+ collector.shutdown()
207
+ return 0
208
+
209
+ except Exception as ex:
210
+ collector.record_event(
211
+ "ExceptionThrown",
212
+ {
213
+ "provider": "azure-openai",
214
+ "error": str(ex),
215
+ },
216
+ level="ERROR",
217
+ )
218
+ print("prompt_tokens=0")
219
+ print("completion_tokens=0")
220
+ print("total_tokens=0")
221
+ print("total_cost_usd=0")
222
+ print(f"probe_status=error:{str(ex).replace(os.linesep, ' ')[:200]}")
223
+ collector.shutdown()
224
+ return 0
161
225
 
162
226
 
163
227
  if __name__ == "__main__":
@@ -0,0 +1,110 @@
1
+ param(
2
+ [switch]$Force,
3
+ [switch]$SkipLangSmith,
4
+ [switch]$SkipLearningReports,
5
+ [switch]$SkipAutoDocs
6
+ )
7
+
8
+ Set-StrictMode -Version Latest
9
+ $ErrorActionPreference = 'Stop'
10
+
11
+ function Get-PythonCommand {
12
+ if (Get-Command py -ErrorAction SilentlyContinue) {
13
+ return @('py', '-3')
14
+ }
15
+ if (Get-Command python -ErrorAction SilentlyContinue) {
16
+ return @('python')
17
+ }
18
+ throw 'Python is required (py or python not found in PATH).'
19
+ }
20
+
21
+ function Invoke-PythonScript {
22
+ param(
23
+ [string]$ScriptPath,
24
+ [string[]]$Args = @(),
25
+ [switch]$IgnoreErrors
26
+ )
27
+
28
+ $python = Get-PythonCommand
29
+ & $python[0] $python[1] $ScriptPath @Args
30
+ $exitCode = $LASTEXITCODE
31
+ if ($exitCode -ne 0 -and -not $IgnoreErrors) {
32
+ throw "Command failed ($exitCode): $ScriptPath"
33
+ }
34
+ return $exitCode
35
+ }
36
+
37
+ $repoRoot = (& git rev-parse --show-toplevel 2>$null)
38
+ if (-not $repoRoot) {
39
+ Write-Host '[mcpee] post-commit-refresh skipped: git repo not detected.'
40
+ exit 0
41
+ }
42
+
43
+ Set-Location $repoRoot
44
+
45
+ $changedFiles = (& git diff-tree --no-commit-id --name-only -r HEAD 2>$null)
46
+ if ($LASTEXITCODE -ne 0) {
47
+ Write-Host '[mcpee] post-commit-refresh skipped: unable to inspect last commit.'
48
+ exit 0
49
+ }
50
+
51
+ $projectChanges = @($changedFiles | Where-Object { $_ -match '^projects/' })
52
+ if (-not $Force -and $projectChanges.Count -eq 0) {
53
+ Write-Host '[mcpee] post-commit-refresh skipped: no changes under projects/ in last commit.'
54
+ exit 0
55
+ }
56
+
57
+ $started = Get-Date
58
+ $status = 'ok'
59
+ $errors = @()
60
+ $steps = @()
61
+
62
+ try {
63
+ if (-not $SkipAutoDocs) {
64
+ Write-Host '[mcpee] AutoDocs: compiling incremental projection...'
65
+ Invoke-PythonScript -ScriptPath '.\scripts\wiki\compiler_main.py'
66
+ $steps += 'autodocs'
67
+ }
68
+
69
+ if (-not $SkipLearningReports) {
70
+ Write-Host '[mcpee] Observability: refreshing learning reports...'
71
+ Invoke-PythonScript -ScriptPath '.\scripts\learning\learning-loop-report.py'
72
+ Invoke-PythonScript -ScriptPath '.\scripts\learning\iteration-value-report.py'
73
+ $steps += 'learning-reports'
74
+ }
75
+
76
+ if (-not $SkipLangSmith) {
77
+ Write-Host '[mcpee] LangSmith: publishing KPI snapshots...'
78
+ Invoke-PythonScript -ScriptPath '.\scripts\ops\publish-langsmith-kpis.py' -IgnoreErrors
79
+ $steps += 'langsmith-kpis'
80
+ }
81
+ }
82
+ catch {
83
+ $status = 'failed'
84
+ $errors += $_.Exception.Message
85
+ Write-Warning ("[mcpee] post-commit-refresh failed: {0}" -f $_.Exception.Message)
86
+ }
87
+
88
+ $finished = Get-Date
89
+ $duration = [math]::Round((($finished - $started).TotalSeconds), 2)
90
+
91
+ $summary = [ordered]@{
92
+ timestamp = $finished.ToString('o')
93
+ operation = 'post-commit-refresh'
94
+ status = $status
95
+ duration_sec = $duration
96
+ changed_projects_files = $projectChanges.Count
97
+ steps = $steps
98
+ errors = $errors
99
+ }
100
+
101
+ $sessionDir = Join-Path $repoRoot 'observability/logs/session'
102
+ New-Item -ItemType Directory -Path $sessionDir -Force | Out-Null
103
+ $stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
104
+ $reportPath = Join-Path $sessionDir ("post-commit-refresh-$stamp.json")
105
+ $summary | ConvertTo-Json -Depth 8 | Set-Content -Path $reportPath -Encoding UTF8
106
+
107
+ Write-Host ("[mcpee] post-commit-refresh {0}. report: {1}" -f $status, $reportPath)
108
+
109
+ # Never block commit history after it was created.
110
+ exit 0