@pentatonic-ai/ai-agent-sdk 0.10.34 → 0.10.36
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.36";
|
|
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.36";
|
|
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.36",
|
|
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",
|
|
@@ -507,6 +507,18 @@ ACTIVE_SYSTEM_PROMPT = (
|
|
|
507
507
|
# guided-JSON traces never mix in a training export).
|
|
508
508
|
SYSTEM_PROMPT_HASH = hashlib.sha256(ACTIVE_SYSTEM_PROMPT.encode()).hexdigest()[:16]
|
|
509
509
|
|
|
510
|
+
# Sentinel system_prompt_hash for STUDENT traces. The student runs with NO
|
|
511
|
+
# system prompt, so it has no natural hash — and crucially, every trace CONSUMER
|
|
512
|
+
# keys off the teacher's hash: build_retrain_corpus.py filters
|
|
513
|
+
# `system_prompt_hash = <clean teacher hash>` and redistill.py treats a trace at
|
|
514
|
+
# that hash as "already re-distilled clean". Tagging student traces with a
|
|
515
|
+
# distinct sentinel (never a 16-hex teacher hash) keeps them out of BOTH — so
|
|
516
|
+
# student output can never be mistaken for training gold or a clean re-distill.
|
|
517
|
+
# `llm_model` (= STUDENT_MODEL) also distinguishes them; the sentinel is the
|
|
518
|
+
# belt to that suspenders. These rows exist purely as the student side of the
|
|
519
|
+
# student-vs-teacher agreement corpus.
|
|
520
|
+
STUDENT_TRACE_PROMPT_HASH = f"student:{STUDENT_MODEL}"
|
|
521
|
+
|
|
510
522
|
|
|
511
523
|
# --------------------------------------------------------------------
|
|
512
524
|
# Content cleaner — strip HTML/CSS so email + doc styling never reaches
|
|
@@ -2296,6 +2308,8 @@ def _insert_trace(
|
|
|
2296
2308
|
user_prompt: str,
|
|
2297
2309
|
raw_response: str,
|
|
2298
2310
|
llm_chunk_ms: float | None,
|
|
2311
|
+
llm_model: str = LLM_MODEL,
|
|
2312
|
+
system_prompt_hash: str = SYSTEM_PROMPT_HASH,
|
|
2299
2313
|
) -> None:
|
|
2300
2314
|
"""Append a (user_prompt, raw_response) pair to distillation_traces.
|
|
2301
2315
|
|
|
@@ -2303,7 +2317,12 @@ def _insert_trace(
|
|
|
2303
2317
|
caller wraps this in a try/except and never lets a trace-insert
|
|
2304
2318
|
failure poison the upsert path. Skip rows with empty raw_response
|
|
2305
2319
|
(no signal to train on); the model occasionally emits a header
|
|
2306
|
-
with no body.
|
|
2320
|
+
with no body.
|
|
2321
|
+
|
|
2322
|
+
Defaults tag the row as the teacher's training gold. The student path
|
|
2323
|
+
passes llm_model=STUDENT_MODEL + system_prompt_hash=STUDENT_TRACE_PROMPT_HASH
|
|
2324
|
+
so its rows form the agreement corpus WITHOUT being pulled into the retrain
|
|
2325
|
+
corpus / redistill (both key off the teacher hash)."""
|
|
2307
2326
|
if not raw_response.strip():
|
|
2308
2327
|
return
|
|
2309
2328
|
with conn.cursor() as cur:
|
|
@@ -2316,7 +2335,7 @@ def _insert_trace(
|
|
|
2316
2335
|
""",
|
|
2317
2336
|
(
|
|
2318
2337
|
event_id, user_prompt, raw_response,
|
|
2319
|
-
|
|
2338
|
+
llm_model, system_prompt_hash, llm_chunk_ms,
|
|
2320
2339
|
),
|
|
2321
2340
|
)
|
|
2322
2341
|
|
|
@@ -2429,6 +2448,27 @@ def claim_next_batch(conn: psycopg.Connection) -> list[dict[str, Any]]:
|
|
|
2429
2448
|
LLM call on filtered events. They're cheap UPDATE statements scoped
|
|
2430
2449
|
to the current pending set."""
|
|
2431
2450
|
with conn.cursor() as cur:
|
|
2451
|
+
# Sweep: retire retry-budget-exhausted rows stuck in 'pending'. The
|
|
2452
|
+
# claim below filters `attempts < MAX_ATTEMPTS`, so a row that reached
|
|
2453
|
+
# MAX_ATTEMPTS while still 'pending' is UNCLAIMABLE — no worker can ever
|
|
2454
|
+
# process it or transition it to 'failed'. Such rows accumulate (legacy
|
|
2455
|
+
# gotcha #11: claiming used to increment attempts; a burst of
|
|
2456
|
+
# deploy-reclaims could push a row to MAX in 'pending') and then pin the
|
|
2457
|
+
# distiller autoscaler awake forever via its flush-on-age trigger (it
|
|
2458
|
+
# counts pending by age) — an idle GPU billed 24/7 for events it can't
|
|
2459
|
+
# touch (62 such rows, oldest 26d, found 2026-07-14). Fail them here so
|
|
2460
|
+
# 'pending' only ever holds claimable work. Terminal by definition:
|
|
2461
|
+
# they already spent the full retry budget.
|
|
2462
|
+
cur.execute(
|
|
2463
|
+
"""
|
|
2464
|
+
UPDATE distillation_queue SET
|
|
2465
|
+
status = 'failed',
|
|
2466
|
+
completed_at = NOW(),
|
|
2467
|
+
last_error = COALESCE(last_error, '') || ' | swept: attempts_exhausted_in_pending'
|
|
2468
|
+
WHERE status = 'pending' AND attempts >= %s
|
|
2469
|
+
""",
|
|
2470
|
+
(MAX_ATTEMPTS,),
|
|
2471
|
+
)
|
|
2432
2472
|
# Pre-filter: agent's-own-output events (by source_kind). Covers any
|
|
2433
2473
|
# agent producer without enumerating its source label.
|
|
2434
2474
|
if SKIP_AGENT_SOURCE_KIND:
|
|
@@ -2794,6 +2834,30 @@ async def _process_cascade(
|
|
|
2794
2834
|
if escalate:
|
|
2795
2835
|
escalate_items.append(item)
|
|
2796
2836
|
reason_by_qid[item["id"]] = reason or "escalated"
|
|
2837
|
+
# Capture the student's (discarded) output on escalated events —
|
|
2838
|
+
# the ONLY place the teacher ALSO runs, so the ONLY place a
|
|
2839
|
+
# student-vs-teacher diff is possible. This is the agreement corpus
|
|
2840
|
+
# that makes student quality measurable (esp. the random_sample
|
|
2841
|
+
# subset = an unbiased paired sample of student-passing traffic, and
|
|
2842
|
+
# the high_value_class lane the student now carries). Tagged student
|
|
2843
|
+
# (model + sentinel hash) so no consumer mistakes it for teacher
|
|
2844
|
+
# gold. sresult is None only on student_parse_fail (nothing to
|
|
2845
|
+
# trace). Best-effort — never blocks escalation.
|
|
2846
|
+
if DISTILL_TRACE_ENABLED and event is not None and sresult is not None:
|
|
2847
|
+
try:
|
|
2848
|
+
_insert_trace(
|
|
2849
|
+
conn,
|
|
2850
|
+
event_id=item["event_id"], # same key the teacher trace uses → rows pair
|
|
2851
|
+
user_prompt=build_event_block(0, event),
|
|
2852
|
+
raw_response=sresult.get("raw_slice", ""),
|
|
2853
|
+
llm_chunk_ms=None,
|
|
2854
|
+
llm_model=STUDENT_MODEL,
|
|
2855
|
+
system_prompt_hash=STUDENT_TRACE_PROMPT_HASH,
|
|
2856
|
+
)
|
|
2857
|
+
except Exception as trace_exc:
|
|
2858
|
+
log.warning(
|
|
2859
|
+
f"student trace insert failed event_id={event.get('id')}: {trace_exc}"
|
|
2860
|
+
)
|
|
2797
2861
|
else:
|
|
2798
2862
|
_apply_extraction(
|
|
2799
2863
|
conn, item=item, event=event, result=sresult, llm_ms=0.0,
|