@niadra/sdk 0.1.0 → 0.1.1
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/CHANGELOG.md +49 -0
- package/README.md +35 -18
- package/dist/index.cjs +169 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -13
- package/dist/index.d.ts +107 -13
- package/dist/index.js +167 -43
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -358,6 +358,15 @@ function checkCloses(closes) {
|
|
|
358
358
|
const byObject = Boolean(closes.object && closes.operation);
|
|
359
359
|
if (byId === byObject) fail("closes takes either item_id, or object and operation");
|
|
360
360
|
}
|
|
361
|
+
function checkUsage(usage) {
|
|
362
|
+
if (!/^[a-z0-9][a-z0-9_.-]{0,63}$/.test(usage.provider)) fail("usage.provider must be lowercase, like `openai`");
|
|
363
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.:/@-]{0,127}$/.test(usage.model)) fail("usage.model is not a model name");
|
|
364
|
+
const counts = [usage.prompt_tokens, usage.cached_tokens ?? 0, usage.cache_write_tokens ?? 0];
|
|
365
|
+
if (!counts.every((n) => Number.isInteger(n) && n >= 0)) fail("usage counts are whole numbers from 0");
|
|
366
|
+
if ((usage.cached_tokens ?? 0) + (usage.cache_write_tokens ?? 0) > usage.prompt_tokens) {
|
|
367
|
+
fail("cached and written tokens are part of prompt_tokens");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
361
370
|
function checkContent(content) {
|
|
362
371
|
if (!content) return;
|
|
363
372
|
if ((content.text?.length ?? 0) > MAX_EVENT_TEXT) fail(`content.text is longer than ${MAX_EVENT_TEXT}`);
|
|
@@ -388,6 +397,10 @@ function buildEvent(input) {
|
|
|
388
397
|
if ((input.action.result?.length ?? 0) > 2e3) fail("action.result is longer than 2000");
|
|
389
398
|
checkCloses(input.action.closes);
|
|
390
399
|
}
|
|
400
|
+
if (input.usage) {
|
|
401
|
+
if (kind !== "message" || speaker.role !== "ai_agent") fail("`usage` is only valid on a message of the `ai_agent`");
|
|
402
|
+
checkUsage(input.usage);
|
|
403
|
+
}
|
|
391
404
|
const event = {
|
|
392
405
|
type: "event",
|
|
393
406
|
kind,
|
|
@@ -403,6 +416,7 @@ function buildEvent(input) {
|
|
|
403
416
|
if (input.canonical_type) event.canonical_type = input.canonical_type;
|
|
404
417
|
if (input.fields) event.fields = input.fields;
|
|
405
418
|
if (input.action) event.action = input.action;
|
|
419
|
+
if (input.usage) event.usage = input.usage;
|
|
406
420
|
copyOptional(event, input);
|
|
407
421
|
assertSerializable(event);
|
|
408
422
|
return event;
|
|
@@ -577,6 +591,76 @@ function withDeltas(result, response, deltas) {
|
|
|
577
591
|
return { ...result, response: merged, suffix: renderSuffix(merged) };
|
|
578
592
|
}
|
|
579
593
|
|
|
594
|
+
// src/usage.ts
|
|
595
|
+
var PROVIDER = /^[a-z0-9][a-z0-9_.-]{0,63}$/;
|
|
596
|
+
var MODEL = /^[A-Za-z0-9][A-Za-z0-9_.:/@-]{0,127}$/;
|
|
597
|
+
function get(value, name) {
|
|
598
|
+
return (typeof value === "object" || typeof value === "function") && value !== null ? value[name] : void 0;
|
|
599
|
+
}
|
|
600
|
+
function count(value) {
|
|
601
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : null;
|
|
602
|
+
}
|
|
603
|
+
function tokenCounts(usage) {
|
|
604
|
+
const read = count(get(usage, "cache_read_input_tokens")) ?? 0;
|
|
605
|
+
const written = count(get(usage, "cache_creation_input_tokens")) ?? 0;
|
|
606
|
+
let prompt = count(get(usage, "prompt_tokens"));
|
|
607
|
+
let cached;
|
|
608
|
+
if (prompt !== null) {
|
|
609
|
+
const reported = count(get(get(usage, "prompt_tokens_details"), "cached_tokens"));
|
|
610
|
+
cached = reported !== null && reported > 0 ? reported : read;
|
|
611
|
+
} else {
|
|
612
|
+
const inputs = count(get(usage, "input_tokens"));
|
|
613
|
+
if (inputs === null) return null;
|
|
614
|
+
const details = get(usage, "input_tokens_details");
|
|
615
|
+
if (details !== void 0 && details !== null) {
|
|
616
|
+
prompt = inputs;
|
|
617
|
+
cached = count(get(details, "cached_tokens")) ?? 0;
|
|
618
|
+
} else {
|
|
619
|
+
prompt = inputs + read + written;
|
|
620
|
+
cached = read;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return { prompt_tokens: Math.max(prompt, cached + written), cached_tokens: cached, cache_write_tokens: written };
|
|
624
|
+
}
|
|
625
|
+
function providerOf(model, usage) {
|
|
626
|
+
const slash = model.indexOf("/");
|
|
627
|
+
if (slash > 0) return model.slice(0, slash).trim().toLowerCase();
|
|
628
|
+
const name = model.trim().toLowerCase();
|
|
629
|
+
if (name.startsWith("claude") || `.${name}`.includes(".anthropic.")) return "anthropic";
|
|
630
|
+
if (name.startsWith("gemini")) return "google";
|
|
631
|
+
if (get(usage, "prompt_tokens") === void 0 && get(usage, "cache_read_input_tokens") !== void 0) {
|
|
632
|
+
return "anthropic";
|
|
633
|
+
}
|
|
634
|
+
return "openai";
|
|
635
|
+
}
|
|
636
|
+
function modelUsage(response, options = {}) {
|
|
637
|
+
try {
|
|
638
|
+
let usage = get(response, "usage");
|
|
639
|
+
if (usage === void 0 || usage === null || typeof usage !== "object") usage = response;
|
|
640
|
+
const counts = tokenCounts(usage);
|
|
641
|
+
const name = options.model ?? get(response, "model");
|
|
642
|
+
if (!counts || typeof name !== "string" || !MODEL.test(name)) return null;
|
|
643
|
+
const provider = (options.provider ?? providerOf(name, usage)).toLowerCase();
|
|
644
|
+
if (!PROVIDER.test(provider)) return null;
|
|
645
|
+
return { provider, model: name, ...counts };
|
|
646
|
+
} catch {
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
function asModelUsage(value) {
|
|
651
|
+
if (value === void 0 || value === null) return null;
|
|
652
|
+
if (isModelUsage(value)) return value;
|
|
653
|
+
return modelUsage(value);
|
|
654
|
+
}
|
|
655
|
+
function isModelUsage(value) {
|
|
656
|
+
const provider = get(value, "provider");
|
|
657
|
+
const model = get(value, "model");
|
|
658
|
+
const prompt = count(get(value, "prompt_tokens"));
|
|
659
|
+
const cached = get(value, "cached_tokens") === void 0 ? 0 : count(get(value, "cached_tokens"));
|
|
660
|
+
const written = get(value, "cache_write_tokens") === void 0 ? 0 : count(get(value, "cache_write_tokens"));
|
|
661
|
+
return typeof provider === "string" && PROVIDER.test(provider) && typeof model === "string" && MODEL.test(model) && prompt !== null && cached !== null && written !== null && cached + written <= prompt;
|
|
662
|
+
}
|
|
663
|
+
|
|
580
664
|
// src/conversation.ts
|
|
581
665
|
var Conversation = class {
|
|
582
666
|
constructor(client, params, hooks) {
|
|
@@ -736,6 +820,8 @@ var Conversation = class {
|
|
|
736
820
|
if (options.visibility) event.visibility = options.visibility;
|
|
737
821
|
if (options.voice) event.voice = options.voice;
|
|
738
822
|
if (options.context_stamp) event.context_stamp = options.context_stamp;
|
|
823
|
+
const usage = role === "ai_agent" ? asModelUsage(options.usage) : null;
|
|
824
|
+
if (usage) event.usage = usage;
|
|
739
825
|
return this.client.track(event);
|
|
740
826
|
}
|
|
741
827
|
};
|
|
@@ -879,6 +965,7 @@ var DEFAULT_CACHE = {
|
|
|
879
965
|
var DEFAULT_QUEUE = {
|
|
880
966
|
flushAt: 15,
|
|
881
967
|
flushIntervalMs: 1e3,
|
|
968
|
+
turnFlushIntervalMs: 200,
|
|
882
969
|
maxBatchSize: 100,
|
|
883
970
|
maxQueueSize: 1e4,
|
|
884
971
|
maxAttempts: 3,
|
|
@@ -902,6 +989,7 @@ var EventQueue = class {
|
|
|
902
989
|
now;
|
|
903
990
|
items = [];
|
|
904
991
|
timer = null;
|
|
992
|
+
timerDueAt = Number.POSITIVE_INFINITY;
|
|
905
993
|
tail = Promise.resolve();
|
|
906
994
|
closed = false;
|
|
907
995
|
droppedSinceWarning = 0;
|
|
@@ -926,7 +1014,8 @@ var EventQueue = class {
|
|
|
926
1014
|
if (this.items.length >= this.options.flushAt) {
|
|
927
1015
|
this.flushInBackground();
|
|
928
1016
|
} else {
|
|
929
|
-
this.
|
|
1017
|
+
const { flushIntervalMs, turnFlushIntervalMs } = this.options;
|
|
1018
|
+
this.schedule(isTurn(item) ? Math.min(turnFlushIntervalMs, flushIntervalMs) : flushIntervalMs);
|
|
930
1019
|
}
|
|
931
1020
|
return true;
|
|
932
1021
|
}
|
|
@@ -1016,20 +1105,29 @@ var EventQueue = class {
|
|
|
1016
1105
|
this.sentInWindow = 0;
|
|
1017
1106
|
return item;
|
|
1018
1107
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1108
|
+
/** Sends what is waiting in `delayMs`, unless a send is already due sooner. */
|
|
1109
|
+
schedule(delayMs) {
|
|
1110
|
+
const dueAt = this.now() + delayMs;
|
|
1111
|
+
if (this.timer !== null && this.timerDueAt <= dueAt) return;
|
|
1112
|
+
this.cancelTimer();
|
|
1113
|
+
this.timerDueAt = dueAt;
|
|
1021
1114
|
this.timer = setTimeout(() => {
|
|
1022
1115
|
this.timer = null;
|
|
1116
|
+
this.timerDueAt = Number.POSITIVE_INFINITY;
|
|
1023
1117
|
this.flushInBackground();
|
|
1024
|
-
},
|
|
1118
|
+
}, delayMs);
|
|
1025
1119
|
unref(this.timer);
|
|
1026
1120
|
}
|
|
1027
1121
|
cancelTimer() {
|
|
1028
1122
|
if (this.timer === null) return;
|
|
1029
1123
|
clearTimeout(this.timer);
|
|
1030
1124
|
this.timer = null;
|
|
1125
|
+
this.timerDueAt = Number.POSITIVE_INFINITY;
|
|
1031
1126
|
}
|
|
1032
1127
|
};
|
|
1128
|
+
function isTurn(item) {
|
|
1129
|
+
return item.type === "event" && item.kind === "message" && Boolean(item.conversation_id);
|
|
1130
|
+
}
|
|
1033
1131
|
function unref(timer) {
|
|
1034
1132
|
if (typeof timer === "object" && timer !== null && "unref" in timer && typeof timer.unref === "function") {
|
|
1035
1133
|
timer.unref();
|
|
@@ -1106,6 +1204,8 @@ var Task = class {
|
|
|
1106
1204
|
if (options.idempotency_key) event.idempotency_key = options.idempotency_key;
|
|
1107
1205
|
if (options.occurred_at) event.occurred_at = options.occurred_at;
|
|
1108
1206
|
if (options.visibility) event.visibility = options.visibility;
|
|
1207
|
+
const usage = asModelUsage(options.usage);
|
|
1208
|
+
if (usage) event.usage = usage;
|
|
1109
1209
|
return this.track(event);
|
|
1110
1210
|
}
|
|
1111
1211
|
/**
|
|
@@ -1177,7 +1277,7 @@ var TOOL_NAMES = {
|
|
|
1177
1277
|
timeline: "get_customer_timeline",
|
|
1178
1278
|
open: "open_history_item"
|
|
1179
1279
|
};
|
|
1180
|
-
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "
|
|
1280
|
+
var ITEM_KINDS = ["episode", "fact", "open_item", "action", "object", "trait"];
|
|
1181
1281
|
var period = {
|
|
1182
1282
|
since: { type: "string", format: "date-time", description: "Only items at or after this ISO 8601 time." },
|
|
1183
1283
|
until: { type: "string", format: "date-time", description: "Only items before this ISO 8601 time." },
|
|
@@ -1197,7 +1297,7 @@ var TOOL_DEFINITIONS = [
|
|
|
1197
1297
|
type: "function",
|
|
1198
1298
|
function: {
|
|
1199
1299
|
name: TOOL_NAMES.search,
|
|
1200
|
-
description: "Search this customer's past conversations, actions and
|
|
1300
|
+
description: "Search this customer's past conversations, actions and business objects by meaning and keywords. Use it when the customer refers to something that happened before and the details are not in the customer context you already have. Do not use it for facts already listed there. The result also says how often the same kind of issue came back. To read one result in full, call open_history_item.",
|
|
1201
1301
|
parameters: {
|
|
1202
1302
|
type: "object",
|
|
1203
1303
|
properties: {
|
|
@@ -1215,7 +1315,7 @@ var TOOL_DEFINITIONS = [
|
|
|
1215
1315
|
type: "function",
|
|
1216
1316
|
function: {
|
|
1217
1317
|
name: TOOL_NAMES.timeline,
|
|
1218
|
-
description: "List this customer's history
|
|
1318
|
+
description: "List this customer's history, newest first, one line per item. Use it when you need the sequence of events, for example what happened since a given date. Prefer search_customer_history when you are looking for something specific.",
|
|
1219
1319
|
parameters: {
|
|
1220
1320
|
type: "object",
|
|
1221
1321
|
properties: {
|
|
@@ -1309,7 +1409,7 @@ function bindTools(subject, binding, navigator, strict) {
|
|
|
1309
1409
|
case TOOL_NAMES.open: {
|
|
1310
1410
|
const id = str(args, "id");
|
|
1311
1411
|
if (!id) throw new NiadraValidationError("open_history_item needs an id");
|
|
1312
|
-
return navigator.open(id, binding, voice);
|
|
1412
|
+
return navigator.open(id, subject, binding, voice);
|
|
1313
1413
|
}
|
|
1314
1414
|
default:
|
|
1315
1415
|
throw new NiadraValidationError(`unknown tool: ${name}`);
|
|
@@ -1348,7 +1448,7 @@ function cloneDefinition(definition) {
|
|
|
1348
1448
|
}
|
|
1349
1449
|
|
|
1350
1450
|
// src/version.ts
|
|
1351
|
-
var VERSION = "0.1.
|
|
1451
|
+
var VERSION = "0.1.1";
|
|
1352
1452
|
|
|
1353
1453
|
// src/transport.ts
|
|
1354
1454
|
var RETRYABLE_WRITE_STATUS = /* @__PURE__ */ new Set([408, 421, 429, 500, 502, 503, 504]);
|
|
@@ -1401,13 +1501,17 @@ var Transport = class {
|
|
|
1401
1501
|
);
|
|
1402
1502
|
}
|
|
1403
1503
|
async retrying(timeoutMs, policy, signal, attempt) {
|
|
1404
|
-
|
|
1405
|
-
|
|
1504
|
+
const end = policy.totalMs === void 0 ? Number.POSITIVE_INFINITY : Date.now() + policy.totalMs;
|
|
1505
|
+
for (let count2 = 1; ; count2++) {
|
|
1506
|
+
const left = end - Date.now();
|
|
1507
|
+
if (left <= 0) throw new NiadraTimeoutError(policy.totalMs ?? timeoutMs);
|
|
1508
|
+
const deadline = new Deadline(Math.min(timeoutMs, left), signal);
|
|
1406
1509
|
try {
|
|
1407
1510
|
return await attempt(deadline);
|
|
1408
1511
|
} catch (error) {
|
|
1409
|
-
if (
|
|
1410
|
-
const delay = retryDelay(error,
|
|
1512
|
+
if (count2 >= policy.maxAttempts || !isTransient(error)) throw error;
|
|
1513
|
+
const delay = retryDelay(error, count2, policy.baseDelayMs, policy.maxDelayMs);
|
|
1514
|
+
if (Date.now() + delay >= end) throw error;
|
|
1411
1515
|
await sleep(delay, signal);
|
|
1412
1516
|
} finally {
|
|
1413
1517
|
deadline.clear();
|
|
@@ -1558,9 +1662,13 @@ function sleep(ms, signal) {
|
|
|
1558
1662
|
|
|
1559
1663
|
// src/client.ts
|
|
1560
1664
|
function readEnv(name) {
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1665
|
+
try {
|
|
1666
|
+
const env = globalThis.process?.env;
|
|
1667
|
+
const value = env?.[name];
|
|
1668
|
+
return value === "" ? void 0 : value;
|
|
1669
|
+
} catch {
|
|
1670
|
+
return void 0;
|
|
1671
|
+
}
|
|
1564
1672
|
}
|
|
1565
1673
|
function describe(error) {
|
|
1566
1674
|
if (error instanceof NiadraAPIError) {
|
|
@@ -1700,7 +1808,7 @@ var Niadra = class {
|
|
|
1700
1808
|
return this.readSpec("POST", "/v1/history/search", params, this.timeouts.navigation, options);
|
|
1701
1809
|
});
|
|
1702
1810
|
}
|
|
1703
|
-
/** The customer's history
|
|
1811
|
+
/** The customer's history, newest first, one line per item, paginated by cursor. */
|
|
1704
1812
|
async timeline(params, options = {}) {
|
|
1705
1813
|
return this.navigate(
|
|
1706
1814
|
() => this.readSpec("POST", "/v1/history/timeline", params, this.timeouts.navigation, options)
|
|
@@ -1708,20 +1816,17 @@ var Niadra = class {
|
|
|
1708
1816
|
}
|
|
1709
1817
|
/**
|
|
1710
1818
|
* Opens one history item from `search()` or `timeline()`: summary, request, commitments,
|
|
1711
|
-
* outcome and resolution.
|
|
1712
|
-
*
|
|
1819
|
+
* outcome and resolution. Sent as `POST /v1/history/open`: the conversation id and `subject` go
|
|
1820
|
+
* in the body, never in a URL.
|
|
1713
1821
|
*/
|
|
1714
1822
|
async open(id, params = {}, options = {}) {
|
|
1715
1823
|
return this.navigate(() => {
|
|
1716
1824
|
if (!id) throw new NiadraValidationError("open() needs an item id");
|
|
1717
|
-
const
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
task_id: params.task_id
|
|
1723
|
-
};
|
|
1724
|
-
return spec;
|
|
1825
|
+
const body = { item_id: id };
|
|
1826
|
+
if (params.subject) body.subject = params.subject;
|
|
1827
|
+
if (params.verification) body.verification = params.verification;
|
|
1828
|
+
if (params.conversation_id) body.conversation_id = params.conversation_id;
|
|
1829
|
+
return this.readSpec("POST", "/v1/history/open", body, this.timeouts.navigation, options);
|
|
1725
1830
|
});
|
|
1726
1831
|
}
|
|
1727
1832
|
/**
|
|
@@ -1762,11 +1867,10 @@ var Niadra = class {
|
|
|
1762
1867
|
const navigator = {
|
|
1763
1868
|
search: (params, voice) => this.search(params, this.voiceBudget(voice)),
|
|
1764
1869
|
timeline: (params, voice) => this.timeline(params, this.voiceBudget(voice)),
|
|
1765
|
-
open: (id, bound, voice) => {
|
|
1766
|
-
const scope = {};
|
|
1870
|
+
open: (id, customer, bound, voice) => {
|
|
1871
|
+
const scope = { subject: customer };
|
|
1767
1872
|
if (bound.verification) scope.verification = bound.verification;
|
|
1768
1873
|
if (bound.conversation_id) scope.conversation_id = bound.conversation_id;
|
|
1769
|
-
if (bound.task_id) scope.task_id = bound.task_id;
|
|
1770
1874
|
return this.open(id, scope, this.voiceBudget(voice));
|
|
1771
1875
|
}
|
|
1772
1876
|
};
|
|
@@ -1829,7 +1933,7 @@ var Niadra = class {
|
|
|
1829
1933
|
body: request,
|
|
1830
1934
|
headers: { "idempotency-key": key },
|
|
1831
1935
|
timeoutMs: this.timeouts.write,
|
|
1832
|
-
retry: core.writes
|
|
1936
|
+
retry: { ...core.writes, totalMs: this.timeouts.write }
|
|
1833
1937
|
});
|
|
1834
1938
|
const [rejected] = response.data?.errors ?? [];
|
|
1835
1939
|
if (rejected) {
|
|
@@ -1860,7 +1964,7 @@ var Niadra = class {
|
|
|
1860
1964
|
path: "/v1/media/uploads",
|
|
1861
1965
|
body: request,
|
|
1862
1966
|
timeoutMs: this.timeouts.write,
|
|
1863
|
-
retry: core.writes,
|
|
1967
|
+
retry: { ...core.writes, totalMs: this.timeouts.write },
|
|
1864
1968
|
signal: options.signal
|
|
1865
1969
|
});
|
|
1866
1970
|
const media_ref = reserved.data?.media_ref;
|
|
@@ -1873,7 +1977,7 @@ var Niadra = class {
|
|
|
1873
1977
|
body: bytes,
|
|
1874
1978
|
headers: uploadHeaders(reserved.data?.upload_headers, request.content_type),
|
|
1875
1979
|
timeoutMs: this.timeouts.upload,
|
|
1876
|
-
retry: core.writes,
|
|
1980
|
+
retry: { ...core.writes, totalMs: this.timeouts.upload },
|
|
1877
1981
|
signal: options.signal
|
|
1878
1982
|
});
|
|
1879
1983
|
}
|
|
@@ -2060,11 +2164,20 @@ var Niadra = class {
|
|
|
2060
2164
|
}
|
|
2061
2165
|
return new Promise((resolve, reject) => {
|
|
2062
2166
|
const key = item.idempotency_key;
|
|
2063
|
-
|
|
2167
|
+
let settled = false;
|
|
2168
|
+
const timer = setTimeout(() => {
|
|
2169
|
+
this.logger.warn(`write not confirmed within ${this.timeouts.write} ms; it stays queued`);
|
|
2170
|
+
settle(new NiadraTimeoutError(this.timeouts.write));
|
|
2171
|
+
}, this.timeouts.write);
|
|
2172
|
+
const settle = (error) => {
|
|
2173
|
+
if (settled) return;
|
|
2174
|
+
settled = true;
|
|
2175
|
+
clearTimeout(timer);
|
|
2064
2176
|
if (!error) resolve({ ok: true, idempotency_key: key, error: null });
|
|
2065
2177
|
else if (this.strict) reject(error);
|
|
2066
2178
|
else resolve({ ok: false, idempotency_key: key, error });
|
|
2067
|
-
}
|
|
2179
|
+
};
|
|
2180
|
+
core.queue.push(item, settle);
|
|
2068
2181
|
core.queue.flushInBackground();
|
|
2069
2182
|
});
|
|
2070
2183
|
}
|
|
@@ -2251,22 +2364,22 @@ var Answer = class {
|
|
|
2251
2364
|
parsed(value) {
|
|
2252
2365
|
try {
|
|
2253
2366
|
if (!this.stream) {
|
|
2254
|
-
this.record(messageText(value));
|
|
2367
|
+
this.record(messageText(value), modelUsage(value));
|
|
2255
2368
|
return value;
|
|
2256
2369
|
}
|
|
2257
|
-
return isAsyncIterable(value) ? captureStream(value, (text) => {
|
|
2258
|
-
this.record(text);
|
|
2370
|
+
return isAsyncIterable(value) ? captureStream(value, (text, usage) => {
|
|
2371
|
+
this.record(text, usage);
|
|
2259
2372
|
}) : value;
|
|
2260
2373
|
} catch (error) {
|
|
2261
2374
|
this.session.logger.warn(`could not capture the model's answer (${errorName(error)})`);
|
|
2262
2375
|
return value;
|
|
2263
2376
|
}
|
|
2264
2377
|
}
|
|
2265
|
-
record(text) {
|
|
2378
|
+
record(text, usage) {
|
|
2266
2379
|
if (this.recorded || !text) return;
|
|
2267
2380
|
this.recorded = true;
|
|
2268
2381
|
try {
|
|
2269
|
-
this.session.agent(text);
|
|
2382
|
+
this.session.agent(text, usage ? { usage } : {});
|
|
2270
2383
|
} catch (error) {
|
|
2271
2384
|
this.session.logger.warn(`could not record the model's answer (${errorName(error)})`);
|
|
2272
2385
|
}
|
|
@@ -2274,11 +2387,19 @@ var Answer = class {
|
|
|
2274
2387
|
};
|
|
2275
2388
|
function captureStream(stream, done) {
|
|
2276
2389
|
const parts = [];
|
|
2390
|
+
let usage = null;
|
|
2391
|
+
let model;
|
|
2277
2392
|
let finished = false;
|
|
2393
|
+
const observe = (chunk) => {
|
|
2394
|
+
const reported = property(chunk, "usage");
|
|
2395
|
+
if (isRecord(reported)) usage = reported;
|
|
2396
|
+
const name = property(chunk, "model");
|
|
2397
|
+
if (typeof name === "string" && name) model = name;
|
|
2398
|
+
};
|
|
2278
2399
|
const finish = () => {
|
|
2279
2400
|
if (finished) return;
|
|
2280
2401
|
finished = true;
|
|
2281
|
-
done(parts.join(""));
|
|
2402
|
+
done(parts.join(""), usage ? modelUsage(usage, model ? { model } : {}) : null);
|
|
2282
2403
|
};
|
|
2283
2404
|
return new Proxy(stream, {
|
|
2284
2405
|
get(target, prop) {
|
|
@@ -2290,7 +2411,10 @@ function captureStream(stream, done) {
|
|
|
2290
2411
|
try {
|
|
2291
2412
|
const step = await inner.next();
|
|
2292
2413
|
if (step.done) finish();
|
|
2293
|
-
else
|
|
2414
|
+
else {
|
|
2415
|
+
parts.push(deltaText(step.value));
|
|
2416
|
+
observe(step.value);
|
|
2417
|
+
}
|
|
2294
2418
|
return step;
|
|
2295
2419
|
} catch (error) {
|
|
2296
2420
|
finish();
|
|
@@ -2350,6 +2474,6 @@ function errorName(error) {
|
|
|
2350
2474
|
return error instanceof Error ? error.name : typeof error;
|
|
2351
2475
|
}
|
|
2352
2476
|
|
|
2353
|
-
export { Conversation, DEFAULT_CACHE, DEFAULT_QUEUE, DEFAULT_TIMEOUTS, MAX_BATCH_ITEMS, MAX_EVENT_TEXT, MAX_MEDIA_BYTES, Niadra, NiadraAPIError, NiadraAbortError, NiadraAuthenticationError, NiadraConfigError, NiadraConnectionError, NiadraError, NiadraPermissionError, NiadraRateLimitError, NiadraTimeoutError, NiadraValidationError, TOOL_DEFINITIONS, TOOL_NAMES, Task, VERSION, baseURLFromKey, consoleLogger, handles, injectContext, parseApiKey, renderLive, renderSuffix, silentLogger, toObjectRef, uuidv7, wrap };
|
|
2477
|
+
export { Conversation, DEFAULT_CACHE, DEFAULT_QUEUE, DEFAULT_TIMEOUTS, MAX_BATCH_ITEMS, MAX_EVENT_TEXT, MAX_MEDIA_BYTES, Niadra, NiadraAPIError, NiadraAbortError, NiadraAuthenticationError, NiadraConfigError, NiadraConnectionError, NiadraError, NiadraPermissionError, NiadraRateLimitError, NiadraTimeoutError, NiadraValidationError, TOOL_DEFINITIONS, TOOL_NAMES, Task, VERSION, baseURLFromKey, consoleLogger, handles, injectContext, modelUsage, parseApiKey, providerOf, renderLive, renderSuffix, silentLogger, toObjectRef, tokenCounts, uuidv7, wrap };
|
|
2354
2478
|
//# sourceMappingURL=index.js.map
|
|
2355
2479
|
//# sourceMappingURL=index.js.map
|