@pentatonic-ai/ai-agent-sdk 0.10.36 → 0.10.37
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.
package/dist/index.cjs
CHANGED
|
@@ -878,7 +878,7 @@ function fireAndForgetEmit(clientConfig, sessionOpts, messages, result, model) {
|
|
|
878
878
|
}
|
|
879
879
|
|
|
880
880
|
// src/telemetry.js
|
|
881
|
-
var VERSION = "0.10.
|
|
881
|
+
var VERSION = "0.10.37";
|
|
882
882
|
var TELEMETRY_URL = "https://sdk-telemetry.philip-134.workers.dev";
|
|
883
883
|
function machineId() {
|
|
884
884
|
const raw = typeof process !== "undefined" ? `${process.env?.USER || process.env?.USERNAME || "u"}:${process.platform || "x"}:${process.arch || "x"}` : "browser";
|
package/dist/index.js
CHANGED
|
@@ -847,7 +847,7 @@ function fireAndForgetEmit(clientConfig, sessionOpts, messages, result, model) {
|
|
|
847
847
|
}
|
|
848
848
|
|
|
849
849
|
// src/telemetry.js
|
|
850
|
-
var VERSION = "0.10.
|
|
850
|
+
var VERSION = "0.10.37";
|
|
851
851
|
var TELEMETRY_URL = "https://sdk-telemetry.philip-134.workers.dev";
|
|
852
852
|
function machineId() {
|
|
853
853
|
const raw = typeof process !== "undefined" ? `${process.env?.USER || process.env?.USERNAME || "u"}:${process.platform || "x"}:${process.arch || "x"}` : "browser";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pentatonic-ai/ai-agent-sdk",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.37",
|
|
4
4
|
"description": "TES SDK — LLM observability and lifecycle tracking via Pentatonic Thing Event System. Track token usage, tool calls, and conversations. Manage things through event-sourced lifecycle stages with AI enrichment and vector search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -37,6 +37,7 @@ import uuid
|
|
|
37
37
|
from contextlib import asynccontextmanager
|
|
38
38
|
from datetime import datetime
|
|
39
39
|
from typing import Any
|
|
40
|
+
from urllib.parse import urlsplit
|
|
40
41
|
|
|
41
42
|
import httpx
|
|
42
43
|
import numpy as np
|
|
@@ -59,6 +60,14 @@ NV_EMBED_PROVIDER = os.environ.get("NV_EMBED_PROVIDER", "openai") # 'openai' |
|
|
|
59
60
|
EMBED_DIM = int(os.environ.get("EMBED_DIM", "4096"))
|
|
60
61
|
# Window (minutes) over which /health/deep computes the distillation drain rate.
|
|
61
62
|
HEALTH_DRAIN_WINDOW_MIN = int(os.environ.get("HEALTH_DRAIN_WINDOW_MIN", "5"))
|
|
63
|
+
# GPU/compute-tier endpoints probed by the /health/deep stack-online gauge. The
|
|
64
|
+
# teacher is probed THROUGH its load-balancer (a stable service name that is up
|
|
65
|
+
# even when the fleet is scaled to zero) — see health_deep for why the probe
|
|
66
|
+
# treats a no-upstream 502 as offline. STUDENT_ENDPOINT is the vLLM student box;
|
|
67
|
+
# when unset (not wired into compat's env) the student tier is simply omitted
|
|
68
|
+
# from the gauge rather than counted as down.
|
|
69
|
+
STUDENT_ENDPOINT = os.environ.get("STUDENT_ENDPOINT", "")
|
|
70
|
+
TEACHER_HEALTH_URL = os.environ.get("TEACHER_HEALTH_URL", "http://teacher-lb:8500/v1/models")
|
|
62
71
|
|
|
63
72
|
COLLECTION_NAME = "evidence"
|
|
64
73
|
|
|
@@ -651,10 +660,36 @@ async def health():
|
|
|
651
660
|
return {"status": "healthy", "service": "pme2-compat", "version": "0.1.0"}
|
|
652
661
|
|
|
653
662
|
|
|
663
|
+
def _models_url(endpoint: str) -> str:
|
|
664
|
+
"""Rewrite any endpoint URL to its OpenAI-style /v1/models path — a cheap
|
|
665
|
+
GET the vLLM student/teacher boxes and the embed gateways all answer. Keeps
|
|
666
|
+
only scheme+host+port so it works whether the source URL was /v1/embeddings,
|
|
667
|
+
/v1/chat/completions, etc."""
|
|
668
|
+
p = urlsplit(endpoint)
|
|
669
|
+
return f"{p.scheme}://{p.netloc}/v1/models"
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
async def _tier_online(url: str, *, healthy_below: int = 600) -> bool:
|
|
673
|
+
"""Liveness probe for one compute tier. Online iff the endpoint answers with
|
|
674
|
+
an HTTP status < healthy_below within the timeout. Connection refused /
|
|
675
|
+
timeout / DNS failure → offline.
|
|
676
|
+
|
|
677
|
+
healthy_below defaults to 600 (any HTTP answer = the box is powered on and
|
|
678
|
+
serving). Pass 500 for a tier fronted by an always-up load balancer (the
|
|
679
|
+
teacher LB): with the fleet scaled to zero the LB itself still answers, but
|
|
680
|
+
with a 502 (no healthy upstream) — which we must read as OFFLINE, not "the
|
|
681
|
+
LB replied, so it's up"."""
|
|
682
|
+
try:
|
|
683
|
+
r = await _http.get(url, timeout=3.0)
|
|
684
|
+
return r.status_code < healthy_below
|
|
685
|
+
except Exception:
|
|
686
|
+
return False
|
|
687
|
+
|
|
688
|
+
|
|
654
689
|
@app.get("/health/deep")
|
|
655
690
|
async def health_deep():
|
|
656
|
-
"""Round-trips all three stores + the embed gateway
|
|
657
|
-
use as a docker healthcheck."""
|
|
691
|
+
"""Round-trips all three stores + the embed gateway, then computes a
|
|
692
|
+
stack-online gauge. Slow; do not use as a docker healthcheck."""
|
|
658
693
|
result = {"compat": "ok", "stores": {}}
|
|
659
694
|
|
|
660
695
|
# org-model
|
|
@@ -710,6 +745,44 @@ async def health_deep():
|
|
|
710
745
|
except Exception as e:
|
|
711
746
|
result["stores"]["embed_gateway"] = {"status": "error", "error": str(e)}
|
|
712
747
|
|
|
748
|
+
# Stack-online gauge: what fraction of the compute stack is powered on RIGHT
|
|
749
|
+
# NOW. Deliberately counts every tier — including the scale-to-zero teacher
|
|
750
|
+
# fleet — so a parked teacher legitimately lowers the %. This is a "what's
|
|
751
|
+
# running" dial, not a pure health signal: the distillation drain/backlog
|
|
752
|
+
# block above says whether a parked teacher is actually a problem. Tiers the
|
|
753
|
+
# store round-trips already assessed are reused (no re-probe); the GPU tiers
|
|
754
|
+
# are probed concurrently for liveness.
|
|
755
|
+
components: dict[str, bool] = {
|
|
756
|
+
"compat": True, # we are the one answering
|
|
757
|
+
"org_model": result["stores"].get("org_model", {}).get("status") == "ok",
|
|
758
|
+
"vector_index": result["stores"].get("vector_index", {}).get("status") == "ok",
|
|
759
|
+
"embed_interactive": result["stores"].get("embed_gateway", {}).get("status") == "ok",
|
|
760
|
+
}
|
|
761
|
+
# GPU/compute tiers not covered by the store probes. (name, url, healthy_below)
|
|
762
|
+
gpu_probes = [("teacher", TEACHER_HEALTH_URL, 500)]
|
|
763
|
+
# Bulk embedder only counts as a distinct tier when it's a separate box.
|
|
764
|
+
if NV_EMBED_URL_BULK and NV_EMBED_URL_BULK != NV_EMBED_URL:
|
|
765
|
+
gpu_probes.append(("embed_bulk", _models_url(NV_EMBED_URL_BULK), 600))
|
|
766
|
+
if STUDENT_ENDPOINT:
|
|
767
|
+
gpu_probes.append(("student", _models_url(STUDENT_ENDPOINT), 600))
|
|
768
|
+
try:
|
|
769
|
+
probe_results = await asyncio.gather(
|
|
770
|
+
*(_tier_online(url, healthy_below=hb) for _, url, hb in gpu_probes)
|
|
771
|
+
)
|
|
772
|
+
for (name, _, _), up in zip(gpu_probes, probe_results):
|
|
773
|
+
components[name] = up
|
|
774
|
+
except Exception as e: # pragma: no cover — probing must never 500 the endpoint
|
|
775
|
+
result["stack_probe_error"] = str(e)
|
|
776
|
+
|
|
777
|
+
online = sum(1 for up in components.values() if up)
|
|
778
|
+
total = len(components)
|
|
779
|
+
result["stack"] = {
|
|
780
|
+
"online_pct": round(100 * online / total, 1) if total else 0.0,
|
|
781
|
+
"online": online,
|
|
782
|
+
"total": total,
|
|
783
|
+
"components": {k: ("up" if up else "down") for k, up in components.items()},
|
|
784
|
+
}
|
|
785
|
+
|
|
713
786
|
return result
|
|
714
787
|
|
|
715
788
|
|