@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.
Files changed (62) hide show
  1. package/dist/client.d.ts +37 -0
  2. package/dist/client.js +48 -0
  3. package/dist/core/client/client.d.ts +30 -0
  4. package/dist/core/client/client.js +143 -0
  5. package/dist/core/client/errors.d.ts +25 -0
  6. package/dist/core/client/errors.js +31 -0
  7. package/dist/core/client/index.d.ts +2 -0
  8. package/dist/core/client/index.js +2 -0
  9. package/dist/core/types.d.ts +28 -0
  10. package/dist/core/types.js +1 -0
  11. package/dist/index.d.ts +9 -124
  12. package/dist/index.js +5 -421
  13. package/dist/langchain.js +8 -2
  14. package/dist/resources/alerts.d.ts +18 -0
  15. package/dist/resources/alerts.js +10 -0
  16. package/dist/resources/credentials.d.ts +22 -0
  17. package/dist/resources/credentials.js +22 -0
  18. package/dist/resources/events.d.ts +41 -0
  19. package/dist/resources/events.js +20 -0
  20. package/dist/resources/index.d.ts +21 -0
  21. package/dist/resources/index.js +12 -0
  22. package/dist/resources/ingest.d.ts +42 -0
  23. package/dist/resources/ingest.js +30 -0
  24. package/dist/resources/keys.d.ts +34 -0
  25. package/dist/resources/keys.js +20 -0
  26. package/dist/resources/playground.d.ts +7 -0
  27. package/dist/resources/playground.js +10 -0
  28. package/dist/resources/pricing.d.ts +49 -0
  29. package/dist/resources/pricing.js +42 -0
  30. package/dist/resources/projects.d.ts +54 -0
  31. package/dist/resources/projects.js +51 -0
  32. package/dist/resources/prompts-admin.d.ts +23 -0
  33. package/dist/resources/prompts-admin.js +26 -0
  34. package/dist/resources/prompts.d.ts +124 -0
  35. package/dist/resources/prompts.js +67 -0
  36. package/dist/resources/stats.d.ts +65 -0
  37. package/dist/resources/stats.js +29 -0
  38. package/dist/resources/tools.d.ts +66 -0
  39. package/dist/resources/tools.js +30 -0
  40. package/dist/rollout/index.d.ts +1 -0
  41. package/dist/rollout/index.js +1 -0
  42. package/dist/rollout/rollout.d.ts +75 -0
  43. package/dist/rollout/rollout.js +1 -0
  44. package/dist/schema/index.d.ts +3 -0
  45. package/dist/schema/index.js +2 -0
  46. package/dist/schema/serialize/index.d.ts +1 -0
  47. package/dist/schema/serialize/index.js +1 -0
  48. package/dist/schema/serialize/serialize.d.ts +12 -0
  49. package/dist/schema/serialize/serialize.js +42 -0
  50. package/dist/schema/types/index.d.ts +1 -0
  51. package/dist/schema/types/index.js +1 -0
  52. package/dist/schema/types/types.d.ts +58 -0
  53. package/dist/schema/types/types.js +1 -0
  54. package/dist/schema/validate/index.d.ts +1 -0
  55. package/dist/schema/validate/index.js +1 -0
  56. package/dist/schema/validate/validate.d.ts +28 -0
  57. package/dist/schema/validate/validate.js +108 -0
  58. package/dist/track/index.d.ts +2 -0
  59. package/dist/track/index.js +1 -0
  60. package/dist/track/track.d.ts +209 -0
  61. package/dist/track/track.js +513 -0
  62. package/package.json +3 -2
