@intelligo-dev/executions 1.0.0-beta.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/LICENSE +201 -0
- package/dist/db/schema.d.ts +340 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +68 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle.d.ts +72 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +236 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/ports.d.ts +68 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +16 -0
- package/dist/ports.js.map +1 -0
- package/dist/pricing.d.ts +159 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +187 -0
- package/dist/pricing.js.map +1 -0
- package/dist/queries.d.ts +126 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +117 -0
- package/dist/queries.js.map +1 -0
- package/package.json +51 -0
package/dist/pricing.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model registry and execution cost accounting.
|
|
3
|
+
*
|
|
4
|
+
* This lives in `executions` rather than beside the provider clients
|
|
5
|
+
* because it is not AI code: it is what an execution costs. The
|
|
6
|
+
* boundary records actor, workspace, capability, usage and cost
|
|
7
|
+
* (ADR-0003), and the price of a token is the last of those.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately a leaf module — no database, no provider SDK, no
|
|
10
|
+
* imports at all — and reachable as `@intelligo-dev/executions/pricing`
|
|
11
|
+
* so a client bundle can read a display name or a price without
|
|
12
|
+
* pulling in Drizzle. `@intelligo-dev/ai` re-exports it, so the ids that
|
|
13
|
+
* pick a provider and the ids that carry a price stay one list; two
|
|
14
|
+
* lists is how a model runs on Gemini and bills at Claude rates.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Model configuration with display information and cost tracking
|
|
18
|
+
*
|
|
19
|
+
* 6 models, 3 providers: Google (2), OpenAI (3), Anthropic (1)
|
|
20
|
+
*/
|
|
21
|
+
export const MODEL_CONFIGS = {
|
|
22
|
+
// Google Gemini
|
|
23
|
+
"google/gemini-2.5-flash": {
|
|
24
|
+
provider: "google",
|
|
25
|
+
model: "gemini-2.5-flash",
|
|
26
|
+
displayName: "Gemini 2.5 Flash",
|
|
27
|
+
costPerMInputTokens: 0.3,
|
|
28
|
+
costPerMOutputTokens: 2.5,
|
|
29
|
+
capabilities: {
|
|
30
|
+
thinking: true,
|
|
31
|
+
toolCall: true,
|
|
32
|
+
vision: true,
|
|
33
|
+
webSearch: true,
|
|
34
|
+
codeExec: true,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
"google/gemini-2.5-pro": {
|
|
38
|
+
provider: "google",
|
|
39
|
+
model: "gemini-2.5-pro",
|
|
40
|
+
displayName: "Gemini 2.5 Pro",
|
|
41
|
+
costPerMInputTokens: 1.25,
|
|
42
|
+
costPerMOutputTokens: 10,
|
|
43
|
+
capabilities: {
|
|
44
|
+
thinking: true,
|
|
45
|
+
toolCall: true,
|
|
46
|
+
vision: true,
|
|
47
|
+
webSearch: true,
|
|
48
|
+
codeExec: true,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
// OpenAI
|
|
52
|
+
"openai/gpt-5-mini": {
|
|
53
|
+
provider: "openai",
|
|
54
|
+
model: "gpt-5-mini",
|
|
55
|
+
displayName: "GPT-5 Mini",
|
|
56
|
+
costPerMInputTokens: 0.25,
|
|
57
|
+
costPerMOutputTokens: 2.0,
|
|
58
|
+
capabilities: {
|
|
59
|
+
thinking: true,
|
|
60
|
+
toolCall: true,
|
|
61
|
+
vision: true,
|
|
62
|
+
webSearch: false,
|
|
63
|
+
codeExec: false,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
"openai/gpt-5.4-mini": {
|
|
67
|
+
provider: "openai",
|
|
68
|
+
model: "gpt-5.4-mini",
|
|
69
|
+
displayName: "GPT-5.4 Mini",
|
|
70
|
+
costPerMInputTokens: 0.75,
|
|
71
|
+
costPerMOutputTokens: 4.5,
|
|
72
|
+
capabilities: {
|
|
73
|
+
thinking: true,
|
|
74
|
+
toolCall: true,
|
|
75
|
+
vision: true,
|
|
76
|
+
webSearch: true,
|
|
77
|
+
codeExec: true,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
"openai/o4-mini": {
|
|
81
|
+
provider: "openai",
|
|
82
|
+
model: "o4-mini",
|
|
83
|
+
displayName: "o4-mini",
|
|
84
|
+
costPerMInputTokens: 1.1,
|
|
85
|
+
costPerMOutputTokens: 4.4,
|
|
86
|
+
capabilities: {
|
|
87
|
+
thinking: true,
|
|
88
|
+
toolCall: true,
|
|
89
|
+
vision: true,
|
|
90
|
+
webSearch: true,
|
|
91
|
+
codeExec: true,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
// Anthropic
|
|
95
|
+
"anthropic/claude-sonnet-4-6": {
|
|
96
|
+
provider: "anthropic",
|
|
97
|
+
model: "claude-sonnet-4-6-20260214",
|
|
98
|
+
displayName: "Claude Sonnet 4.6",
|
|
99
|
+
costPerMInputTokens: 3.0,
|
|
100
|
+
costPerMOutputTokens: 15.0,
|
|
101
|
+
capabilities: {
|
|
102
|
+
thinking: true,
|
|
103
|
+
toolCall: true,
|
|
104
|
+
vision: true,
|
|
105
|
+
webSearch: true,
|
|
106
|
+
codeExec: true,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Calculate the dollar cost for a given model and token counts.
|
|
112
|
+
*/
|
|
113
|
+
export function calculateCost(modelId, inputTokens, outputTokens) {
|
|
114
|
+
const config = MODEL_CONFIGS[modelId];
|
|
115
|
+
if (!config) {
|
|
116
|
+
// Loud fallback: an unknown model ID means somewhere upstream is
|
|
117
|
+
// shipping a stale string that doesn't match MODEL_CONFIGS. We
|
|
118
|
+
// price it at the highest known rate so we don't accidentally give
|
|
119
|
+
// away expensive completions for free, but the warning here is the
|
|
120
|
+
// contract — silent fallback is how we ended up over-billing every
|
|
121
|
+
// free Gemini turn at Claude prices.
|
|
122
|
+
console.warn(`[calculateCost] Unknown model "${modelId}" — pricing at worst-case Claude rate ($3/$15 per M). Add it to MODEL_CONFIGS.`);
|
|
123
|
+
return (inputTokens / 1000000) * 3.0 + (outputTokens / 1000000) * 15.0;
|
|
124
|
+
}
|
|
125
|
+
return ((inputTokens / 1000000) * config.costPerMInputTokens +
|
|
126
|
+
(outputTokens / 1000000) * config.costPerMOutputTokens);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Default billing margin multiplier applied on top of raw model cost.
|
|
130
|
+
* Covers infra, tool overhead, FX volatility, and profit. Tunable per
|
|
131
|
+
* deploy via billing_settings.margin_multiplier; this constant is the
|
|
132
|
+
* fallback when the DB value is absent.
|
|
133
|
+
*
|
|
134
|
+
* INVARIANT — 65% gross margin floor (founder contract):
|
|
135
|
+
* gross_margin = 1 − 1/multiplier
|
|
136
|
+
* We must keep gross_margin ≥ 0.65 across every model in MODEL_CONFIGS,
|
|
137
|
+
* which means multiplier ≥ 1/0.35 ≈ 2.857. The current value of 4
|
|
138
|
+
* yields 75%, leaving comfortable buffer above the floor for FX moves
|
|
139
|
+
* and unexpected provider price hikes. The Vitest assertion in
|
|
140
|
+
* models.test.ts pins this — drop below 2.857 at your own risk.
|
|
141
|
+
*/
|
|
142
|
+
export const DEFAULT_BILLING_MARGIN = 4;
|
|
143
|
+
/**
|
|
144
|
+
* Default USD→MNT exchange rate fallback. Real value lives in
|
|
145
|
+
* billing_settings.usd_to_mnt_rate and is read per request.
|
|
146
|
+
*/
|
|
147
|
+
export const DEFAULT_USD_TO_MNT_RATE = 3450;
|
|
148
|
+
/**
|
|
149
|
+
* Per-model maximum output budget used for worst-case pre-request cost
|
|
150
|
+
* estimation in checkQuota. Numbers are conservative — cap on streamed
|
|
151
|
+
* output tokens we'd actually let a single chat turn produce.
|
|
152
|
+
*/
|
|
153
|
+
export const MODEL_OUTPUT_BUDGET = {
|
|
154
|
+
"google/gemini-2.5-flash": 8000,
|
|
155
|
+
"google/gemini-2.5-pro": 8000,
|
|
156
|
+
"openai/gpt-5-mini": 8000,
|
|
157
|
+
"openai/gpt-5.4-mini": 8000,
|
|
158
|
+
"openai/o4-mini": 8000,
|
|
159
|
+
"anthropic/claude-sonnet-4-6": 8000,
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Convert raw model cost to MNT user-facing charged amount.
|
|
163
|
+
*
|
|
164
|
+
* chargedMnt = ceil( rawCostUsd × margin × fxRate )
|
|
165
|
+
*
|
|
166
|
+
* The rounding is upward so micro-fractions never let a free request
|
|
167
|
+
* slip through; over-billing per request is at most 1 MNT.
|
|
168
|
+
*/
|
|
169
|
+
export function calculateChargedMnt(modelId, inputTokens, outputTokens, fxRate = DEFAULT_USD_TO_MNT_RATE, margin = DEFAULT_BILLING_MARGIN) {
|
|
170
|
+
const rawCostUsd = calculateCost(modelId, inputTokens, outputTokens);
|
|
171
|
+
const chargedMnt = Math.ceil(rawCostUsd * margin * fxRate);
|
|
172
|
+
return { rawCostUsd, chargedMnt };
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Worst-case MNT cost estimate for a single chat turn against a given
|
|
176
|
+
* model. Used by checkQuota to refuse requests whose ceiling cost would
|
|
177
|
+
* exceed remaining balance, before we burn provider tokens. Uses the
|
|
178
|
+
* full MODEL_OUTPUT_BUDGET as the output side and a generous 16K input
|
|
179
|
+
* budget to cover system prompt + conversation history.
|
|
180
|
+
*/
|
|
181
|
+
export function estimateWorstCaseChargedMnt(modelId, fxRate = DEFAULT_USD_TO_MNT_RATE, margin = DEFAULT_BILLING_MARGIN) {
|
|
182
|
+
const outputBudget = MODEL_OUTPUT_BUDGET[modelId] ?? 8000;
|
|
183
|
+
const inputBudget = 16000;
|
|
184
|
+
return calculateChargedMnt(modelId, inputBudget, outputBudget, fxRate, margin)
|
|
185
|
+
.chargedMnt;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read models over the executions table — for the product's usage UI
|
|
3
|
+
* and, in Phase 5, the Intelligo admin console.
|
|
4
|
+
*/
|
|
5
|
+
export type ListExecutionsOptions = {
|
|
6
|
+
workspaceId?: string;
|
|
7
|
+
userId?: string;
|
|
8
|
+
capability?: string;
|
|
9
|
+
status?: "running" | "settling" | "succeeded" | "failed" | "refused";
|
|
10
|
+
/** Keyset pagination: rows started strictly before this. */
|
|
11
|
+
before?: Date;
|
|
12
|
+
limit?: number;
|
|
13
|
+
};
|
|
14
|
+
export declare function listExecutions(options?: ListExecutionsOptions): Promise<{
|
|
15
|
+
id: string;
|
|
16
|
+
workspaceId: string;
|
|
17
|
+
userId: string | null;
|
|
18
|
+
capability: string;
|
|
19
|
+
requestId: string;
|
|
20
|
+
status: string;
|
|
21
|
+
model: string | null;
|
|
22
|
+
inputTokens: number | null;
|
|
23
|
+
outputTokens: number | null;
|
|
24
|
+
totalTokens: number | null;
|
|
25
|
+
chargedMnt: number | null;
|
|
26
|
+
reservedMnt: number | null;
|
|
27
|
+
refusalReason: string | null;
|
|
28
|
+
errorMessage: string | null;
|
|
29
|
+
startedAt: Date;
|
|
30
|
+
finishedAt: Date | null;
|
|
31
|
+
durationMs: number | null;
|
|
32
|
+
metadata: Record<string, unknown> | null;
|
|
33
|
+
}[]>;
|
|
34
|
+
export declare function getExecutionByRequestId(requestId: string): Promise<{
|
|
35
|
+
id: string;
|
|
36
|
+
workspaceId: string;
|
|
37
|
+
userId: string | null;
|
|
38
|
+
capability: string;
|
|
39
|
+
requestId: string;
|
|
40
|
+
status: string;
|
|
41
|
+
model: string | null;
|
|
42
|
+
inputTokens: number | null;
|
|
43
|
+
outputTokens: number | null;
|
|
44
|
+
totalTokens: number | null;
|
|
45
|
+
chargedMnt: number | null;
|
|
46
|
+
reservedMnt: number | null;
|
|
47
|
+
refusalReason: string | null;
|
|
48
|
+
errorMessage: string | null;
|
|
49
|
+
startedAt: Date;
|
|
50
|
+
finishedAt: Date | null;
|
|
51
|
+
durationMs: number | null;
|
|
52
|
+
metadata: Record<string, unknown> | null;
|
|
53
|
+
} | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Aggregate counts, tokens, and charge for a workspace over a window.
|
|
56
|
+
* Refused executions are counted separately — they consumed no tokens
|
|
57
|
+
* but they are the signal that a plan's limits are biting.
|
|
58
|
+
*/
|
|
59
|
+
export declare function summarizeExecutions(workspaceId: string, window: {
|
|
60
|
+
from: Date;
|
|
61
|
+
to: Date;
|
|
62
|
+
}): Promise<{
|
|
63
|
+
byStatus: {
|
|
64
|
+
[k: string]: {
|
|
65
|
+
count: number;
|
|
66
|
+
totalTokens: number;
|
|
67
|
+
chargedMnt: number;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
totals: {
|
|
71
|
+
count: number;
|
|
72
|
+
totalTokens: number;
|
|
73
|
+
chargedMnt: number;
|
|
74
|
+
};
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* Per-day totals for a workspace over a window — the read model behind
|
|
78
|
+
* a usage chart.
|
|
79
|
+
*
|
|
80
|
+
* Aggregated in SQL rather than by bucketing rows in the application:
|
|
81
|
+
* a month of a busy workspace is thousands of rows to ship over the
|
|
82
|
+
* wire to produce thirty numbers, and any consumer that had to do that
|
|
83
|
+
* bucketing itself would be writing the query this package should own.
|
|
84
|
+
*
|
|
85
|
+
* Days with no activity are absent rather than zero — the caller knows
|
|
86
|
+
* the window it asked for, and a gap means "nothing ran", which a
|
|
87
|
+
* chart should draw as zero and a table should leave empty. `date` is
|
|
88
|
+
* a `YYYY-MM-DD` string in UTC, so the series is stable regardless of
|
|
89
|
+
* where the reader is.
|
|
90
|
+
*/
|
|
91
|
+
export declare function summarizeExecutionsByDay(workspaceId: string, window: {
|
|
92
|
+
from: Date;
|
|
93
|
+
to: Date;
|
|
94
|
+
}): Promise<Array<{
|
|
95
|
+
date: string;
|
|
96
|
+
count: number;
|
|
97
|
+
totalTokens: number;
|
|
98
|
+
chargedMnt: number;
|
|
99
|
+
}>>;
|
|
100
|
+
/**
|
|
101
|
+
* Executions stuck in a non-terminal state past a cutoff — a stream
|
|
102
|
+
* that died without reaching complete()/fail() (`running`), or a
|
|
103
|
+
* settlement that threw partway (`settling`). The cleanup cron reports
|
|
104
|
+
* these; they are the operational signal that usage went unrecorded.
|
|
105
|
+
*/
|
|
106
|
+
export declare function findStaleExecutions(olderThan: Date, limit?: number): Promise<{
|
|
107
|
+
id: string;
|
|
108
|
+
workspaceId: string;
|
|
109
|
+
userId: string | null;
|
|
110
|
+
capability: string;
|
|
111
|
+
requestId: string;
|
|
112
|
+
status: string;
|
|
113
|
+
model: string | null;
|
|
114
|
+
inputTokens: number | null;
|
|
115
|
+
outputTokens: number | null;
|
|
116
|
+
totalTokens: number | null;
|
|
117
|
+
chargedMnt: number | null;
|
|
118
|
+
reservedMnt: number | null;
|
|
119
|
+
refusalReason: string | null;
|
|
120
|
+
errorMessage: string | null;
|
|
121
|
+
startedAt: Date;
|
|
122
|
+
finishedAt: Date | null;
|
|
123
|
+
durationMs: number | null;
|
|
124
|
+
metadata: Record<string, unknown> | null;
|
|
125
|
+
}[]>;
|
|
126
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IACrE,4DAA4D;IAC5D,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,cAAc,CAAC,OAAO,GAAE,qBAA0B;;;;;;;;;;;;;;;;;;;KAmBvE;AAED,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;UAO9D;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE;;;;;;;;;;;;;GAyCjC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE,GAC/B,OAAO,CACR,KAAK,CAAC;IACJ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CACH,CA2BA;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,SAAM;;;;;;;;;;;;;;;;;;;KAYrE"}
|
package/dist/queries.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read models over the executions table — for the product's usage UI
|
|
3
|
+
* and, in Phase 5, the Intelligo admin console.
|
|
4
|
+
*/
|
|
5
|
+
import { db } from "@intelligo-dev/core/db";
|
|
6
|
+
import { and, desc, eq, gte, inArray, lt, lte, sql } from "drizzle-orm";
|
|
7
|
+
import { executions } from "./db/schema";
|
|
8
|
+
export async function listExecutions(options = {}) {
|
|
9
|
+
const filters = [
|
|
10
|
+
options.workspaceId
|
|
11
|
+
? eq(executions.workspaceId, options.workspaceId)
|
|
12
|
+
: undefined,
|
|
13
|
+
options.userId ? eq(executions.userId, options.userId) : undefined,
|
|
14
|
+
options.capability
|
|
15
|
+
? eq(executions.capability, options.capability)
|
|
16
|
+
: undefined,
|
|
17
|
+
options.status ? eq(executions.status, options.status) : undefined,
|
|
18
|
+
options.before ? lt(executions.startedAt, options.before) : undefined,
|
|
19
|
+
].filter(Boolean);
|
|
20
|
+
return db
|
|
21
|
+
.select()
|
|
22
|
+
.from(executions)
|
|
23
|
+
.where(filters.length > 0 ? and(...filters) : undefined)
|
|
24
|
+
.orderBy(desc(executions.startedAt))
|
|
25
|
+
.limit(Math.min(options.limit ?? 50, 500));
|
|
26
|
+
}
|
|
27
|
+
export async function getExecutionByRequestId(requestId) {
|
|
28
|
+
const [row] = await db
|
|
29
|
+
.select()
|
|
30
|
+
.from(executions)
|
|
31
|
+
.where(eq(executions.requestId, requestId))
|
|
32
|
+
.limit(1);
|
|
33
|
+
return row ?? null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Aggregate counts, tokens, and charge for a workspace over a window.
|
|
37
|
+
* Refused executions are counted separately — they consumed no tokens
|
|
38
|
+
* but they are the signal that a plan's limits are biting.
|
|
39
|
+
*/
|
|
40
|
+
export async function summarizeExecutions(workspaceId, window) {
|
|
41
|
+
const rows = await db
|
|
42
|
+
.select({
|
|
43
|
+
status: executions.status,
|
|
44
|
+
count: sql `count(*)`,
|
|
45
|
+
totalTokens: sql `coalesce(sum(${executions.totalTokens}), 0)`,
|
|
46
|
+
chargedMnt: sql `coalesce(sum(${executions.chargedMnt}), 0)`,
|
|
47
|
+
})
|
|
48
|
+
.from(executions)
|
|
49
|
+
.where(and(eq(executions.workspaceId, workspaceId), gte(executions.startedAt, window.from), lte(executions.startedAt, window.to)))
|
|
50
|
+
.groupBy(executions.status);
|
|
51
|
+
const byStatus = Object.fromEntries(rows.map((r) => [
|
|
52
|
+
r.status,
|
|
53
|
+
{
|
|
54
|
+
count: Number(r.count),
|
|
55
|
+
totalTokens: Number(r.totalTokens),
|
|
56
|
+
chargedMnt: Number(r.chargedMnt),
|
|
57
|
+
},
|
|
58
|
+
]));
|
|
59
|
+
return {
|
|
60
|
+
byStatus,
|
|
61
|
+
totals: rows.reduce((acc, r) => ({
|
|
62
|
+
count: acc.count + Number(r.count),
|
|
63
|
+
totalTokens: acc.totalTokens + Number(r.totalTokens),
|
|
64
|
+
chargedMnt: acc.chargedMnt + Number(r.chargedMnt),
|
|
65
|
+
}), { count: 0, totalTokens: 0, chargedMnt: 0 }),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Per-day totals for a workspace over a window — the read model behind
|
|
70
|
+
* a usage chart.
|
|
71
|
+
*
|
|
72
|
+
* Aggregated in SQL rather than by bucketing rows in the application:
|
|
73
|
+
* a month of a busy workspace is thousands of rows to ship over the
|
|
74
|
+
* wire to produce thirty numbers, and any consumer that had to do that
|
|
75
|
+
* bucketing itself would be writing the query this package should own.
|
|
76
|
+
*
|
|
77
|
+
* Days with no activity are absent rather than zero — the caller knows
|
|
78
|
+
* the window it asked for, and a gap means "nothing ran", which a
|
|
79
|
+
* chart should draw as zero and a table should leave empty. `date` is
|
|
80
|
+
* a `YYYY-MM-DD` string in UTC, so the series is stable regardless of
|
|
81
|
+
* where the reader is.
|
|
82
|
+
*/
|
|
83
|
+
export async function summarizeExecutionsByDay(workspaceId, window) {
|
|
84
|
+
const day = sql `to_char(${executions.startedAt} AT TIME ZONE 'UTC', 'YYYY-MM-DD')`;
|
|
85
|
+
const rows = await db
|
|
86
|
+
.select({
|
|
87
|
+
date: day,
|
|
88
|
+
count: sql `count(*)`,
|
|
89
|
+
totalTokens: sql `coalesce(sum(${executions.totalTokens}), 0)`,
|
|
90
|
+
chargedMnt: sql `coalesce(sum(${executions.chargedMnt}), 0)`,
|
|
91
|
+
})
|
|
92
|
+
.from(executions)
|
|
93
|
+
.where(and(eq(executions.workspaceId, workspaceId), gte(executions.startedAt, window.from), lte(executions.startedAt, window.to)))
|
|
94
|
+
.groupBy(day)
|
|
95
|
+
.orderBy(day);
|
|
96
|
+
return rows.map((row) => ({
|
|
97
|
+
date: row.date,
|
|
98
|
+
count: Number(row.count),
|
|
99
|
+
totalTokens: Number(row.totalTokens),
|
|
100
|
+
chargedMnt: Number(row.chargedMnt),
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Executions stuck in a non-terminal state past a cutoff — a stream
|
|
105
|
+
* that died without reaching complete()/fail() (`running`), or a
|
|
106
|
+
* settlement that threw partway (`settling`). The cleanup cron reports
|
|
107
|
+
* these; they are the operational signal that usage went unrecorded.
|
|
108
|
+
*/
|
|
109
|
+
export async function findStaleExecutions(olderThan, limit = 100) {
|
|
110
|
+
return db
|
|
111
|
+
.select()
|
|
112
|
+
.from(executions)
|
|
113
|
+
.where(and(inArray(executions.status, ["running", "settling"]), lt(executions.startedAt, olderThan)))
|
|
114
|
+
.orderBy(desc(executions.startedAt))
|
|
115
|
+
.limit(limit);
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@intelligo-dev/executions",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/intelligo-mn/framework.git",
|
|
8
|
+
"directory": "packages/executions"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/intelligo-mn/framework#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/intelligo-mn/framework/issues"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./pricing": {
|
|
21
|
+
"types": "./dist/pricing.d.ts",
|
|
22
|
+
"default": "./dist/pricing.js"
|
|
23
|
+
},
|
|
24
|
+
"./db": {
|
|
25
|
+
"types": "./dist/db/schema.d.ts",
|
|
26
|
+
"default": "./dist/db/schema.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"drizzle-orm": "^0.45.1",
|
|
31
|
+
"@intelligo-dev/audit": "1.0.0-beta.1",
|
|
32
|
+
"@intelligo-dev/core": "1.0.0-beta.1"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^22.0.0",
|
|
36
|
+
"@types/pg": "^8.16.0",
|
|
37
|
+
"pg": "^8.18.0",
|
|
38
|
+
"typescript": "^5.7.2"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"type-check": "tsc --noEmit",
|
|
48
|
+
"lint": "eslint .",
|
|
49
|
+
"build": "tsc -p tsconfig.build.json"
|
|
50
|
+
}
|
|
51
|
+
}
|