@spendgraph/sdk 0.1.0 → 0.2.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/client.d.ts +37 -0
- package/dist/client.js +48 -0
- package/dist/core/client/client.d.ts +30 -0
- package/dist/core/client/client.js +143 -0
- package/dist/core/client/errors.d.ts +25 -0
- package/dist/core/client/errors.js +31 -0
- package/dist/core/client/index.d.ts +2 -0
- package/dist/core/client/index.js +2 -0
- package/dist/core/types.d.ts +28 -0
- package/dist/core/types.js +1 -0
- package/dist/index.d.ts +9 -124
- package/dist/index.js +5 -421
- package/dist/langchain.js +8 -2
- package/dist/resources/alerts.d.ts +18 -0
- package/dist/resources/alerts.js +10 -0
- package/dist/resources/credentials.d.ts +22 -0
- package/dist/resources/credentials.js +22 -0
- package/dist/resources/events.d.ts +41 -0
- package/dist/resources/events.js +20 -0
- package/dist/resources/index.d.ts +21 -0
- package/dist/resources/index.js +12 -0
- package/dist/resources/ingest.d.ts +42 -0
- package/dist/resources/ingest.js +30 -0
- package/dist/resources/keys.d.ts +34 -0
- package/dist/resources/keys.js +20 -0
- package/dist/resources/playground.d.ts +7 -0
- package/dist/resources/playground.js +10 -0
- package/dist/resources/pricing.d.ts +49 -0
- package/dist/resources/pricing.js +42 -0
- package/dist/resources/projects.d.ts +54 -0
- package/dist/resources/projects.js +51 -0
- package/dist/resources/prompts-admin.d.ts +23 -0
- package/dist/resources/prompts-admin.js +26 -0
- package/dist/resources/prompts.d.ts +124 -0
- package/dist/resources/prompts.js +67 -0
- package/dist/resources/stats.d.ts +65 -0
- package/dist/resources/stats.js +29 -0
- package/dist/resources/tools.d.ts +66 -0
- package/dist/resources/tools.js +30 -0
- package/dist/rollout/index.d.ts +1 -0
- package/dist/rollout/index.js +1 -0
- package/dist/rollout/rollout.d.ts +75 -0
- package/dist/rollout/rollout.js +1 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/serialize/index.d.ts +1 -0
- package/dist/schema/serialize/index.js +1 -0
- package/dist/schema/serialize/serialize.d.ts +12 -0
- package/dist/schema/serialize/serialize.js +42 -0
- package/dist/schema/types/index.d.ts +1 -0
- package/dist/schema/types/index.js +1 -0
- package/dist/schema/types/types.d.ts +58 -0
- package/dist/schema/types/types.js +1 -0
- package/dist/schema/validate/index.d.ts +1 -0
- package/dist/schema/validate/index.js +1 -0
- package/dist/schema/validate/validate.d.ts +28 -0
- package/dist/schema/validate/validate.js +108 -0
- package/dist/track/index.d.ts +2 -0
- package/dist/track/index.js +1 -0
- package/dist/track/track.d.ts +209 -0
- package/dist/track/track.js +513 -0
- package/package.json +3 -2
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Client } from "./core/client/index.js";
|
|
2
|
+
import type { ClientOptions } from "./core/types.js";
|
|
3
|
+
import { Alerts, Credentials, Events, Ingest, Invites, Keys, Models, Playground, Pricing, Projects, Prompts, PromptsAdmin, Stats, Tools } from "./resources/index.js";
|
|
4
|
+
export interface SpendgraphOptions extends ClientOptions {
|
|
5
|
+
/** Scopes writes that accept one. A key is already pinned to its project. */
|
|
6
|
+
project?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The whole spendgraph API, one client.
|
|
10
|
+
*
|
|
11
|
+
* Every other package in this repo goes through this rather than calling the
|
|
12
|
+
* app directly, so base URL, auth, retries and error shapes are decided once.
|
|
13
|
+
*
|
|
14
|
+
* An `apiKey` reaches usage, stats and prompts. The dashboard half — keys,
|
|
15
|
+
* projects, pricing, credentials — is gated on a signed-in user server-side and
|
|
16
|
+
* needs `session`; there is no API-key path to it, which is what stops a leaked
|
|
17
|
+
* ingest key from minting more keys or reading a provider secret.
|
|
18
|
+
*/
|
|
19
|
+
export declare class Spendgraph {
|
|
20
|
+
/** The transport. Reach for it only for a route this class does not cover. */
|
|
21
|
+
readonly http: Client;
|
|
22
|
+
readonly ingest: Ingest;
|
|
23
|
+
readonly stats: Stats;
|
|
24
|
+
readonly events: Events;
|
|
25
|
+
readonly alerts: Alerts;
|
|
26
|
+
readonly prompts: Prompts;
|
|
27
|
+
readonly promptsAdmin: PromptsAdmin;
|
|
28
|
+
readonly keys: Keys;
|
|
29
|
+
readonly projects: Projects;
|
|
30
|
+
readonly invites: Invites;
|
|
31
|
+
readonly pricing: Pricing;
|
|
32
|
+
readonly models: Models;
|
|
33
|
+
readonly credentials: Credentials;
|
|
34
|
+
readonly playground: Playground;
|
|
35
|
+
readonly tools: Tools;
|
|
36
|
+
constructor(opts: SpendgraphOptions);
|
|
37
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Client } from "./core/client/index.js";
|
|
2
|
+
import { Alerts, Credentials, Events, Ingest, Invites, Keys, Models, Playground, Pricing, Projects, Prompts, PromptsAdmin, Stats, Tools, } from "./resources/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The whole spendgraph API, one client.
|
|
5
|
+
*
|
|
6
|
+
* Every other package in this repo goes through this rather than calling the
|
|
7
|
+
* app directly, so base URL, auth, retries and error shapes are decided once.
|
|
8
|
+
*
|
|
9
|
+
* An `apiKey` reaches usage, stats and prompts. The dashboard half — keys,
|
|
10
|
+
* projects, pricing, credentials — is gated on a signed-in user server-side and
|
|
11
|
+
* needs `session`; there is no API-key path to it, which is what stops a leaked
|
|
12
|
+
* ingest key from minting more keys or reading a provider secret.
|
|
13
|
+
*/
|
|
14
|
+
export class Spendgraph {
|
|
15
|
+
/** The transport. Reach for it only for a route this class does not cover. */
|
|
16
|
+
http;
|
|
17
|
+
ingest;
|
|
18
|
+
stats;
|
|
19
|
+
events;
|
|
20
|
+
alerts;
|
|
21
|
+
prompts;
|
|
22
|
+
promptsAdmin;
|
|
23
|
+
keys;
|
|
24
|
+
projects;
|
|
25
|
+
invites;
|
|
26
|
+
pricing;
|
|
27
|
+
models;
|
|
28
|
+
credentials;
|
|
29
|
+
playground;
|
|
30
|
+
tools;
|
|
31
|
+
constructor(opts) {
|
|
32
|
+
this.http = new Client(opts);
|
|
33
|
+
this.ingest = new Ingest(this.http, opts.project);
|
|
34
|
+
this.stats = new Stats(this.http);
|
|
35
|
+
this.events = new Events(this.http);
|
|
36
|
+
this.alerts = new Alerts(this.http);
|
|
37
|
+
this.prompts = new Prompts(this.http);
|
|
38
|
+
this.promptsAdmin = new PromptsAdmin(this.http);
|
|
39
|
+
this.keys = new Keys(this.http);
|
|
40
|
+
this.projects = new Projects(this.http);
|
|
41
|
+
this.invites = new Invites(this.http);
|
|
42
|
+
this.pricing = new Pricing(this.http);
|
|
43
|
+
this.models = new Models(this.http);
|
|
44
|
+
this.credentials = new Credentials(this.http);
|
|
45
|
+
this.playground = new Playground(this.http);
|
|
46
|
+
this.tools = new Tools(this.http);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ClientOptions, Query } from "../types.js";
|
|
2
|
+
/** Drops empty parameters rather than sending the string "undefined". */
|
|
3
|
+
export declare function queryString(query?: Query): string;
|
|
4
|
+
/**
|
|
5
|
+
* The one thing in this package that talks to spendgraph.
|
|
6
|
+
*
|
|
7
|
+
* Every resource module is given this and returns data, which is what makes
|
|
8
|
+
* the rest testable with no server.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Client {
|
|
11
|
+
private readonly baseUrl;
|
|
12
|
+
private readonly doFetch;
|
|
13
|
+
private readonly sleep;
|
|
14
|
+
private readonly attempts;
|
|
15
|
+
private readonly maxWaitMs;
|
|
16
|
+
private readonly auth;
|
|
17
|
+
constructor(opts: ClientOptions);
|
|
18
|
+
/** True once anything is set that the server might accept. */
|
|
19
|
+
get authenticated(): boolean;
|
|
20
|
+
request<T>(path: string, init?: RequestInit): Promise<T>;
|
|
21
|
+
get<T>(path: string, query?: Query): Promise<T>;
|
|
22
|
+
post<T>(path: string, body?: unknown, query?: Query): Promise<T>;
|
|
23
|
+
put<T>(path: string, body?: unknown, query?: Query): Promise<T>;
|
|
24
|
+
patch<T>(path: string, body?: unknown, query?: Query): Promise<T>;
|
|
25
|
+
delete<T>(path: string, query?: Query): Promise<T>;
|
|
26
|
+
private send;
|
|
27
|
+
/** A 204 and an empty body are success, not a JSON parse failure. */
|
|
28
|
+
private decode;
|
|
29
|
+
private toError;
|
|
30
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { SpendgraphError } from "./errors.js";
|
|
2
|
+
const DEFAULT_ATTEMPTS = 3;
|
|
3
|
+
const DEFAULT_MAX_WAIT_MS = 5_000;
|
|
4
|
+
const BACKOFF_BASE_MS = 250;
|
|
5
|
+
/**
|
|
6
|
+
* `retry-after` in milliseconds, or null when the server did not say.
|
|
7
|
+
*
|
|
8
|
+
* Whole seconds per the HTTP spec. Honouring it beats guessing — a backoff
|
|
9
|
+
* shorter than the window just burns another attempt.
|
|
10
|
+
*/
|
|
11
|
+
function retryAfterMs(res) {
|
|
12
|
+
const raw = res.headers.get("retry-after");
|
|
13
|
+
if (!raw)
|
|
14
|
+
return null;
|
|
15
|
+
const seconds = Number(raw);
|
|
16
|
+
return Number.isFinite(seconds) && seconds >= 0 ? seconds * 1000 : null;
|
|
17
|
+
}
|
|
18
|
+
/** Drops empty parameters rather than sending the string "undefined". */
|
|
19
|
+
export function queryString(query = {}) {
|
|
20
|
+
const params = new URLSearchParams();
|
|
21
|
+
for (const [key, value] of Object.entries(query)) {
|
|
22
|
+
if (value === undefined || value === null || value === "")
|
|
23
|
+
continue;
|
|
24
|
+
params.set(key, String(value));
|
|
25
|
+
}
|
|
26
|
+
const encoded = params.toString();
|
|
27
|
+
return encoded ? `?${encoded}` : "";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The one thing in this package that talks to spendgraph.
|
|
31
|
+
*
|
|
32
|
+
* Every resource module is given this and returns data, which is what makes
|
|
33
|
+
* the rest testable with no server.
|
|
34
|
+
*/
|
|
35
|
+
export class Client {
|
|
36
|
+
baseUrl;
|
|
37
|
+
doFetch;
|
|
38
|
+
sleep;
|
|
39
|
+
attempts;
|
|
40
|
+
maxWaitMs;
|
|
41
|
+
auth;
|
|
42
|
+
constructor(opts) {
|
|
43
|
+
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
44
|
+
this.doFetch = opts.fetch ?? globalThis.fetch;
|
|
45
|
+
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
46
|
+
this.attempts = Math.max(1, opts.attempts ?? DEFAULT_ATTEMPTS);
|
|
47
|
+
this.maxWaitMs = opts.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;
|
|
48
|
+
this.auth = {
|
|
49
|
+
...opts.headers,
|
|
50
|
+
...(opts.apiKey ? { "x-api-key": opts.apiKey } : {}),
|
|
51
|
+
...(opts.session ? { cookie: opts.session } : {}),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** True once anything is set that the server might accept. */
|
|
55
|
+
get authenticated() {
|
|
56
|
+
return Object.keys(this.auth).length > 0;
|
|
57
|
+
}
|
|
58
|
+
async request(path, init = {}) {
|
|
59
|
+
if (!this.authenticated) {
|
|
60
|
+
throw new SpendgraphError(401, "no_credentials", "No spendgraph apiKey or session set.");
|
|
61
|
+
}
|
|
62
|
+
const url = `${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
63
|
+
let last;
|
|
64
|
+
for (let attempt = 1; attempt <= this.attempts; attempt++) {
|
|
65
|
+
let res;
|
|
66
|
+
try {
|
|
67
|
+
res = await this.doFetch(url, {
|
|
68
|
+
...init,
|
|
69
|
+
headers: { "content-type": "application/json", ...this.auth, ...init.headers },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
last = new SpendgraphError(0, "network_error", err.message, {
|
|
74
|
+
attempts: attempt,
|
|
75
|
+
});
|
|
76
|
+
if (attempt < this.attempts) {
|
|
77
|
+
await this.sleep(Math.min(this.maxWaitMs, BACKOFF_BASE_MS * 2 ** (attempt - 1)));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
throw last;
|
|
81
|
+
}
|
|
82
|
+
if (res.ok)
|
|
83
|
+
return (await this.decode(res));
|
|
84
|
+
last = await this.toError(res, attempt);
|
|
85
|
+
if (!last.retryable || attempt === this.attempts)
|
|
86
|
+
throw last;
|
|
87
|
+
const wait = retryAfterMs(res) ?? BACKOFF_BASE_MS * 2 ** (attempt - 1);
|
|
88
|
+
await this.sleep(Math.min(this.maxWaitMs, wait));
|
|
89
|
+
}
|
|
90
|
+
throw last ?? new SpendgraphError(0, "unknown", "Request failed.");
|
|
91
|
+
}
|
|
92
|
+
get(path, query) {
|
|
93
|
+
return this.request(`${path}${queryString(query)}`);
|
|
94
|
+
}
|
|
95
|
+
post(path, body, query) {
|
|
96
|
+
return this.send("POST", path, body, query);
|
|
97
|
+
}
|
|
98
|
+
put(path, body, query) {
|
|
99
|
+
return this.send("PUT", path, body, query);
|
|
100
|
+
}
|
|
101
|
+
patch(path, body, query) {
|
|
102
|
+
return this.send("PATCH", path, body, query);
|
|
103
|
+
}
|
|
104
|
+
delete(path, query) {
|
|
105
|
+
return this.request(`${path}${queryString(query)}`, { method: "DELETE" });
|
|
106
|
+
}
|
|
107
|
+
send(method, path, body, query) {
|
|
108
|
+
return this.request(`${path}${queryString(query)}`, {
|
|
109
|
+
method,
|
|
110
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/** A 204 and an empty body are success, not a JSON parse failure. */
|
|
114
|
+
async decode(res) {
|
|
115
|
+
if (res.status === 204)
|
|
116
|
+
return undefined;
|
|
117
|
+
const text = await res.text();
|
|
118
|
+
if (!text)
|
|
119
|
+
return undefined;
|
|
120
|
+
try {
|
|
121
|
+
return JSON.parse(text);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return text;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async toError(res, attempt) {
|
|
128
|
+
let code = "http_error";
|
|
129
|
+
let message = `${res.status} ${res.statusText}`.trim();
|
|
130
|
+
try {
|
|
131
|
+
const body = (await res.json());
|
|
132
|
+
if (body?.error?.code)
|
|
133
|
+
code = body.error.code;
|
|
134
|
+
if (body?.error?.message)
|
|
135
|
+
message = body.error.message;
|
|
136
|
+
}
|
|
137
|
+
catch { }
|
|
138
|
+
return new SpendgraphError(res.status, code, message, {
|
|
139
|
+
attempts: attempt,
|
|
140
|
+
retryAfterMs: retryAfterMs(res),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One error type for everything the API can refuse, carrying the status.
|
|
3
|
+
*
|
|
4
|
+
* A caller retrying blind is the failure this prevents: 429 is worth waiting
|
|
5
|
+
* out, 401 never will be, and a message alone cannot tell them apart.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SpendgraphError extends Error {
|
|
8
|
+
readonly status: number;
|
|
9
|
+
/** The API's machine-readable code, when it sent one. */
|
|
10
|
+
readonly code: string;
|
|
11
|
+
/** How many attempts were made, so a log line says "gave up after 4". */
|
|
12
|
+
readonly attempts: number;
|
|
13
|
+
/** The server's own `retry-after`, in ms. Null when it did not send one. */
|
|
14
|
+
readonly retryAfterMs: number | null;
|
|
15
|
+
constructor(status: number, code: string, message: string, opts?: {
|
|
16
|
+
attempts?: number;
|
|
17
|
+
retryAfterMs?: number | null;
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Worth trying again: rate limits, server faults, and a connection that never
|
|
21
|
+
* got far enough to have a status. Never a bad request — retrying a 401 or a
|
|
22
|
+
* 422 just spends the same attempt budget on the same answer.
|
|
23
|
+
*/
|
|
24
|
+
get retryable(): boolean;
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One error type for everything the API can refuse, carrying the status.
|
|
3
|
+
*
|
|
4
|
+
* A caller retrying blind is the failure this prevents: 429 is worth waiting
|
|
5
|
+
* out, 401 never will be, and a message alone cannot tell them apart.
|
|
6
|
+
*/
|
|
7
|
+
export class SpendgraphError extends Error {
|
|
8
|
+
status;
|
|
9
|
+
/** The API's machine-readable code, when it sent one. */
|
|
10
|
+
code;
|
|
11
|
+
/** How many attempts were made, so a log line says "gave up after 4". */
|
|
12
|
+
attempts;
|
|
13
|
+
/** The server's own `retry-after`, in ms. Null when it did not send one. */
|
|
14
|
+
retryAfterMs;
|
|
15
|
+
constructor(status, code, message, opts = {}) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "SpendgraphError";
|
|
18
|
+
this.status = status;
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.attempts = opts.attempts ?? 1;
|
|
21
|
+
this.retryAfterMs = opts.retryAfterMs ?? null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Worth trying again: rate limits, server faults, and a connection that never
|
|
25
|
+
* got far enough to have a status. Never a bad request — retrying a 401 or a
|
|
26
|
+
* 422 just spends the same attempt budget on the same answer.
|
|
27
|
+
*/
|
|
28
|
+
get retryable() {
|
|
29
|
+
return this.status === 0 || this.status === 429 || this.status >= 500;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type Tag = string | number | boolean;
|
|
2
|
+
/** How a request proves who it is. */
|
|
3
|
+
export interface Credentials {
|
|
4
|
+
/** spendgraph API key (sg_…). Reaches the key-scoped half of the API. */
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
/**
|
|
7
|
+
* A dashboard session cookie. The other half of the API is gated on a signed-in
|
|
8
|
+
* user and has no API-key path, so a caller without one gets a 401 from the
|
|
9
|
+
* server rather than a different answer from here.
|
|
10
|
+
*/
|
|
11
|
+
session?: string;
|
|
12
|
+
/** Sent on every request, under anything set above. */
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export interface ClientOptions extends Credentials {
|
|
16
|
+
/** Base URL of your deployed spendgraph app. */
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
/** Injected for tests and for runtimes with a non-global fetch. */
|
|
19
|
+
fetch?: typeof fetch;
|
|
20
|
+
/** Injected so tests do not actually wait out a backoff. */
|
|
21
|
+
sleep?: (ms: number) => Promise<void>;
|
|
22
|
+
/** Total attempts including the first. Default 3. */
|
|
23
|
+
attempts?: number;
|
|
24
|
+
/** Never wait longer than this for one backoff. Default 5000. */
|
|
25
|
+
maxWaitMs?: number;
|
|
26
|
+
}
|
|
27
|
+
/** Query parameters, with undefined dropped rather than sent as "undefined". */
|
|
28
|
+
export type Query = Record<string, string | number | boolean | undefined | null>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,124 +1,9 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
export interface TrackEvent {
|
|
12
|
-
model: string;
|
|
13
|
-
/** Uncached input tokens — cache tokens go in the fields below. */
|
|
14
|
-
inputTokens: number;
|
|
15
|
-
outputTokens: number;
|
|
16
|
-
/** Prompt-cache read (hit) tokens, billed at the provider's cache-read rate. */
|
|
17
|
-
cacheReadTokens?: number;
|
|
18
|
-
/** Prompt-cache write (creation) tokens. */
|
|
19
|
-
cacheWriteTokens?: number;
|
|
20
|
-
/**
|
|
21
|
-
* Client-side id: retried sends dedupe server-side.
|
|
22
|
-
* Must be 8–64 characters — shorter ids are rejected with a 422.
|
|
23
|
-
*/
|
|
24
|
-
eventId?: string;
|
|
25
|
-
/** ISO timestamp; defaults to server receive time. */
|
|
26
|
-
timestamp?: string;
|
|
27
|
-
metadata?: Record<string, string | number | boolean>;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Fire-and-forget token tracking. track() never throws and never blocks;
|
|
31
|
-
* events batch in memory and flush every 5s or 20 events. Network failures
|
|
32
|
-
* retry once, then drop with one console.warn — tracking can never break
|
|
33
|
-
* the host app.
|
|
34
|
-
*/
|
|
35
|
-
export declare class SpendGraph {
|
|
36
|
-
private readonly opts;
|
|
37
|
-
private queue;
|
|
38
|
-
private timer;
|
|
39
|
-
private warned;
|
|
40
|
-
/** Events lost in the current run of failures, reset by a successful flush. */
|
|
41
|
-
private dropped;
|
|
42
|
-
/** Unpriced model ids already reported, so each is named at most once. */
|
|
43
|
-
private readonly unpricedSeen;
|
|
44
|
-
private readonly interval;
|
|
45
|
-
private readonly maxBatch;
|
|
46
|
-
/** When the pending flush was scheduled, for the suspend check in track(). */
|
|
47
|
-
private timerAt;
|
|
48
|
-
private suspendWarned;
|
|
49
|
-
private noKeyWarned;
|
|
50
|
-
constructor(opts: SpendGraphOptions);
|
|
51
|
-
/** Record one LLM call. Synchronous, returns void, never throws. */
|
|
52
|
-
track(event: TrackEvent): void;
|
|
53
|
-
/**
|
|
54
|
-
* Says once that tracking is off.
|
|
55
|
-
*
|
|
56
|
-
* Running without a key stays a no-op on purpose — it keeps spendgraph out of
|
|
57
|
-
* tests and local runs without branching at every call site. But an unset
|
|
58
|
-
* SPENDGRAPH_API_KEY is indistinguishable from that choice, and it is the
|
|
59
|
-
* likeliest reason a fresh integration records nothing at all: the code is
|
|
60
|
-
* wired up correctly, the dashboard is empty, and nothing anywhere says why.
|
|
61
|
-
* Every other failure in this class warns once; this was the one that stayed
|
|
62
|
-
* quiet, which made it the hardest to find.
|
|
63
|
-
*/
|
|
64
|
-
private warnNoKey;
|
|
65
|
-
/**
|
|
66
|
-
* Notices that the runtime froze with events still buffered.
|
|
67
|
-
*
|
|
68
|
-
* Serverless platforms suspend a function once it returns rather than
|
|
69
|
-
* exiting it, so neither the flush timer nor `beforeExit` ever runs and the
|
|
70
|
-
* queue is lost without a sound — the failure Langfuse documents for Lambda
|
|
71
|
-
* and Vercel. There is no reliable flag for "am I serverless", but there is
|
|
72
|
-
* direct evidence: a pending timer whose deadline passed long ago did not
|
|
73
|
-
* fire, which only happens if the runtime stopped executing between calls.
|
|
74
|
-
*
|
|
75
|
-
* Seeing that, send the stragglers now (they survive into this invocation)
|
|
76
|
-
* and say once what the fix is. Correct callers await flush(), which clears
|
|
77
|
-
* the timer, so this never fires for them.
|
|
78
|
-
*/
|
|
79
|
-
private detectSuspendedRuntime;
|
|
80
|
-
/** Send everything buffered now. Call at the end of serverless handlers. */
|
|
81
|
-
flush(): Promise<void>;
|
|
82
|
-
/**
|
|
83
|
-
* Wrap an Anthropic or OpenAI client. Use the wrapped client exactly as
|
|
84
|
-
* before — token usage is read off each response and tracked automatically.
|
|
85
|
-
*
|
|
86
|
-
* Streaming is covered too:
|
|
87
|
-
* - helper streams (`anthropic.messages.stream()`,
|
|
88
|
-
* `openai.beta.chat.completions.stream()`) are tracked via their
|
|
89
|
-
* final-message promise — the stream you get back is untouched;
|
|
90
|
-
* - raw streams (`create({ stream: true })`) are tee'd: you receive one
|
|
91
|
-
* branch, usage is accumulated off the other. For OpenAI raw streams,
|
|
92
|
-
* pass `stream_options: { include_usage: true }` or there is no usage
|
|
93
|
-
* to read and the call goes untracked.
|
|
94
|
-
*/
|
|
95
|
-
wrap<T extends object>(client: T): T;
|
|
96
|
-
private proxy;
|
|
97
|
-
private observeResult;
|
|
98
|
-
private interceptStream;
|
|
99
|
-
/** Accumulate usage off a tee'd SSE branch (Anthropic events / OpenAI chunks). */
|
|
100
|
-
private consumeStream;
|
|
101
|
-
private trackFromResponse;
|
|
102
|
-
private send;
|
|
103
|
-
/**
|
|
104
|
-
* Surfaces model ids the server could not price, once each.
|
|
105
|
-
*
|
|
106
|
-
* These are accepted and stored, so nothing here is an error — but they cost
|
|
107
|
-
* $0, and a dashboard reading $0 is indistinguishable from one reading
|
|
108
|
-
* "nothing happened". Naming the id in the integrator's own console is the
|
|
109
|
-
* cheapest possible moment to catch a typo or an unmapped model, and the
|
|
110
|
-
* per-id guard keeps a steady stream of the same unknown model from becoming
|
|
111
|
-
* log noise.
|
|
112
|
-
*/
|
|
113
|
-
private reportUnpriced;
|
|
114
|
-
/**
|
|
115
|
-
* Warns once per outage, with a running count of what was lost.
|
|
116
|
-
*
|
|
117
|
-
* Warning on every flush would spam a hot loop, but warning exactly once per
|
|
118
|
-
* process — the previous behaviour — hid a server-side bug that failed every
|
|
119
|
-
* full batch: one line early in a long-lived process, then silence, while the
|
|
120
|
-
* dashboard quietly undercounted. The count is what makes the silence legible
|
|
121
|
-
* when someone does go looking.
|
|
122
|
-
*/
|
|
123
|
-
private reportDropped;
|
|
124
|
-
}
|
|
1
|
+
export type { SpendgraphOptions } from "./client.js";
|
|
2
|
+
export { Spendgraph } from "./client.js";
|
|
3
|
+
export { Client, queryString, SpendgraphError } from "./core/client/index.js";
|
|
4
|
+
export type { ClientOptions, Credentials as ClientCredentials, Query, Tag } from "./core/types.js";
|
|
5
|
+
export * from "./resources/index.js";
|
|
6
|
+
export type { RolloutInput, RolloutRecord, RolloutStep } from "./rollout/index.js";
|
|
7
|
+
export { type FieldError, type FieldSpec, type FieldType, FieldValidationError, serializeFields, validateFields, } from "./schema/index.js";
|
|
8
|
+
export type { SpendGraphOptions, TrackEvent } from "./track/index.js";
|
|
9
|
+
export { SpendGraph } from "./track/index.js";
|