@@ -0,0 +1,66 @@
1
+ import type { FieldSpec } from "../schema/index.js";
2
+ import type { Client } from "../core/client/index.js";
3
+ import type { Scoped } from "./prompts.js";
4
+ /** What calling a tool does outside the process. Null means nobody has said. */
5
+ export type ToolEffect = "readonly" | "idempotent" | "destructive";
6
+ /**
7
+ * A tool's declaration, as stored.
8
+ *
9
+ * The handler is not here and never will be: this is what the model is told,
10
+ * and the function it maps to lives in the caller's codebase.
11
+ */
12
+ export interface ToolRow {
13
+ id: string;
14
+ projectId: string;
15
+ name: string;
16
+ description: string;
17
+ args: FieldSpec[];
18
+ effect: ToolEffect | null;
19
+ pinned: boolean;
20
+ archivedAt: string | null;
21
+ createdAt: string;
22
+ updatedAt: string;
23
+ }
24
+ export interface SaveToolInput {
25
+ projectId: string;
26
+ name: string;
27
+ description: string;
28
+ args?: FieldSpec[];
29
+ effect?: ToolEffect | null;
30
+ pinned?: boolean;
31
+ }
32
+ /**
33
+ * Declared tools.
34
+ *
35
+ * `get` and `list` take an API key, because binding a handler happens in a
36
+ * running service. Writing one is a dashboard action and needs a session — the
37
+ * same split prompts have.
38
+ */
39
+ export declare class Tools {
40
+ private readonly client;
41
+ constructor(client: Client);
42
+ list(query?: Scoped & {
43
+ limit?: number;
44
+ cursor?: string;
45
+ archived?: "only" | "active" | "all";
46
+ }): Promise<{
47
+ tools: ToolRow[];
48
+ nextCursor: string | null;
49
+ }>;
50
+ /** One tool, addressed by uuid or by the name the model calls. */
51
+ get(name: string, query?: Scoped): Promise<{
52
+ tool: ToolRow;
53
+ }>;
54
+ create(body: SaveToolInput): Promise<{
55
+ tool: ToolRow;
56
+ }>;
57
+ update(id: string, body: SaveToolInput, query?: Scoped): Promise<{
58
+ tool: ToolRow;
59
+ }>;
60
+ /** Out of the list, not destroyed. `archived: false` brings it back. */
61
+ archive(id: string, archived: boolean, query?: Scoped): Promise<{
62
+ id: string;
63
+ archivedAt: string | null;
64
+ unchanged?: boolean;
65
+ }>;
66
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Declared tools.
3
+ *
4
+ * `get` and `list` take an API key, because binding a handler happens in a
5
+ * running service. Writing one is a dashboard action and needs a session — the
6
+ * same split prompts have.
7
+ */
8
+ export class Tools {
9
+ client;
10
+ constructor(client) {
11
+ this.client = client;
12
+ }
13
+ list(query = {}) {
14
+ return this.client.get("/api/v1/tools", { ...query });
15
+ }
16
+ /** One tool, addressed by uuid or by the name the model calls. */
17
+ get(name, query = {}) {
18
+ return this.client.get(`/api/v1/tools/${encodeURIComponent(name)}`, { ...query });
19
+ }
20
+ create(body) {
21
+ return this.client.post("/api/v1/tools", body);
22
+ }
23
+ update(id, body, query = {}) {
24
+ return this.client.patch(`/api/v1/tools/${encodeURIComponent(id)}`, body, { ...query });
25
+ }
26
+ /** Out of the list, not destroyed. `archived: false` brings it back. */
27
+ archive(id, archived, query = {}) {
28
+ return this.client.post(`/api/v1/tools/${encodeURIComponent(id)}/archive`, { archived }, { ...query });
29
+ }
30
+ }
@@ -0,0 +1 @@
1
+ export type { RolloutInput, RolloutRecord, RolloutStep } from "./rollout.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import type { RenderedMessage as Message } from "../resources/prompts.js";
2
+ /**
3
+ * The record every capability writes.
4
+ *
5
+ * A prompt run is a rollout with one step. A graph run is a rollout with
6
+ * several. The `steps` column exists in `prompt_rollouts` from the first
7
+ * migration for exactly that reason, so the shape lives here rather than under
8
+ * `prompt/` — a second copy is how the two capabilities end up disagreeing
9
+ * about what a rollout is.
10
+ */
11
+ /** One unit of work inside a rollout. A prompt run has one; an agent run many. */
12
+ export interface RolloutStep {
13
+ /** Position in the run, so a failure can be attributed to a step. */
14
+ index: number;
15
+ /** What produced it — a prompt id, a tool name, a node name. */
16
+ source: string;
17
+ model?: string;
18
+ input?: unknown;
19
+ output?: string;
20
+ status?: "completed" | "failed";
21
+ error?: string;
22
+ inputTokens?: number;
23
+ outputTokens?: number;
24
+ latencyMs?: number;
25
+ }
26
+ /** What a caller hands back after running something itself. */
27
+ export interface RolloutInput {
28
+ /** Dedupe key. Generated if omitted; supply your own to make a retry safe. */
29
+ rolloutId?: string;
30
+ versionId?: string | null;
31
+ model: string;
32
+ fields: Record<string, string>;
33
+ rendered: Message[];
34
+ output: string;
35
+ status?: "completed" | "failed";
36
+ error?: string;
37
+ inputTokens?: number;
38
+ outputTokens?: number;
39
+ cacheReadTokens?: number;
40
+ cacheWriteTokens?: number;
41
+ citationTokens?: number;
42
+ reasoningTokens?: number;
43
+ latencyMs?: number;
44
+ /**
45
+ * The steps a multi-step run took. Absent for a single prompt call, which is
46
+ * the same thing with one step and nothing worth listing.
47
+ */
48
+ steps?: RolloutStep[];
49
+ /**
50
+ * The tools this run was offered, whether or not it called them.
51
+ *
52
+ * Without it a rollout cannot answer the first question anyone asks of a
53
+ * tool-using agent that got something wrong: was the tool it needed even in
54
+ * front of it? "Chose not to call `refund`" and "was never shown `refund`"
55
+ * produce identical records and want opposite fixes. Selection makes this
56
+ * sharper, not softer, because the offered set now varies per turn.
57
+ */
58
+ offeredTools?: string[];
59
+ /** Set only for evaluation rollouts. Production traffic leaves it unset. */
60
+ caseId?: string;
61
+ seed?: number;
62
+ candidateId?: string;
63
+ parentId?: string;
64
+ generation?: number;
65
+ }
66
+ /** What the server returns once it has priced and stored one. */
67
+ export interface RolloutRecord {
68
+ rolloutId: string;
69
+ status: "completed" | "failed";
70
+ output: string;
71
+ costMicros: number;
72
+ latencyMs: number;
73
+ inputTokens: number;
74
+ outputTokens: number;
75
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export { serializeFields } from "./serialize/index.js";
2
+ export type { FieldSpec, FieldType } from "./types/index.js";
3
+ export { type FieldError, FieldValidationError, validateFields } from "./validate/index.js";
@@ -0,0 +1,2 @@
1
+ export { serializeFields } from "./serialize/index.js";
2
+ export { FieldValidationError, validateFields } from "./validate/index.js";
@@ -0,0 +1 @@
1
+ export { serializeFields } from "./serialize.js";
@@ -0,0 +1 @@
1
+ export { serializeFields } from "./serialize.js";
@@ -0,0 +1,12 @@
1
+ import type { FieldSpec } from "../types/index.js";
2
+ /**
3
+ * Validated values as the flat string map the renderer substitutes.
4
+ *
5
+ * Throws rather than rendering something approximate. Headless there is no
6
+ * human reading the reply, so a missing required field would otherwise buy a
7
+ * paid-for response with a hole in it that nothing downstream can detect.
8
+ *
9
+ * An optional field left unset is omitted, so its `{name}` stays visible — the
10
+ * same behaviour the prompt builder has always had.
11
+ */
12
+ export declare function serializeFields(values: Record<string, unknown>, spec: FieldSpec[]): Record<string, string>;
@@ -0,0 +1,42 @@
1
+ import { FieldValidationError, isMissing, toBoolean, toNumber, validateFields, } from "../validate/index.js";
2
+ /** One declared value as the text that replaces its placeholder. */
3
+ function renderOne(field, value) {
4
+ switch (field.type) {
5
+ case "number":
6
+ return String(toNumber(value));
7
+ case "boolean":
8
+ return toBoolean(value) ? (field.trueText ?? "true") : (field.falseText ?? "false");
9
+ case "list":
10
+ return value.map(String).join(field.separator ?? ", ");
11
+ case "json":
12
+ return JSON.stringify(value, null, 2);
13
+ default:
14
+ return String(value);
15
+ }
16
+ }
17
+ /**
18
+ * Validated values as the flat string map the renderer substitutes.
19
+ *
20
+ * Throws rather than rendering something approximate. Headless there is no
21
+ * human reading the reply, so a missing required field would otherwise buy a
22
+ * paid-for response with a hole in it that nothing downstream can detect.
23
+ *
24
+ * An optional field left unset is omitted, so its `{name}` stays visible — the
25
+ * same behaviour the prompt builder has always had.
26
+ */
27
+ export function serializeFields(values, spec) {
28
+ const errors = validateFields(values, spec);
29
+ if (errors.length)
30
+ throw new FieldValidationError(errors);
31
+ const out = {};
32
+ for (const field of spec) {
33
+ const value = values[field.name];
34
+ if (isMissing(value)) {
35
+ if (field.default !== undefined)
36
+ out[field.name] = field.default;
37
+ continue;
38
+ }
39
+ out[field.name] = renderOne(field, value);
40
+ }
41
+ return out;
42
+ }
@@ -0,0 +1 @@
1
+ export type { FieldSpec, FieldType } from "./types.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Typed prompt fields — what a `{placeholder}` accepts, and how it renders.
3
+ *
4
+ * Deliberately a copy of `lib/fields.ts` rather than an import. This package is
5
+ * published and depends on nothing; reaching into the app for a type would make
6
+ * every installed copy break the next time the app moved a file. The two are
7
+ * kept in step by the wire format, which is the only contract that matters.
8
+ *
9
+ * A field type is two things, and the second is the one that gets forgotten: an
10
+ * input contract, and a rendering rule. Everything ends up interpolated into a
11
+ * string, so `{max_words}` given `50` has to become `"50"` and `{tags}` given
12
+ * `["a","b"]` has to become something the prompt author chose rather than
13
+ * whatever the first implementation happened to do.
14
+ *
15
+ * Deliberately not called `Variable`: a tool's arguments and an agent node's
16
+ * inputs are the same problem, and naming this after prompts guarantees it gets
17
+ * written twice more with three different meanings for `required`.
18
+ */
19
+ export type FieldType = "string" | "text" | "number" | "boolean" | "enum" | "list" | "json";
20
+ export interface FieldSpec {
21
+ /** Matches the `{placeholder}` in the blocks or question. */
22
+ name: string;
23
+ type: FieldType;
24
+ /** A missing required field throws before anything is sent. */
25
+ required: boolean;
26
+ /**
27
+ * Substituted verbatim when the field is absent — already-rendered text, not
28
+ * a value to coerce. A `list` default is the joined string, a `json` default
29
+ * is the printed JSON. Authored once by a human rather than typed per run, so
30
+ * asking for it in final form is simpler than parsing it back per type.
31
+ */
32
+ default?: string;
33
+ /**
34
+ * What the field means. Shown in the UI, and given to the optimizer's
35
+ * reflector — knowing what a field is *for* is the difference between a
36
+ * useful prompt mutation and a guess.
37
+ */
38
+ description?: string;
39
+ /** `enum` — the permitted values. */
40
+ options?: string[];
41
+ /** `list` — what joins the items. Defaults to ", ". */
42
+ separator?: string;
43
+ /** `number` — inclusive bounds. */
44
+ min?: number;
45
+ max?: number;
46
+ /** `string` | `text` — rejected above this length. */
47
+ maxLength?: number;
48
+ /** `boolean` — words to render instead of "true"/"false". */
49
+ trueText?: string;
50
+ falseText?: string;
51
+ /**
52
+ * This is the field a dataset varies.
53
+ *
54
+ * A prompt declaring `{question}` plus 200 stored values for it *is* a
55
+ * 200-case dataset; this flag is what says which field those values fill.
56
+ */
57
+ datasetKey?: boolean;
58
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export { type FieldError, FieldValidationError, isMissing, toBoolean, toNumber, validateFields, } from "./validate.js";
@@ -0,0 +1 @@
1
+ export { FieldValidationError, isMissing, toBoolean, toNumber, validateFields, } from "./validate.js";
@@ -0,0 +1,28 @@
1
+ import type { FieldSpec } from "../types/index.js";
2
+ export type FieldError = {
3
+ field: string;
4
+ message: string;
5
+ };
6
+ export declare class FieldValidationError extends Error {
7
+ readonly errors: FieldError[];
8
+ constructor(errors: FieldError[]);
9
+ }
10
+ /** Absent means absent. An empty string is a value someone chose to send. */
11
+ export declare function isMissing(value: unknown): boolean;
12
+ /**
13
+ * Coercions shared with the renderer.
14
+ *
15
+ * Shared deliberately: if the checker and the renderer disagreed, a value could
16
+ * pass validation and then render as something else — a request you paid for
17
+ * and cannot explain.
18
+ */
19
+ export declare function toNumber(value: unknown): number;
20
+ export declare function toBoolean(value: unknown): boolean;
21
+ /**
22
+ * Everything wrong with these values, rather than the first thing.
23
+ *
24
+ * Values with no matching field are ignored, not reported: a stored prompt keeps
25
+ * values for placeholders that were later edited out, and treating a stale key
26
+ * as an error would fail a prompt that renders perfectly well.
27
+ */
28
+ export declare function validateFields(values: Record<string, unknown>, spec: FieldSpec[]): FieldError[];
@@ -0,0 +1,108 @@
1
+ export class FieldValidationError extends Error {
2
+ errors;
3
+ constructor(errors) {
4
+ super(errors.map((e) => `${e.field}: ${e.message}`).join("; "));
5
+ this.name = "FieldValidationError";
6
+ this.errors = errors;
7
+ }
8
+ }
9
+ /** Absent means absent. An empty string is a value someone chose to send. */
10
+ export function isMissing(value) {
11
+ return value === undefined || value === null;
12
+ }
13
+ /**
14
+ * Coercions shared with the renderer.
15
+ *
16
+ * Shared deliberately: if the checker and the renderer disagreed, a value could
17
+ * pass validation and then render as something else — a request you paid for
18
+ * and cannot explain.
19
+ */
20
+ export function toNumber(value) {
21
+ return typeof value === "number" ? value : Number(String(value).trim());
22
+ }
23
+ export function toBoolean(value) {
24
+ return typeof value === "boolean" ? value : String(value).toLowerCase() === "true";
25
+ }
26
+ function checkText(field, value) {
27
+ const text = String(value);
28
+ return field.maxLength !== undefined && text.length > field.maxLength
29
+ ? `is ${text.length} characters, over the limit of ${field.maxLength}`
30
+ : null;
31
+ }
32
+ function checkNumber(field, value) {
33
+ const n = toNumber(value);
34
+ if (!Number.isFinite(n))
35
+ return `expects a number, got ${JSON.stringify(value)}`;
36
+ if (field.min !== undefined && n < field.min)
37
+ return `is ${n}, below the minimum of ${field.min}`;
38
+ if (field.max !== undefined && n > field.max)
39
+ return `is ${n}, above the maximum of ${field.max}`;
40
+ return null;
41
+ }
42
+ function checkBoolean(value) {
43
+ if (typeof value === "boolean")
44
+ return null;
45
+ const s = String(value).toLowerCase();
46
+ return s === "true" || s === "false"
47
+ ? null
48
+ : `expects true or false, got ${JSON.stringify(value)}`;
49
+ }
50
+ function checkEnum(field, value) {
51
+ const options = field.options ?? [];
52
+ if (options.length === 0)
53
+ return "is an enum with no options declared";
54
+ return options.includes(String(value))
55
+ ? null
56
+ : `must be one of ${options.join(" | ")}, got ${JSON.stringify(value)}`;
57
+ }
58
+ /** Catches cycles and BigInt, which otherwise reach the provider as a crash. */
59
+ function checkJson(value) {
60
+ try {
61
+ JSON.stringify(value);
62
+ return null;
63
+ }
64
+ catch (err) {
65
+ return `is not serialisable: ${err.message}`;
66
+ }
67
+ }
68
+ function checkOne(field, value) {
69
+ switch (field.type) {
70
+ case "string":
71
+ case "text":
72
+ return checkText(field, value);
73
+ case "number":
74
+ return checkNumber(field, value);
75
+ case "boolean":
76
+ return checkBoolean(value);
77
+ case "enum":
78
+ return checkEnum(field, value);
79
+ case "list":
80
+ return Array.isArray(value) ? null : `expects an array, got ${typeof value}`;
81
+ case "json":
82
+ return checkJson(value);
83
+ }
84
+ }
85
+ /**
86
+ * Everything wrong with these values, rather than the first thing.
87
+ *
88
+ * Values with no matching field are ignored, not reported: a stored prompt keeps
89
+ * values for placeholders that were later edited out, and treating a stale key
90
+ * as an error would fail a prompt that renders perfectly well.
91
+ */
92
+ export function validateFields(values, spec) {
93
+ const errors = [];
94
+ for (const field of spec) {
95
+ const value = values[field.name];
96
+ if (isMissing(value)) {
97
+ if (field.default !== undefined)
98
+ continue;
99
+ if (field.required)
100
+ errors.push({ field: field.name, message: "is required" });
101
+ continue; // optional and unset — the placeholder stays visible
102
+ }
103
+ const problem = checkOne(field, value);
104
+ if (problem)
105
+ errors.push({ field: field.name, message: problem });
106
+ }
107
+ return errors;
108
+ }
@@ -0,0 +1,2 @@
1
+ export type { SpendGraphOptions, TrackEvent } from "./track.js";
2
+ export { SpendGraph } from "./track.js";
@@ -0,0 +1 @@
1
+ export { SpendGraph } from "./track.js";