@broberg/ai-sdk 0.51.0 → 0.53.0
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/chunk-ZSLRCOYM.js +478 -0
- package/dist/chunk-ZSLRCOYM.js.map +1 -0
- package/dist/index.d.ts +90 -60
- package/dist/index.js +129 -5
- package/dist/index.js.map +1 -1
- package/dist/pricing.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-O4LR6X4U.js +0 -478
- package/dist/chunk-O4LR6X4U.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
listMediaPrices,
|
|
19
19
|
pricingFreshness,
|
|
20
20
|
pricingGeneratedAt
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-ZSLRCOYM.js";
|
|
22
22
|
|
|
23
23
|
// src/cost/default-hosts.ts
|
|
24
24
|
var DEFAULT_BASE_URLS = {
|
|
@@ -517,6 +517,7 @@ function anthropicAdapter(config = {}) {
|
|
|
517
517
|
cacheReadTokens: data.usage?.cache_read_input_tokens ?? 0,
|
|
518
518
|
cacheCreationTokens: data.usage?.cache_creation_input_tokens ?? 0
|
|
519
519
|
});
|
|
520
|
+
if (typeof data.model === "string" && data.model) usage.servedModel = data.model;
|
|
520
521
|
const result = { text, usage };
|
|
521
522
|
if (toolCalls.length > 0) result.toolCalls = toolCalls;
|
|
522
523
|
return result;
|
|
@@ -809,6 +810,7 @@ function makeOpenAICompatibleAdapter(config) {
|
|
|
809
810
|
if (config.costFromResponseField && typeof data.usage?.cost === "number") {
|
|
810
811
|
usage.costUsd = data.usage.cost;
|
|
811
812
|
}
|
|
813
|
+
if (typeof data.model === "string" && data.model) usage.servedModel = data.model;
|
|
812
814
|
const result = { text, usage };
|
|
813
815
|
if (toolCalls && toolCalls.length > 0) result.toolCalls = toolCalls;
|
|
814
816
|
return result;
|
|
@@ -876,6 +878,7 @@ function makeOpenAICompatibleAdapter(config) {
|
|
|
876
878
|
if (config.costFromResponseField && typeof chunk.usage.cost === "number") {
|
|
877
879
|
usage.costUsd = chunk.usage.cost;
|
|
878
880
|
}
|
|
881
|
+
if (typeof chunk.model === "string" && chunk.model) usage.servedModel = chunk.model;
|
|
879
882
|
yield { type: "usage", costUsd: usage.costUsd, model: usage.model, usage };
|
|
880
883
|
}
|
|
881
884
|
}
|
|
@@ -3498,6 +3501,9 @@ var aiConfigSchema = z.object({
|
|
|
3498
3501
|
/** F069.1 — customer-key mode. Every provider must be built with byokAdapter(), so no
|
|
3499
3502
|
* call can fall back to the fleet's env keys; createAI throws at setup otherwise. */
|
|
3500
3503
|
byok: z.boolean().optional(),
|
|
3504
|
+
/** F069.2 — attribution labels stamped on EVERY call's usage (and so on every cost-sink
|
|
3505
|
+
* row), e.g. { byok: "true", tenantId }. A call's own `labels` win on the same key. */
|
|
3506
|
+
labels: z.record(z.string(), z.string()).optional(),
|
|
3501
3507
|
/** F039.2 — client-wide opt-out from prompt caching (default: on where the
|
|
3502
3508
|
* provider supports it). A per-call `promptCache` wins over this. */
|
|
3503
3509
|
promptCache: z.boolean().optional(),
|
|
@@ -3517,8 +3523,8 @@ var aiConfigSchema = z.object({
|
|
|
3517
3523
|
import { randomUUID } from "crypto";
|
|
3518
3524
|
|
|
3519
3525
|
// src/version.ts
|
|
3520
|
-
var VERSION = "0.
|
|
3521
|
-
var SDK_TAG = "@broberg/ai-sdk@0.
|
|
3526
|
+
var VERSION = "0.53.0";
|
|
3527
|
+
var SDK_TAG = "@broberg/ai-sdk@0.53.0";
|
|
3522
3528
|
|
|
3523
3529
|
// src/cost/sinks/upmetrics.ts
|
|
3524
3530
|
function isRetryableStatus(status) {
|
|
@@ -3568,6 +3574,9 @@ function upmetricsSink(config) {
|
|
|
3568
3574
|
// capability/transport — no ingest-schema change, and without it the one
|
|
3569
3575
|
// field built for auditability existed only in memory.
|
|
3570
3576
|
region: usage.region,
|
|
3577
|
+
// F071.2 — the model that answered (contract with upmetrics F048). Omitted when
|
|
3578
|
+
// the provider did not say, rather than echoing the requested model as a guess.
|
|
3579
|
+
...usage.servedModel ? { served_model: usage.servedModel } : {},
|
|
3571
3580
|
// F050: HOW cost_usd was arrived at. upmetrics already distinguishes
|
|
3572
3581
|
// reported / computed / unpriced — we were sending an assumed number in
|
|
3573
3582
|
// the same field as a measured one, so their labels could not be right
|
|
@@ -3594,6 +3603,36 @@ function upmetricsSink(config) {
|
|
|
3594
3603
|
void config.complianceMode;
|
|
3595
3604
|
return JSON.stringify(body);
|
|
3596
3605
|
}
|
|
3606
|
+
function buildFailureBody(f) {
|
|
3607
|
+
const endedAt = new Date(new Date(f.ts).getTime()).toISOString();
|
|
3608
|
+
const startedAt = new Date(new Date(f.ts).getTime() - (f.latencyMs || 0)).toISOString();
|
|
3609
|
+
const body = {
|
|
3610
|
+
mode: "record",
|
|
3611
|
+
agent_kind: config.agentKind ?? (f.capability === "embedding" ? "embedding" : "chatbot"),
|
|
3612
|
+
agent_name: config.agentName,
|
|
3613
|
+
provider: f.provider,
|
|
3614
|
+
model: f.model,
|
|
3615
|
+
status: "error",
|
|
3616
|
+
input_tokens: 0,
|
|
3617
|
+
output_tokens: 0,
|
|
3618
|
+
cost_usd: 0,
|
|
3619
|
+
duration_ms: f.latencyMs,
|
|
3620
|
+
started_at: startedAt,
|
|
3621
|
+
ended_at: endedAt,
|
|
3622
|
+
tags: {
|
|
3623
|
+
...f.labels,
|
|
3624
|
+
capability: f.capability,
|
|
3625
|
+
transport: f.transport,
|
|
3626
|
+
error_code: f.errorCode,
|
|
3627
|
+
error_kind: f.errorKind,
|
|
3628
|
+
sdk: SDK_TAG,
|
|
3629
|
+
idempotencyKey: randomUUID()
|
|
3630
|
+
}
|
|
3631
|
+
};
|
|
3632
|
+
if (f.tier !== void 0) body.tier = f.tier;
|
|
3633
|
+
if (f.purpose !== void 0) body.purpose = f.purpose;
|
|
3634
|
+
return JSON.stringify(body);
|
|
3635
|
+
}
|
|
3597
3636
|
async function send(body) {
|
|
3598
3637
|
try {
|
|
3599
3638
|
const res = await doFetch(url, {
|
|
@@ -3687,6 +3726,30 @@ function upmetricsSink(config) {
|
|
|
3687
3726
|
config.onError?.(err);
|
|
3688
3727
|
}
|
|
3689
3728
|
},
|
|
3729
|
+
async recordFailure(failure) {
|
|
3730
|
+
try {
|
|
3731
|
+
const body = buildFailureBody(failure);
|
|
3732
|
+
const r = await send(body);
|
|
3733
|
+
if (r.kind === "ok") {
|
|
3734
|
+
counts.sent += 1;
|
|
3735
|
+
return;
|
|
3736
|
+
}
|
|
3737
|
+
if (r.kind === "reject") {
|
|
3738
|
+
counts.rejected += 1;
|
|
3739
|
+
config.onError?.(r.err);
|
|
3740
|
+
return;
|
|
3741
|
+
}
|
|
3742
|
+
if (!retry) {
|
|
3743
|
+
lose(r.err);
|
|
3744
|
+
return;
|
|
3745
|
+
}
|
|
3746
|
+
queue.push({ body, attempts: 1 });
|
|
3747
|
+
enforceCap();
|
|
3748
|
+
schedule();
|
|
3749
|
+
} catch (err) {
|
|
3750
|
+
config.onError?.(err);
|
|
3751
|
+
}
|
|
3752
|
+
},
|
|
3690
3753
|
async flush() {
|
|
3691
3754
|
if (timer) {
|
|
3692
3755
|
clearTimeout(timer);
|
|
@@ -3742,6 +3805,29 @@ function assertByokProviders(providers) {
|
|
|
3742
3805
|
}
|
|
3743
3806
|
}
|
|
3744
3807
|
|
|
3808
|
+
// src/cost/failure.ts
|
|
3809
|
+
function classifyFailure(err) {
|
|
3810
|
+
const e = err;
|
|
3811
|
+
const name = e?.name ?? "";
|
|
3812
|
+
const message = String(e?.message ?? "");
|
|
3813
|
+
if (name === "AbortError" || name === "TimeoutError" || /timed? ?out/i.test(message.split("\n")[0] ?? "")) {
|
|
3814
|
+
return { errorCode: "timeout", errorKind: "timeout" };
|
|
3815
|
+
}
|
|
3816
|
+
let status = typeof e?.status === "number" ? e.status : void 0;
|
|
3817
|
+
if (status === void 0) {
|
|
3818
|
+
const m = /(?:^|\s)([45]\d{2})(?=[:\s]|$)/.exec(message.split("\n")[0] ?? "");
|
|
3819
|
+
if (m) status = Number(m[1]);
|
|
3820
|
+
}
|
|
3821
|
+
if (status !== void 0) {
|
|
3822
|
+
const kind = status === 401 || status === 403 ? "auth" : status === 404 ? "not_found" : status === 429 ? "rate_limit" : status >= 500 ? "server" : "other";
|
|
3823
|
+
return { errorCode: String(status), errorKind: kind };
|
|
3824
|
+
}
|
|
3825
|
+
if (name === "TypeError" && /fetch failed|network|ECONN|ENOTFOUND/i.test(message + (e?.cause?.code ?? ""))) {
|
|
3826
|
+
return { errorCode: "network", errorKind: "network" };
|
|
3827
|
+
}
|
|
3828
|
+
return { errorCode: "unknown", errorKind: "other" };
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3745
3831
|
// src/client.ts
|
|
3746
3832
|
var DEFAULT_IMAGE_SPEC = {
|
|
3747
3833
|
provider: "fal",
|
|
@@ -3831,11 +3917,19 @@ function createAI(config = {}) {
|
|
|
3831
3917
|
usage.capability = capability;
|
|
3832
3918
|
if (tier) usage.tier = tier;
|
|
3833
3919
|
if (purpose) usage.purpose = purpose;
|
|
3834
|
-
|
|
3920
|
+
const merged = { ...cfg.labels, ...labels };
|
|
3921
|
+
if (Object.keys(merged).length > 0) usage.labels = merged;
|
|
3835
3922
|
usage.latencyMs = Math.round(latencyMs);
|
|
3836
3923
|
if (!usage.ts) usage.ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
3837
3924
|
return usage;
|
|
3838
3925
|
}
|
|
3926
|
+
async function reportFailure(f, err) {
|
|
3927
|
+
if (!costSink?.recordFailure) return;
|
|
3928
|
+
try {
|
|
3929
|
+
await costSink.recordFailure({ ...f, ...classifyFailure(err), ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3930
|
+
} catch {
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3839
3933
|
async function report(usage) {
|
|
3840
3934
|
if (!costSink) return;
|
|
3841
3935
|
try {
|
|
@@ -3871,8 +3965,8 @@ function createAI(config = {}) {
|
|
|
3871
3965
|
for (let i = 0; i < routes.length; i++) {
|
|
3872
3966
|
const spec = routes[i];
|
|
3873
3967
|
await preflight(spec, opts.estIn, opts.estOut);
|
|
3968
|
+
const t0 = performance.now();
|
|
3874
3969
|
try {
|
|
3875
|
-
const t0 = performance.now();
|
|
3876
3970
|
const res = await opts.invoke(spec);
|
|
3877
3971
|
enrich(res.usage, opts.capability, i === 0 ? opts.tier : void 0, opts.purpose, performance.now() - t0, opts.labels);
|
|
3878
3972
|
await settle(res.usage);
|
|
@@ -3880,6 +3974,20 @@ function createAI(config = {}) {
|
|
|
3880
3974
|
return res;
|
|
3881
3975
|
} catch (e) {
|
|
3882
3976
|
lastErr = e;
|
|
3977
|
+
const labels = { ...cfg.labels, ...opts.labels };
|
|
3978
|
+
await reportFailure(
|
|
3979
|
+
{
|
|
3980
|
+
provider: spec.provider,
|
|
3981
|
+
model: spec.model,
|
|
3982
|
+
transport: spec.transport,
|
|
3983
|
+
capability: opts.capability,
|
|
3984
|
+
...i === 0 && opts.tier ? { tier: opts.tier } : {},
|
|
3985
|
+
...opts.purpose ? { purpose: opts.purpose } : {},
|
|
3986
|
+
...Object.keys(labels).length ? { labels } : {},
|
|
3987
|
+
latencyMs: Math.round(performance.now() - t0)
|
|
3988
|
+
},
|
|
3989
|
+
e
|
|
3990
|
+
);
|
|
3883
3991
|
}
|
|
3884
3992
|
}
|
|
3885
3993
|
throw lastErr;
|
|
@@ -3949,6 +4057,19 @@ function createAI(config = {}) {
|
|
|
3949
4057
|
return;
|
|
3950
4058
|
} catch (e) {
|
|
3951
4059
|
lastErr = e;
|
|
4060
|
+
await reportFailure(
|
|
4061
|
+
{
|
|
4062
|
+
provider: spec.provider,
|
|
4063
|
+
model: spec.model,
|
|
4064
|
+
transport: spec.transport,
|
|
4065
|
+
capability: "chat",
|
|
4066
|
+
...i === 0 ? { tier } : {},
|
|
4067
|
+
...input.purpose ? { purpose: input.purpose } : {},
|
|
4068
|
+
...Object.keys({ ...cfg.labels, ...input.labels }).length ? { labels: { ...cfg.labels, ...input.labels } } : {},
|
|
4069
|
+
latencyMs: Math.round(performance.now() - t0)
|
|
4070
|
+
},
|
|
4071
|
+
e
|
|
4072
|
+
);
|
|
3952
4073
|
if (emitted || !eligibleForFallback(e)) {
|
|
3953
4074
|
yield errorEvent(e);
|
|
3954
4075
|
return;
|
|
@@ -4495,6 +4616,9 @@ function multiSink(sinks) {
|
|
|
4495
4616
|
return {
|
|
4496
4617
|
async record(usage) {
|
|
4497
4618
|
await Promise.allSettled(sinks.map(async (s) => s.record(usage)));
|
|
4619
|
+
},
|
|
4620
|
+
async recordFailure(failure) {
|
|
4621
|
+
await Promise.allSettled(sinks.map(async (s) => s.recordFailure?.(failure)));
|
|
4498
4622
|
}
|
|
4499
4623
|
};
|
|
4500
4624
|
}
|