@buildinternet/releases-api-types 0.52.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buildinternet/releases-api-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"description": "Wire protocol — request/response types for the Releases registry API. Shared by web, workers, and clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@buildinternet/releases-core": "^0.
|
|
20
|
+
"@buildinternet/releases-core": "^0.31.0",
|
|
21
21
|
"zod": "^4.4.3"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
package/src/api-types.ts
CHANGED
|
@@ -21,6 +21,11 @@ import type {
|
|
|
21
21
|
OpenRouterCatalogModelSchema,
|
|
22
22
|
AiLaneModelsResponseSchema,
|
|
23
23
|
} from "./schemas/ai-models.js";
|
|
24
|
+
import type {
|
|
25
|
+
MarketingClassifierThresholdPutSchema,
|
|
26
|
+
MarketingFilteredSourceSchema,
|
|
27
|
+
MarketingClassifierStateSchema,
|
|
28
|
+
} from "./schemas/marketing-classifier.js";
|
|
24
29
|
import type {
|
|
25
30
|
MediaItemSchema,
|
|
26
31
|
PaginationSchema,
|
|
@@ -216,6 +221,12 @@ import type {
|
|
|
216
221
|
FeedbackUpdateBodySchema,
|
|
217
222
|
FeedbackDeleteResponseSchema,
|
|
218
223
|
} from "./schemas/feedback.js";
|
|
224
|
+
import type {
|
|
225
|
+
ClassificationSummarySchema,
|
|
226
|
+
ClassificationRecentResponseSchema,
|
|
227
|
+
ClassificationRecentItemSchema,
|
|
228
|
+
} from "./schemas/classifications.js";
|
|
229
|
+
import type { SemanticAlertSummarySchema } from "./schemas/semantic-alert-summary.js";
|
|
219
230
|
import type {
|
|
220
231
|
RecommendationNotifyAddedBodySchema,
|
|
221
232
|
RecommendationNotifyAddedResultSchema,
|
|
@@ -304,6 +315,13 @@ export {
|
|
|
304
315
|
OpenRouterCatalogModelSchema,
|
|
305
316
|
AiLaneModelsResponseSchema,
|
|
306
317
|
} from "./schemas/ai-models.js";
|
|
318
|
+
export {
|
|
319
|
+
MarketingClassifierThresholdPutSchema,
|
|
320
|
+
MarketingFilteredSourceSchema,
|
|
321
|
+
MarketingClassifierStateSchema,
|
|
322
|
+
MARKETING_THRESHOLD_MIN,
|
|
323
|
+
MARKETING_THRESHOLD_MAX,
|
|
324
|
+
} from "./schemas/marketing-classifier.js";
|
|
307
325
|
export {
|
|
308
326
|
OrgListItemSchema,
|
|
309
327
|
OrgListResponseSchema,
|
|
@@ -551,6 +569,12 @@ export {
|
|
|
551
569
|
FeedbackUpdateBodySchema,
|
|
552
570
|
FeedbackDeleteResponseSchema,
|
|
553
571
|
} from "./schemas/feedback.js";
|
|
572
|
+
export {
|
|
573
|
+
ClassificationSummarySchema,
|
|
574
|
+
ClassificationRecentResponseSchema,
|
|
575
|
+
ClassificationRecentItemSchema,
|
|
576
|
+
} from "./schemas/classifications.js";
|
|
577
|
+
export { SemanticAlertSummarySchema } from "./schemas/semantic-alert-summary.js";
|
|
554
578
|
export {
|
|
555
579
|
RecommendationNotifyAddedBodySchema,
|
|
556
580
|
RecommendationNotifyAddedResultSchema,
|
|
@@ -629,6 +653,10 @@ export type AiLaneState = z.infer<typeof AiLaneStateSchema>;
|
|
|
629
653
|
export type OpenRouterCatalogModel = z.infer<typeof OpenRouterCatalogModelSchema>;
|
|
630
654
|
export type AiLaneModelsResponse = z.infer<typeof AiLaneModelsResponseSchema>;
|
|
631
655
|
|
|
656
|
+
export type MarketingClassifierThresholdPut = z.infer<typeof MarketingClassifierThresholdPutSchema>;
|
|
657
|
+
export type MarketingFilteredSource = z.infer<typeof MarketingFilteredSourceSchema>;
|
|
658
|
+
export type MarketingClassifierState = z.infer<typeof MarketingClassifierStateSchema>;
|
|
659
|
+
|
|
632
660
|
// ── Stub tier (#1947) ──
|
|
633
661
|
|
|
634
662
|
export type OrgStatus = z.infer<typeof OrgStatusSchema>;
|
|
@@ -1240,6 +1268,46 @@ export interface TestUserWebhookResponse {
|
|
|
1240
1268
|
eventId: string;
|
|
1241
1269
|
}
|
|
1242
1270
|
|
|
1271
|
+
// ── Workspace webhooks ──
|
|
1272
|
+
|
|
1273
|
+
/** Workspace member role (Better Auth `member.role`, not `user.role`). */
|
|
1274
|
+
export type WorkspaceMemberRole = "owner" | "admin" | "member";
|
|
1275
|
+
|
|
1276
|
+
/**
|
|
1277
|
+
* A workspace-owned webhook subscription row (no signing secret). Workspace
|
|
1278
|
+
* webhooks are org-scoped only; there is no workspace follows scope.
|
|
1279
|
+
*/
|
|
1280
|
+
export interface WorkspaceWebhookSubscription extends Omit<
|
|
1281
|
+
UserWebhookSubscription,
|
|
1282
|
+
"userId" | "scope"
|
|
1283
|
+
> {
|
|
1284
|
+
workspaceId: string;
|
|
1285
|
+
scope: "org";
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
/** List item returned by GET /v1/workspaces/:workspaceId/webhooks. */
|
|
1289
|
+
export interface WorkspaceWebhookListItem extends Omit<UserWebhookListItem, "scope"> {
|
|
1290
|
+
workspaceId: string;
|
|
1291
|
+
scope: "org";
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/** GET /v1/workspaces/:workspaceId/webhooks response. */
|
|
1295
|
+
export interface WorkspaceWebhookListResponse {
|
|
1296
|
+
subscriptions: WorkspaceWebhookListItem[];
|
|
1297
|
+
/** The caller's role in the workspace. */
|
|
1298
|
+
role: WorkspaceMemberRole;
|
|
1299
|
+
/** True for owners and admins: create, edit, rotate, and delete. Members can view and test. */
|
|
1300
|
+
canManage: boolean;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/** POST /v1/workspaces/:workspaceId/webhooks response — signing key shown once (omitted for slack/discord). */
|
|
1304
|
+
export interface CreateWorkspaceWebhookResponse
|
|
1305
|
+
extends WorkspaceWebhookSubscription, UserWebhookDeliveryHealth {
|
|
1306
|
+
orgSlug: string | null;
|
|
1307
|
+
orgName: string | null;
|
|
1308
|
+
signingKey?: string;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1243
1311
|
/** One Analytics Engine delivery-attempt row from GET …/webhooks/:id/deliveries. */
|
|
1244
1312
|
export interface WebhookDeliveryRow {
|
|
1245
1313
|
timestamp?: string;
|
|
@@ -1269,15 +1337,88 @@ export interface DigestPrefsRequest {
|
|
|
1269
1337
|
cadence: DigestCadence;
|
|
1270
1338
|
}
|
|
1271
1339
|
|
|
1340
|
+
// ── Semantic alerts (#2304 Phase 1) ──
|
|
1341
|
+
|
|
1342
|
+
/** Hard cap on saved semantic alerts per account. Enforced on create. */
|
|
1343
|
+
export const SEMANTIC_ALERT_MAX_PER_USER = 5;
|
|
1344
|
+
|
|
1345
|
+
/** Freeform interest text, after trim. User-private — never an analytics label. */
|
|
1346
|
+
export const SEMANTIC_ALERT_QUERY_MAX_CHARS = 500;
|
|
1347
|
+
|
|
1348
|
+
/** Default selected-choice probability. Phase 2 matcher uses this unless overridden. */
|
|
1349
|
+
export const SEMANTIC_ALERT_THRESHOLD_DEFAULT = 0.8;
|
|
1350
|
+
|
|
1351
|
+
export const SEMANTIC_ALERT_THRESHOLD_MIN = 0.5;
|
|
1352
|
+
export const SEMANTIC_ALERT_THRESHOLD_MAX = 1;
|
|
1353
|
+
|
|
1354
|
+
/**
|
|
1355
|
+
* v1 candidate pool. Matching (Phase 2) scores only releases that already hit
|
|
1356
|
+
* the caller's follow graph. Not a per-alert setting.
|
|
1357
|
+
*/
|
|
1358
|
+
export const SEMANTIC_ALERT_CANDIDATE_POOL = "follows" as const;
|
|
1359
|
+
|
|
1360
|
+
/** One saved semantic alert. Query text is returned only to the owning user. */
|
|
1361
|
+
export interface SemanticAlert {
|
|
1362
|
+
id: string;
|
|
1363
|
+
query: string;
|
|
1364
|
+
enabled: boolean;
|
|
1365
|
+
/** Inclusive match probability in [0.50, 1.00]. Default 0.80. */
|
|
1366
|
+
threshold: number;
|
|
1367
|
+
/** Stored preference. Phase 1 does not send email. */
|
|
1368
|
+
deliverEmail: boolean;
|
|
1369
|
+
/** Stored preference. Phase 1 does not send webhook deliveries. */
|
|
1370
|
+
deliverWebhook: boolean;
|
|
1371
|
+
/** Optional link to one of the caller's `/v1/me/webhooks` subscriptions. */
|
|
1372
|
+
webhookSubscriptionId: string | null;
|
|
1373
|
+
createdAt: string;
|
|
1374
|
+
updatedAt: string;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* Claimed-match activity for one alert (#2320). Counts are rows in
|
|
1379
|
+
* `semantic_alert_matches` (notified matches only — below-threshold scores
|
|
1380
|
+
* are not stored). Present on list and notifications-bootstrap rows.
|
|
1381
|
+
*/
|
|
1382
|
+
export interface SemanticAlertActivity {
|
|
1383
|
+
/** Matches in the last 7 days. Included in `matches30d`. */
|
|
1384
|
+
matches7d: number;
|
|
1385
|
+
/** Matches in the last 30 days. */
|
|
1386
|
+
matches30d: number;
|
|
1387
|
+
/** Newest claim, or null when the alert has never matched. */
|
|
1388
|
+
lastMatchedAt: string | null;
|
|
1389
|
+
/** Newest claim's release, when that row still exists. */
|
|
1390
|
+
lastMatch: {
|
|
1391
|
+
releaseId: string;
|
|
1392
|
+
title: string;
|
|
1393
|
+
/** Canonical site path, `/release/rel_…`. */
|
|
1394
|
+
path: string;
|
|
1395
|
+
} | null;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
/** A list row: the saved alert plus recent match activity. */
|
|
1399
|
+
export interface SemanticAlertListItem extends SemanticAlert {
|
|
1400
|
+
activity: SemanticAlertActivity;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
/** GET /v1/me/semantic-alerts. `candidatePool` is the locked v1 scope. */
|
|
1404
|
+
export interface SemanticAlertListResponse {
|
|
1405
|
+
alerts: SemanticAlertListItem[];
|
|
1406
|
+
candidatePool: typeof SEMANTIC_ALERT_CANDIDATE_POOL;
|
|
1407
|
+
maxAlerts: number;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1272
1410
|
/**
|
|
1273
1411
|
* GET /v1/me/settings/notifications — one-shot bootstrap for `/account/notifications`.
|
|
1274
1412
|
* Composes digest cadence + feed token + webhook list so the settings page can
|
|
1275
1413
|
* render with a single round-trip instead of three independent mounts.
|
|
1414
|
+
* `semanticAlerts` is `null` when `semantic-alerts-enabled` is off (hide the
|
|
1415
|
+
* section); an array, possibly empty, when the lane is on.
|
|
1276
1416
|
*/
|
|
1277
1417
|
export interface NotificationSettingsResponse {
|
|
1278
1418
|
cadence: DigestCadence;
|
|
1279
1419
|
feedToken: FeedToken | null;
|
|
1280
1420
|
webhooks: UserWebhookListItem[];
|
|
1421
|
+
semanticAlerts: SemanticAlertListItem[] | null;
|
|
1281
1422
|
}
|
|
1282
1423
|
|
|
1283
1424
|
/**
|
|
@@ -1290,6 +1431,24 @@ export interface DeveloperSettingsResponse {
|
|
|
1290
1431
|
apiKeys: UserApiKey[] | null;
|
|
1291
1432
|
}
|
|
1292
1433
|
|
|
1434
|
+
/** GET /v1/me/workspaces — one row per workspace the caller belongs to. */
|
|
1435
|
+
export interface MeWorkspace {
|
|
1436
|
+
/** Better Auth organization id. */
|
|
1437
|
+
id: string;
|
|
1438
|
+
name: string;
|
|
1439
|
+
slug: string;
|
|
1440
|
+
logo: string | null;
|
|
1441
|
+
/** Workspace membership role (`member.role`, not `user.role`). */
|
|
1442
|
+
role: WorkspaceMemberRole;
|
|
1443
|
+
/** The caller's active workspace (see docs/architecture/workspaces.md). */
|
|
1444
|
+
active: boolean;
|
|
1445
|
+
createdAt: string;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
export interface MeWorkspacesResponse {
|
|
1449
|
+
workspaces: MeWorkspace[];
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1293
1452
|
// ── Search ──
|
|
1294
1453
|
|
|
1295
1454
|
export type SearchOrgHit = z.infer<typeof SearchOrgHitSchema>;
|
|
@@ -1755,6 +1914,13 @@ export type FeedbackListResponse = z.infer<typeof FeedbackListResponseSchema>;
|
|
|
1755
1914
|
export type FeedbackUpdateBody = z.infer<typeof FeedbackUpdateBodySchema>;
|
|
1756
1915
|
export type FeedbackDeleteResponse = z.infer<typeof FeedbackDeleteResponseSchema>;
|
|
1757
1916
|
|
|
1917
|
+
// ── Classification analytics ──
|
|
1918
|
+
export type ClassificationSummary = z.infer<typeof ClassificationSummarySchema>;
|
|
1919
|
+
export type ClassificationRecentResponse = z.infer<typeof ClassificationRecentResponseSchema>;
|
|
1920
|
+
export type ClassificationRecentItem = z.infer<typeof ClassificationRecentItemSchema>;
|
|
1921
|
+
|
|
1922
|
+
export type SemanticAlertSummary = z.infer<typeof SemanticAlertSummarySchema>;
|
|
1923
|
+
|
|
1758
1924
|
// ── Recommendations ──
|
|
1759
1925
|
export type RecommendationNotifyAddedBody = z.infer<typeof RecommendationNotifyAddedBodySchema>;
|
|
1760
1926
|
export type RecommendationNotifyAddedResult = z.infer<typeof RecommendationNotifyAddedResultSchema>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Admin classification analytics (`GET /v1/admin/classifications/summary`
|
|
5
|
+
* and `/recent`). Decisions live in Analytics Engine; these shapes are the
|
|
6
|
+
* hydrated read model, not the positional point schema.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const ClassificationOriginSchema = z.enum(["ingest", "manual", "eval"]);
|
|
10
|
+
const ClassificationOriginFilterSchema = z.enum(["ingest", "manual", "eval", "all"]);
|
|
11
|
+
const ClassificationDispositionSchema = z.enum(["kept", "suppressed", "failed", "skipped"]);
|
|
12
|
+
|
|
13
|
+
const ProbabilityBinSchema = z.object({
|
|
14
|
+
/** Inclusive. Width is 0.1. See `probability.threshold` for the current
|
|
15
|
+
* effective suppression threshold and which bin it falls in. */
|
|
16
|
+
start: z.number(),
|
|
17
|
+
/** Exclusive, except the last bin (`0.9`–`1`) which includes `1`. */
|
|
18
|
+
end: z.number(),
|
|
19
|
+
count: z.number(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const ProbabilityHistogramSchema = z.object({
|
|
23
|
+
bins: z.array(ProbabilityBinSchema),
|
|
24
|
+
/** Sampled rows whose double is `< 0` (the absent sentinel). */
|
|
25
|
+
missing: z.number(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/** `GET /v1/admin/classifications/summary`. */
|
|
29
|
+
export const ClassificationSummarySchema = z.object({
|
|
30
|
+
after: z.string(),
|
|
31
|
+
before: z.string(),
|
|
32
|
+
bucket: z.enum(["hour", "day"]),
|
|
33
|
+
origin: ClassificationOriginFilterSchema,
|
|
34
|
+
totals: z.object({
|
|
35
|
+
classified: z.number(),
|
|
36
|
+
kept: z.number(),
|
|
37
|
+
suppressed: z.number(),
|
|
38
|
+
failed: z.number(),
|
|
39
|
+
skipped: z.number(),
|
|
40
|
+
costUsd: z.number(),
|
|
41
|
+
/** `suppressed / (kept + suppressed)`, or null when the denominator is 0. */
|
|
42
|
+
suppressionRate: z.number().nullable(),
|
|
43
|
+
}),
|
|
44
|
+
series: z.array(
|
|
45
|
+
z.object({
|
|
46
|
+
t: z.string(),
|
|
47
|
+
kept: z.number(),
|
|
48
|
+
suppressed: z.number(),
|
|
49
|
+
failed: z.number(),
|
|
50
|
+
skipped: z.number(),
|
|
51
|
+
}),
|
|
52
|
+
),
|
|
53
|
+
choices: z.array(z.object({ choice: z.string(), count: z.number() })),
|
|
54
|
+
choiceSeries: z.array(
|
|
55
|
+
z.object({
|
|
56
|
+
t: z.string(),
|
|
57
|
+
choices: z.record(z.string(), z.number()),
|
|
58
|
+
}),
|
|
59
|
+
),
|
|
60
|
+
models: z.array(
|
|
61
|
+
z.object({
|
|
62
|
+
provider: z.string(),
|
|
63
|
+
model: z.string(),
|
|
64
|
+
count: z.number(),
|
|
65
|
+
costUsd: z.number(),
|
|
66
|
+
}),
|
|
67
|
+
),
|
|
68
|
+
probability: z.object({
|
|
69
|
+
/** Current effective suppression threshold (operator override, else the
|
|
70
|
+
* code default) — no longer a fixed literal now that it's editable via
|
|
71
|
+
* `GET/PUT /v1/admin/marketing-classifier`. */
|
|
72
|
+
threshold: z.number(),
|
|
73
|
+
selected: ProbabilityHistogramSchema,
|
|
74
|
+
confidence: ProbabilityHistogramSchema,
|
|
75
|
+
}),
|
|
76
|
+
meta: z.object({
|
|
77
|
+
dataset: z.string(),
|
|
78
|
+
retentionDays: z.literal(90),
|
|
79
|
+
sampled: z.literal(true),
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/** One sampled decision from `GET /v1/admin/classifications/recent`. */
|
|
84
|
+
export const ClassificationRecentItemSchema = z.object({
|
|
85
|
+
timestamp: z.string(),
|
|
86
|
+
origin: ClassificationOriginSchema,
|
|
87
|
+
sourceId: z.string().nullable(),
|
|
88
|
+
sourceName: z.string().nullable(),
|
|
89
|
+
sourceSlug: z.string().nullable(),
|
|
90
|
+
orgSlug: z.string().nullable(),
|
|
91
|
+
releaseId: z.string().nullable(),
|
|
92
|
+
releaseTitle: z.string().nullable(),
|
|
93
|
+
provider: z.string(),
|
|
94
|
+
model: z.string(),
|
|
95
|
+
choice: z.string(),
|
|
96
|
+
selectedChoiceProbability: z.number().nullable(),
|
|
97
|
+
providerConfidence: z.number().nullable(),
|
|
98
|
+
disposition: ClassificationDispositionSchema,
|
|
99
|
+
reason: z.string(),
|
|
100
|
+
failureCategory: z.string(),
|
|
101
|
+
costUsd: z.number().nullable(),
|
|
102
|
+
durationMs: z.number().nullable(),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/** `GET /v1/admin/classifications/recent` — newest sampled rows, not weighted. */
|
|
106
|
+
export const ClassificationRecentResponseSchema = z.object({
|
|
107
|
+
items: z.array(ClassificationRecentItemSchema),
|
|
108
|
+
nextCursor: z.string().nullable(),
|
|
109
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const MARKETING_THRESHOLD_MIN = 0.5;
|
|
4
|
+
export const MARKETING_THRESHOLD_MAX = 0.99;
|
|
5
|
+
|
|
6
|
+
/** PUT /v1/admin/marketing-classifier — set the suppression threshold. */
|
|
7
|
+
export const MarketingClassifierThresholdPutSchema = z
|
|
8
|
+
.object({
|
|
9
|
+
threshold: z.number().min(MARKETING_THRESHOLD_MIN).max(MARKETING_THRESHOLD_MAX),
|
|
10
|
+
})
|
|
11
|
+
.strict();
|
|
12
|
+
|
|
13
|
+
export const MarketingFilteredSourceSchema = z.object({
|
|
14
|
+
id: z.string(),
|
|
15
|
+
slug: z.string(),
|
|
16
|
+
name: z.string(),
|
|
17
|
+
type: z.string(),
|
|
18
|
+
orgSlug: z.string().nullable(),
|
|
19
|
+
hint: z.string().nullable(),
|
|
20
|
+
recentReleaseCount: z.number(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/** GET/PUT /v1/admin/marketing-classifier response. */
|
|
24
|
+
export const MarketingClassifierStateSchema = z.object({
|
|
25
|
+
threshold: z.number(),
|
|
26
|
+
defaultThreshold: z.number(),
|
|
27
|
+
updatedAt: z.string().nullable(),
|
|
28
|
+
sources: z.array(MarketingFilteredSourceSchema),
|
|
29
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Admin interest-alert quality (`GET /v1/admin/semantic-alerts/summary`).
|
|
5
|
+
* Decisions live in the classifications Analytics Engine dataset with
|
|
6
|
+
* `blob4 = semantic-alert`. This is the hydrated read model. Alert query
|
|
7
|
+
* text is not a field — it is not stored on the points.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const ProbabilityBinSchema = z.object({
|
|
11
|
+
/** Inclusive. Width is 0.1. `0.8` is the start of the default-threshold bin. */
|
|
12
|
+
start: z.number(),
|
|
13
|
+
/** Exclusive, except the last bin (`0.9`–`1`) which includes `1`. */
|
|
14
|
+
end: z.number(),
|
|
15
|
+
count: z.number(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const SemanticAlertDispositionCountsSchema = z.object({
|
|
19
|
+
matched: z.number(),
|
|
20
|
+
belowThreshold: z.number(),
|
|
21
|
+
failed: z.number(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* `GET /v1/admin/semantic-alerts/summary`.
|
|
26
|
+
*
|
|
27
|
+
* `scored` is matched + below threshold (a probability came back).
|
|
28
|
+
* `matchRate` is matched / scored, or null when nothing was scored.
|
|
29
|
+
* `failed` is fail-closed and stays out of the rate.
|
|
30
|
+
* Cost is not on these points; `meta.costLane` names the `ai_usage` lane.
|
|
31
|
+
*/
|
|
32
|
+
export const SemanticAlertSummarySchema = z.object({
|
|
33
|
+
after: z.string(),
|
|
34
|
+
before: z.string(),
|
|
35
|
+
bucket: z.enum(["hour", "day"]),
|
|
36
|
+
totals: SemanticAlertDispositionCountsSchema.extend({
|
|
37
|
+
scored: z.number(),
|
|
38
|
+
matchRate: z.number().nullable(),
|
|
39
|
+
}),
|
|
40
|
+
series: z.array(
|
|
41
|
+
SemanticAlertDispositionCountsSchema.extend({
|
|
42
|
+
t: z.string(),
|
|
43
|
+
}),
|
|
44
|
+
),
|
|
45
|
+
failures: z.array(
|
|
46
|
+
z.object({
|
|
47
|
+
category: z.enum(["provider_error", "invalid_probability", "unknown"]),
|
|
48
|
+
count: z.number(),
|
|
49
|
+
}),
|
|
50
|
+
),
|
|
51
|
+
probability: z.object({
|
|
52
|
+
/** Default alert threshold. Each point stores its own threshold separately. */
|
|
53
|
+
defaultThreshold: z.literal(0.8),
|
|
54
|
+
bins: z.array(ProbabilityBinSchema),
|
|
55
|
+
/** Sampled rows whose probability double is `< 0` (the absent sentinel). */
|
|
56
|
+
missing: z.number(),
|
|
57
|
+
}),
|
|
58
|
+
meta: z.object({
|
|
59
|
+
dataset: z.string(),
|
|
60
|
+
retentionDays: z.literal(90),
|
|
61
|
+
sampled: z.literal(true),
|
|
62
|
+
/** `ai_usage` lane that carries JEV cost. Not summed from these points. */
|
|
63
|
+
costLane: z.literal("semantic-alert-match"),
|
|
64
|
+
}),
|
|
65
|
+
});
|