@sixb/pg 0.1.6 → 0.1.7
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/agents/rows.d.ts +8 -5
- package/dist/agents/rows.d.ts.map +1 -1
- package/dist/agents/runs.d.ts +3 -2
- package/dist/agents/runs.d.ts.map +1 -1
- package/dist/agents/threads.d.ts.map +1 -1
- package/dist/ai-cost-groups.d.ts +5 -0
- package/dist/ai-cost-groups.d.ts.map +1 -0
- package/dist/auth-storage/auth-storage.d.ts +4 -1
- package/dist/auth-storage/auth-storage.d.ts.map +1 -1
- package/dist/auth-storage/device-authorizations.d.ts +29 -0
- package/dist/auth-storage/device-authorizations.d.ts.map +1 -0
- package/dist/auth-storage/rows.d.ts +19 -1
- package/dist/auth-storage/rows.d.ts.map +1 -1
- package/dist/auth-storage/shared.d.ts +1 -1
- package/dist/auth-storage/shared.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1721 -393
- package/dist/index.js.map +17 -14
- package/dist/migrations.d.ts.map +1 -1
- package/dist/pg-ai-cost-storage.d.ts +2 -1
- package/dist/pg-ai-cost-storage.d.ts.map +1 -1
- package/dist/pg-ai-limit-storage.d.ts +34 -0
- package/dist/pg-ai-limit-storage.d.ts.map +1 -0
- package/dist/pg-ai-usage-storage.d.ts +28 -0
- package/dist/pg-ai-usage-storage.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/agents/rows.ts +38 -9
- package/src/agents/runs.ts +148 -11
- package/src/agents/threads.ts +1 -17
- package/src/ai-cost-groups.ts +102 -0
- package/src/auth-storage/auth-storage.ts +84 -0
- package/src/auth-storage/device-authorizations.ts +131 -0
- package/src/auth-storage/rows.ts +41 -0
- package/src/auth-storage/shared.ts +1 -0
- package/src/index.ts +8 -1
- package/src/migrations/029-model-accounting.sql +24 -0
- package/src/migrations/030-ai-usage-limits.sql +94 -0
- package/src/migrations/031-subagent-runs.sql +97 -0
- package/src/migrations/032-conversation-run-spec.sql +23 -0
- package/src/migrations/033-retire-agent-definitions.sql +28 -0
- package/src/migrations/034-device-authorizations.sql +24 -0
- package/src/migrations.ts +18 -0
- package/src/pg-ai-cost-storage.ts +71 -24
- package/src/pg-ai-limit-storage.ts +901 -0
- package/src/pg-ai-usage-storage.ts +34 -0
- package/src/pg-workflow-run-storage.ts +8 -8
package/dist/migrations.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAGV,YAAY,EACZ,aAAa,EACb,oBAAoB,EACrB,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAGV,YAAY,EACZ,aAAa,EACb,oBAAoB,EACrB,MAAM,oBAAoB,CAAA;AA+E3B,OAAO,KAAK,EAAE,GAAG,EAAa,MAAM,aAAa,CAAA;AAEjD,MAAM,WAAW,wBAAwB;IACvC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrC;AAED,eAAO,MAAM,2BAA2B,wBAAwB,CAAA;AAEhE,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,wBAAwB,CAAC,CAE1F;AAED,wBAAgB,MAAM,CACpB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAC/D,OAAO,GAAE,oBAAyB,GACjC,aAAa,CAAC,wBAAwB,CAAC,CAEzC;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,GACjB,SAAS,eAAe,EAAE,CAQ5B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;IACjB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,CAAA;CAC5D,GAAG,eAAe,CAqBlB;AAED,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxF;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAKrD;AA6KD,eAAO,MAAM,yBAAyB,wCAsCpC,CAAA"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { type AiAccountingOverview, type AiCostStorage, type AiCostSummary, type AiModelCallCostRecord, type ListAiModelCallAccountingInput, type ListAiModelCallAccountingResult, type QueryAiAccountingOverviewInput, type SummarizeAiCostExecutionsInput } from "@sixb/core/storage";
|
|
1
|
+
import { type AiAccountingOverview, type AiCostStorage, type AiCostSummary, type AiModelCallCostRecord, type ListAiModelCallAccountingInput, type ListAiModelCallAccountingResult, type ListAiModelCallGroupsInput, type ListAiModelCallGroupsResult, type QueryAiAccountingOverviewInput, type SummarizeAiCostExecutionsInput } from "@sixb/core/storage";
|
|
2
2
|
import type { PgStoreClient } from "./transactions";
|
|
3
3
|
/** PostgreSQL-backed immutable valuations stored in one append-only table. */
|
|
4
4
|
export declare class PgAiCostStorage implements AiCostStorage {
|
|
5
5
|
private readonly sql;
|
|
6
6
|
constructor(sql: PgStoreClient);
|
|
7
|
+
listModelCallGroups(input: ListAiModelCallGroupsInput): Promise<ListAiModelCallGroupsResult>;
|
|
7
8
|
recordModelCallCost(input: AiModelCallCostRecord): Promise<void>;
|
|
8
9
|
summarizeExecutions(input: SummarizeAiCostExecutionsInput): Promise<readonly AiCostSummary[]>;
|
|
9
10
|
queryProjectOverview(input: QueryAiAccountingOverviewInput): Promise<AiAccountingOverview>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-ai-cost-storage.d.ts","sourceRoot":"","sources":["../src/pg-ai-cost-storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pg-ai-cost-storage.d.ts","sourceRoot":"","sources":["../src/pg-ai-cost-storage.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAElB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAE1B,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACpC,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,8BAA8B,EACnC,KAAK,8BAA8B,EACpC,MAAM,oBAAoB,CAAA;AAG3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAGnD,8EAA8E;AAC9E,qBAAa,eAAgB,YAAW,aAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,aAAa;IAEzC,mBAAmB,CACvB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,2BAA2B,CAAC;IAIjC,mBAAmB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BhE,mBAAmB,CACvB,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC;IAsB9B,oBAAoB,CAAC,KAAK,EAAE,8BAA8B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAoH1F,cAAc,CAClB,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,+BAA+B,CAAC;CAwF5C"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AiLimitPolicy, AiLimitPolicyStatus, AiLimitStorage, AiModelCallReservation, CreateAiLimitPolicyInput, DeleteAiLimitPolicyInput, GetAiLimitPolicyInput, ListAiLimitPoliciesInput, ListAiLimitPolicyStatusesInput, MarkAiModelCallReservationUnknownInput, ReconcileAiModelCallInput, RecordAiModelCallLimitActualsInput, ReserveAiModelCallInput, ReserveAiModelCallResult, UpdateAiLimitPolicyInput } from "@sixb/core/storage";
|
|
2
|
+
import { type PgStoreClient } from "./transactions";
|
|
3
|
+
/** PostgreSQL-backed editable AI limits and atomic model-call reservations. */
|
|
4
|
+
export declare class PgAiLimitStorage implements AiLimitStorage {
|
|
5
|
+
private readonly sql;
|
|
6
|
+
private readonly operations;
|
|
7
|
+
constructor(sql: PgStoreClient);
|
|
8
|
+
/** @internal Keep queued work inside the lifetime of the owning SQL transaction. */
|
|
9
|
+
settleOperations(): Promise<void>;
|
|
10
|
+
createPolicy(input: CreateAiLimitPolicyInput): Promise<AiLimitPolicy>;
|
|
11
|
+
updatePolicy(input: UpdateAiLimitPolicyInput): Promise<AiLimitPolicy>;
|
|
12
|
+
deletePolicy(input: DeleteAiLimitPolicyInput): Promise<boolean>;
|
|
13
|
+
getPolicy(input: GetAiLimitPolicyInput): Promise<AiLimitPolicy | null>;
|
|
14
|
+
listPolicies(input: ListAiLimitPoliciesInput): Promise<readonly AiLimitPolicy[]>;
|
|
15
|
+
listPolicyStatuses(input: ListAiLimitPolicyStatusesInput): Promise<readonly AiLimitPolicyStatus[]>;
|
|
16
|
+
reserveModelCall(input: ReserveAiModelCallInput): Promise<ReserveAiModelCallResult>;
|
|
17
|
+
recordModelCallActuals(input: RecordAiModelCallLimitActualsInput): Promise<void>;
|
|
18
|
+
reconcileModelCall(input: ReconcileAiModelCallInput): Promise<AiModelCallReservation>;
|
|
19
|
+
markReservationUnknown(input: MarkAiModelCallReservationUnknownInput): Promise<AiModelCallReservation>;
|
|
20
|
+
private withProjectLock;
|
|
21
|
+
private policyRows;
|
|
22
|
+
private requirePolicy;
|
|
23
|
+
private statusForPolicy;
|
|
24
|
+
private changeCounters;
|
|
25
|
+
private moveEstimateCounters;
|
|
26
|
+
private reconcileEstimateCounters;
|
|
27
|
+
private insertReservation;
|
|
28
|
+
private updateReservation;
|
|
29
|
+
private findReservation;
|
|
30
|
+
private accountingEntries;
|
|
31
|
+
private requireAccountingEntry;
|
|
32
|
+
private assertExecutionExists;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=pg-ai-limit-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg-ai-limit-storage.d.ts","sourceRoot":"","sources":["../src/pg-ai-limit-storage.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAEV,aAAa,EACb,mBAAmB,EAGnB,cAAc,EAEd,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,8BAA8B,EAC9B,sCAAsC,EACtC,yBAAyB,EACzB,kCAAkC,EAClC,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAoB,KAAK,aAAa,EAAoB,MAAM,gBAAgB,CAAA;AAEvF,+EAA+E;AAC/E,qBAAa,gBAAiB,YAAW,cAAc;IAGzC,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFhC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;gBAE3B,GAAG,EAAE,aAAa;IAE/C,oFAAoF;IACpF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,YAAY,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,aAAa,CAAC;IAsBrE,YAAY,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,aAAa,CAAC;IAiBrE,YAAY,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,OAAO,CAAC;IAY/D,SAAS,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAStE,YAAY,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC;IAOhF,kBAAkB,CACtB,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,SAAS,mBAAmB,EAAE,CAAC;IAapC,gBAAgB,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAqGnF,sBAAsB,CAAC,KAAK,EAAE,kCAAkC,GAAG,OAAO,CAAC,IAAI,CAAC;IA0ChF,kBAAkB,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA+BrF,sBAAsB,CAC1B,KAAK,EAAE,sCAAsC,GAC5C,OAAO,CAAC,sBAAsB,CAAC;YAepB,eAAe;IAe7B,OAAO,CAAC,UAAU;YAYJ,aAAa;YAeb,eAAe;YA2Cf,cAAc;YA8Cd,oBAAoB;YA2BpB,yBAAyB;YAmCzB,iBAAiB;YAkBjB,iBAAiB;YAgBjB,eAAe;YAmBf,iBAAiB;YA8CjB,sBAAsB;YAwCtB,qBAAqB;CAgBpC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ReadonlyJsonObject } from "@sixb/core/storage";
|
|
1
2
|
import { type AiModelCallUsageRecord, type AiUsageExecutionSummary, type AiUsageStorage, type GetLatestAiModelCallForExecutionInput, type RecordAiModelCallInput, type RecordAiModelCallResult, type SummarizeAiUsageExecutionInput, type SummarizeAiUsageExecutionsInput } from "@sixb/core/storage";
|
|
2
3
|
import { type PgStoreClient } from "./transactions";
|
|
3
4
|
/** PostgreSQL-backed append-only model-call usage ledger. */
|
|
@@ -14,4 +15,31 @@ export declare class PgAiUsageStorage implements AiUsageStorage {
|
|
|
14
15
|
private executionRows;
|
|
15
16
|
private assertExecutionExists;
|
|
16
17
|
}
|
|
18
|
+
interface AiUsageRow {
|
|
19
|
+
readonly project_id: string;
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly execution_id: string;
|
|
22
|
+
readonly attempt: number | string;
|
|
23
|
+
readonly call_id: string;
|
|
24
|
+
readonly provider_id: string;
|
|
25
|
+
readonly provider_ids: Record<string, unknown> | string | null;
|
|
26
|
+
readonly requested_model_id: string;
|
|
27
|
+
readonly requested_reasoning: string | Record<string, unknown> | null;
|
|
28
|
+
readonly response_model_id: string | null;
|
|
29
|
+
readonly response_id: string;
|
|
30
|
+
readonly input_tokens: number | string | null;
|
|
31
|
+
readonly output_tokens: number | string | null;
|
|
32
|
+
readonly total_tokens: number | string | null;
|
|
33
|
+
readonly uncached_input_tokens: number | string | null;
|
|
34
|
+
readonly cache_read_input_tokens: number | string | null;
|
|
35
|
+
readonly cache_write_input_tokens: number | string | null;
|
|
36
|
+
readonly text_output_tokens: number | string | null;
|
|
37
|
+
readonly reasoning_output_tokens: number | string | null;
|
|
38
|
+
readonly reporting_status: AiModelCallUsageRecord["usage"]["reportingStatus"];
|
|
39
|
+
readonly raw_usage: ReadonlyJsonObject | string | null;
|
|
40
|
+
readonly occurred_at: Date | string;
|
|
41
|
+
readonly recorded_at: Date | string;
|
|
42
|
+
}
|
|
43
|
+
export declare function requestedReasoningFromRow(value: Exclude<AiUsageRow["requested_reasoning"], null>): AiModelCallUsageRecord["requestedReasoning"];
|
|
44
|
+
export {};
|
|
17
45
|
//# sourceMappingURL=pg-ai-usage-storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-ai-usage-storage.d.ts","sourceRoot":"","sources":["../src/pg-ai-usage-storage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EAInB,KAAK,qCAAqC,EAE1C,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACrC,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,KAAK,aAAa,EAAoB,MAAM,gBAAgB,CAAA;AAErE,6DAA6D;AAC7D,qBAAa,gBAAiB,YAAW,cAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,aAAa;IAEzC,eAAe,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"pg-ai-usage-storage.d.ts","sourceRoot":"","sources":["../src/pg-ai-usage-storage.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sixb/pg",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "PostgreSQL storage provider for Sixb",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sixb",
|
|
@@ -33,17 +33,17 @@
|
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "bun ../../scripts/build-package.ts",
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
|
-
"test:e2e": "bun test --preload ./tests/setup.ts ./tests/*.e2e.ts",
|
|
36
|
+
"test:e2e": "bun ../../scripts/ci-guard.ts --stall 90 --max 600 bun test --preload ./tests/setup.ts ./tests/*.e2e.ts",
|
|
37
37
|
"prepack": "bun run build"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"postgres": "^3.4.5"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@sixb/core": "0.1.
|
|
43
|
+
"@sixb/core": "0.1.7"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@sixb/core": "0.1.
|
|
46
|
+
"@sixb/core": "0.1.7",
|
|
47
47
|
"@types/bun": "latest",
|
|
48
48
|
"typescript": "^5.7.0"
|
|
49
49
|
},
|
package/src/agents/rows.ts
CHANGED
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
type AgentRunDiagnostic,
|
|
8
8
|
type AgentRunRecord,
|
|
9
9
|
type AgentThreadRecord,
|
|
10
|
+
type ConversationAgentRunSpec,
|
|
10
11
|
coerceAgentRunFinishReason,
|
|
12
|
+
type SubagentRunResult,
|
|
13
|
+
type SubagentRunSpec,
|
|
11
14
|
} from "@sixb/core/storage"
|
|
12
15
|
import type { SQLClient, SqlParameter } from "../pg-client"
|
|
13
16
|
|
|
@@ -17,7 +20,6 @@ import type { SQLClient, SqlParameter } from "../pg-client"
|
|
|
17
20
|
export interface AgentThreadRow {
|
|
18
21
|
project_id: string
|
|
19
22
|
id: string
|
|
20
|
-
agent_id: string
|
|
21
23
|
owner_principal_type: Principal["type"]
|
|
22
24
|
owner_principal_id: string
|
|
23
25
|
title: string | null
|
|
@@ -33,9 +35,13 @@ export interface AgentRunRow {
|
|
|
33
35
|
project_id: string
|
|
34
36
|
id: string
|
|
35
37
|
execution_id: string
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
trigger_message_id: string
|
|
38
|
+
kind: AgentRunRecord["kind"]
|
|
39
|
+
thread_id: string | null
|
|
40
|
+
trigger_message_id: string | null
|
|
41
|
+
parent_run_id: string | null
|
|
42
|
+
spawn_key: string | null
|
|
43
|
+
spec: ConversationAgentRunSpec | SubagentRunSpec | string | null
|
|
44
|
+
result: SubagentRunResult | string | null
|
|
39
45
|
requester_group_ids: string[] | string
|
|
40
46
|
status: AgentRunRecord["status"]
|
|
41
47
|
model_id: string | null
|
|
@@ -89,7 +95,6 @@ export function rowToThreadRecord(row: AgentThreadRow): AgentThreadRecord {
|
|
|
89
95
|
return {
|
|
90
96
|
id: row.id,
|
|
91
97
|
projectId: row.project_id,
|
|
92
|
-
agentId: row.agent_id,
|
|
93
98
|
ownerPrincipal: { type: row.owner_principal_type, id: row.owner_principal_id },
|
|
94
99
|
title: row.title ?? undefined,
|
|
95
100
|
status: row.status,
|
|
@@ -102,13 +107,10 @@ export function rowToThreadRecord(row: AgentThreadRow): AgentThreadRecord {
|
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
export function rowToRunRecord(row: AgentRunRow): AgentRunRecord {
|
|
105
|
-
|
|
110
|
+
const base = {
|
|
106
111
|
id: row.id,
|
|
107
112
|
projectId: row.project_id,
|
|
108
113
|
executionId: row.execution_id,
|
|
109
|
-
threadId: row.thread_id,
|
|
110
|
-
agentId: row.agent_id,
|
|
111
|
-
triggerMessageId: row.trigger_message_id,
|
|
112
114
|
requesterGroupIds:
|
|
113
115
|
typeof row.requester_group_ids === "string"
|
|
114
116
|
? (JSON.parse(row.requester_group_ids) as string[])
|
|
@@ -130,6 +132,29 @@ export function rowToRunRecord(row: AgentRunRow): AgentRunRecord {
|
|
|
130
132
|
startedAt: row.started_at ? new Date(row.started_at) : undefined,
|
|
131
133
|
completedAt: row.completed_at ? new Date(row.completed_at) : undefined,
|
|
132
134
|
}
|
|
135
|
+
if (row.kind === "conversation") {
|
|
136
|
+
if (!row.thread_id || !row.trigger_message_id) {
|
|
137
|
+
throw new Error(`[SixbPg] Conversational Agent run '${row.id}' has invalid persisted fields.`)
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
...base,
|
|
141
|
+
kind: "conversation",
|
|
142
|
+
threadId: row.thread_id,
|
|
143
|
+
triggerMessageId: row.trigger_message_id,
|
|
144
|
+
...(row.spec === null ? {} : { spec: parseJsonColumn<ConversationAgentRunSpec>(row.spec) }),
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!row.parent_run_id || !row.spawn_key || row.spec === null) {
|
|
148
|
+
throw new Error(`[SixbPg] Subagent run '${row.id}' has invalid persisted fields.`)
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
...base,
|
|
152
|
+
kind: "subagent",
|
|
153
|
+
parentRunId: row.parent_run_id,
|
|
154
|
+
spawnKey: row.spawn_key,
|
|
155
|
+
spec: parseJsonColumn<SubagentRunSpec>(row.spec),
|
|
156
|
+
...(row.result === null ? {} : { result: parseJsonColumn<SubagentRunResult>(row.result) }),
|
|
157
|
+
}
|
|
133
158
|
}
|
|
134
159
|
|
|
135
160
|
export function rowToMessageRecord(row: AgentMessageRow): AgentMessageRecord {
|
|
@@ -203,6 +228,10 @@ function normalizeDiagnostics(
|
|
|
203
228
|
return typeof value === "string" ? (JSON.parse(value) as AgentRunDiagnostic[]) : value
|
|
204
229
|
}
|
|
205
230
|
|
|
231
|
+
function parseJsonColumn<T>(value: unknown): T {
|
|
232
|
+
return typeof value === "string" ? (JSON.parse(value) as T) : (value as T)
|
|
233
|
+
}
|
|
234
|
+
|
|
206
235
|
// ── offset pagination (threads / messages order by their own column, not started_at) ─────────────
|
|
207
236
|
|
|
208
237
|
export async function queryAgentList<TRow>(input: {
|
package/src/agents/runs.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
assertAgentRunExecution,
|
|
3
|
+
assertConversationAgentRunSpec,
|
|
4
|
+
assertCreateSubagentRunInput,
|
|
5
|
+
assertSubagentRunResult,
|
|
6
|
+
subagentRunMatchesCreateInput,
|
|
7
|
+
} from "@sixb/core/internal/agent-run-storage-provider"
|
|
2
8
|
import { normalizeRequesterGroupIds } from "@sixb/core/internal/auth"
|
|
3
9
|
import { serializeSixbFailure } from "@sixb/core/internal/errors"
|
|
4
10
|
import {
|
|
@@ -7,7 +13,9 @@ import {
|
|
|
7
13
|
type AgentRunStore,
|
|
8
14
|
AgentStorageError,
|
|
9
15
|
type ConfirmAgentRunExecutionOwnershipInput,
|
|
16
|
+
type ConversationAgentRunRecord,
|
|
10
17
|
type CreateAgentRunInput,
|
|
18
|
+
type CreateSubagentRunInput,
|
|
11
19
|
type ExecutionStorage,
|
|
12
20
|
type FinishAgentRunInput,
|
|
13
21
|
type FinishQueuedAgentRunInput,
|
|
@@ -15,6 +23,7 @@ import {
|
|
|
15
23
|
type ListAgentRunsResult,
|
|
16
24
|
type ReclaimAgentRunInput,
|
|
17
25
|
type StartAgentRunInput,
|
|
26
|
+
type SubagentRunRecord,
|
|
18
27
|
} from "@sixb/core/storage"
|
|
19
28
|
import type { SQLClient, SqlParameter } from "../pg-client"
|
|
20
29
|
import { appendRunListFilters, hasEmptyStatuses, queryRunList } from "../run-list-query"
|
|
@@ -30,14 +39,14 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
30
39
|
private readonly executions: ExecutionStorage
|
|
31
40
|
) {}
|
|
32
41
|
|
|
33
|
-
async create(input: CreateAgentRunInput): Promise<
|
|
42
|
+
async create(input: CreateAgentRunInput): Promise<ConversationAgentRunRecord> {
|
|
43
|
+
assertConversationAgentRunSpec(input.spec, "SixbPg")
|
|
34
44
|
const createdAt = input.createdAt ?? new Date()
|
|
35
45
|
await assertAgentRunExecution({
|
|
36
46
|
executions: this.executions,
|
|
37
47
|
projectId: input.projectId,
|
|
38
48
|
executionId: input.executionId,
|
|
39
49
|
runId: input.id,
|
|
40
|
-
agentId: input.agentId,
|
|
41
50
|
})
|
|
42
51
|
|
|
43
52
|
try {
|
|
@@ -54,7 +63,6 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
54
63
|
`[SixbPg] Agent thread '${input.threadId}' not found for project '${input.projectId}'.`
|
|
55
64
|
)
|
|
56
65
|
}
|
|
57
|
-
|
|
58
66
|
if (thread.active_run_id !== null) {
|
|
59
67
|
throw new AgentStorageError(
|
|
60
68
|
"active_run_exists",
|
|
@@ -67,9 +75,10 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
67
75
|
project_id,
|
|
68
76
|
id,
|
|
69
77
|
execution_id,
|
|
78
|
+
kind,
|
|
70
79
|
thread_id,
|
|
71
|
-
agent_id,
|
|
72
80
|
trigger_message_id,
|
|
81
|
+
spec,
|
|
73
82
|
requester_group_ids,
|
|
74
83
|
status,
|
|
75
84
|
attempt,
|
|
@@ -78,9 +87,10 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
78
87
|
${input.projectId},
|
|
79
88
|
${input.id},
|
|
80
89
|
${input.executionId},
|
|
90
|
+
${"conversation"},
|
|
81
91
|
${input.threadId},
|
|
82
|
-
${input.agentId},
|
|
83
92
|
${input.triggerMessageId},
|
|
93
|
+
${JSON.stringify(input.spec)}::text::jsonb,
|
|
84
94
|
${JSON.stringify(normalizeRequesterGroupIds(input.requesterGroupIds))}::text::jsonb,
|
|
85
95
|
${"queued"},
|
|
86
96
|
${0},
|
|
@@ -94,7 +104,11 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
94
104
|
WHERE project_id = ${input.projectId} AND id = ${input.threadId}
|
|
95
105
|
`
|
|
96
106
|
|
|
97
|
-
|
|
107
|
+
const created = rowToRunRecord(row)
|
|
108
|
+
if (created.kind !== "conversation") {
|
|
109
|
+
throw new Error(`[SixbPg] Agent run insert '${input.id}' returned the wrong run kind.`)
|
|
110
|
+
}
|
|
111
|
+
return created
|
|
98
112
|
})
|
|
99
113
|
} catch (error) {
|
|
100
114
|
if (isUniqueViolation(error)) {
|
|
@@ -108,6 +122,108 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
108
122
|
}
|
|
109
123
|
}
|
|
110
124
|
|
|
125
|
+
async createSubagent(input: CreateSubagentRunInput): Promise<SubagentRunRecord> {
|
|
126
|
+
assertCreateSubagentRunInput(input, "SixbPg")
|
|
127
|
+
const execution = await assertAgentRunExecution({
|
|
128
|
+
executions: this.executions,
|
|
129
|
+
projectId: input.projectId,
|
|
130
|
+
executionId: input.executionId,
|
|
131
|
+
runId: input.id,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
return await runPgTransaction(this.sql, async (tx) => {
|
|
136
|
+
const [parent] = await tx<AgentRunRow[]>`
|
|
137
|
+
SELECT * FROM agent_runs
|
|
138
|
+
WHERE project_id = ${input.projectId} AND id = ${input.parentRunId}
|
|
139
|
+
FOR UPDATE
|
|
140
|
+
`
|
|
141
|
+
if (!parent || parent.kind !== "conversation") {
|
|
142
|
+
throw new AgentStorageError(
|
|
143
|
+
"run_not_found",
|
|
144
|
+
`[SixbPg] Parent Agent run '${input.parentRunId}' was not found.`
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const [existingRow] = await tx<AgentRunRow[]>`
|
|
149
|
+
SELECT * FROM agent_runs
|
|
150
|
+
WHERE project_id = ${input.projectId} AND id = ${input.id}
|
|
151
|
+
`
|
|
152
|
+
if (existingRow) {
|
|
153
|
+
const existing = rowToRunRecord(existingRow)
|
|
154
|
+
if (subagentRunMatchesCreateInput(existing, input)) return existing
|
|
155
|
+
throw new AgentStorageError(
|
|
156
|
+
"duplicate_id",
|
|
157
|
+
`[SixbPg] Subagent run '${input.id}' already exists with different immutable inputs.`
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
if (parent.status !== "running") {
|
|
161
|
+
throw new AgentStorageError(
|
|
162
|
+
"invalid_state",
|
|
163
|
+
`[SixbPg] Parent Agent run '${parent.id}' is not running (status '${parent.status}').`
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
if (
|
|
167
|
+
execution.source.type !== "execution" ||
|
|
168
|
+
execution.source.executionId !== parent.execution_id
|
|
169
|
+
) {
|
|
170
|
+
throw new AgentStorageError(
|
|
171
|
+
"invalid_input",
|
|
172
|
+
`[SixbPg] Subagent execution '${execution.id}' is not a child of parent execution '${parent.execution_id}'.`
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
if (parent.execution_token !== input.parentExecutionToken) {
|
|
176
|
+
throw new AgentStorageError(
|
|
177
|
+
"execution_lost",
|
|
178
|
+
`[SixbPg] Parent Agent run '${parent.id}' is no longer owned by this execution.`
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
const parentRecord = rowToRunRecord(parent)
|
|
182
|
+
|
|
183
|
+
const [countRow] = await tx<{ count: string | number }[]>`
|
|
184
|
+
SELECT COUNT(*)::bigint AS count FROM agent_runs
|
|
185
|
+
WHERE project_id = ${input.projectId}
|
|
186
|
+
AND parent_run_id = ${input.parentRunId}
|
|
187
|
+
AND status IN (${"queued"}, ${"running"})
|
|
188
|
+
`
|
|
189
|
+
const activeChildren = Number(countRow?.count ?? 0)
|
|
190
|
+
if (activeChildren >= input.maxActiveChildren) {
|
|
191
|
+
throw new AgentStorageError(
|
|
192
|
+
"active_child_limit",
|
|
193
|
+
`[SixbPg] Agent run '${parent.id}' already has ${activeChildren} active subagents.`
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const [row] = await tx<AgentRunRow[]>`
|
|
198
|
+
INSERT INTO agent_runs (
|
|
199
|
+
project_id, id, execution_id, kind, parent_run_id, spawn_key, spec,
|
|
200
|
+
requester_group_ids, status, attempt, created_at
|
|
201
|
+
) VALUES (
|
|
202
|
+
${input.projectId}, ${input.id}, ${input.executionId}, ${"subagent"},
|
|
203
|
+
${input.parentRunId}, ${input.spawnKey},
|
|
204
|
+
${JSON.stringify(input.spec)}::text::jsonb,
|
|
205
|
+
${JSON.stringify(parentRecord.requesterGroupIds)}::text::jsonb,
|
|
206
|
+
${"queued"}, ${0}, ${input.createdAt ?? new Date()}
|
|
207
|
+
)
|
|
208
|
+
RETURNING *
|
|
209
|
+
`
|
|
210
|
+
const created = rowToRunRecord(row)
|
|
211
|
+
if (created.kind !== "subagent") {
|
|
212
|
+
throw new Error(`[SixbPg] Subagent insert '${input.id}' returned the wrong run kind.`)
|
|
213
|
+
}
|
|
214
|
+
return created
|
|
215
|
+
})
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (isUniqueViolation(error)) {
|
|
218
|
+
throw new AgentStorageError(
|
|
219
|
+
"duplicate_id",
|
|
220
|
+
`[SixbPg] Subagent run '${input.id}' conflicts with an existing durable spawn.`
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
throw error
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
111
227
|
async start(input: StartAgentRunInput): Promise<AgentRunRecord> {
|
|
112
228
|
const startedAt = input.startedAt ?? new Date()
|
|
113
229
|
return runPgTransaction(this.sql, async (tx) => {
|
|
@@ -198,6 +314,20 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
198
314
|
return runPgTransaction(this.sql, async (tx) => {
|
|
199
315
|
const run = await this.lockRunning(tx, input.projectId, input.id)
|
|
200
316
|
|
|
317
|
+
if (input.status === "succeeded" && run.kind === "subagent") {
|
|
318
|
+
assertSubagentRunResult(input.result, run.id, "SixbPg")
|
|
319
|
+
}
|
|
320
|
+
if (
|
|
321
|
+
input.status === "succeeded" &&
|
|
322
|
+
run.kind === "conversation" &&
|
|
323
|
+
input.result !== undefined
|
|
324
|
+
) {
|
|
325
|
+
throw new AgentStorageError(
|
|
326
|
+
"invalid_input",
|
|
327
|
+
`[SixbPg] Conversational Agent run '${run.id}' cannot persist a subagent result.`
|
|
328
|
+
)
|
|
329
|
+
}
|
|
330
|
+
|
|
201
331
|
if (run.execution_token !== input.executionToken) {
|
|
202
332
|
throw new AgentStorageError(
|
|
203
333
|
"execution_lost",
|
|
@@ -213,6 +343,7 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
213
343
|
finish_reason = ${input.finishReason ?? null},
|
|
214
344
|
error = ${errorValue}::text::jsonb,
|
|
215
345
|
diagnostics = ${input.diagnostics === undefined ? null : JSON.stringify(input.diagnostics)}::text::jsonb,
|
|
346
|
+
result = ${input.status === "succeeded" && input.result !== undefined ? JSON.stringify(input.result) : null}::text::jsonb,
|
|
216
347
|
execution_token = ${null},
|
|
217
348
|
execution_queue_lease_expires_at = ${null},
|
|
218
349
|
completed_at = ${completedAt}
|
|
@@ -262,7 +393,7 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
262
393
|
}
|
|
263
394
|
|
|
264
395
|
async list(input: ListAgentRunsInput): Promise<ListAgentRunsResult> {
|
|
265
|
-
if (hasEmptyStatuses(input)) {
|
|
396
|
+
if (hasEmptyStatuses(input) || input.kinds?.length === 0) {
|
|
266
397
|
return { runs: [], hasMore: false, total: 0 }
|
|
267
398
|
}
|
|
268
399
|
|
|
@@ -270,14 +401,19 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
270
401
|
const params: SqlParameter[] = [input.projectId]
|
|
271
402
|
let index = 2
|
|
272
403
|
|
|
404
|
+
if (input.kinds) {
|
|
405
|
+
whereClauses.push(`kind = ANY($${index++}::text[])`)
|
|
406
|
+
params.push(input.kinds as string[])
|
|
407
|
+
}
|
|
408
|
+
|
|
273
409
|
if (input.threadId) {
|
|
274
410
|
whereClauses.push(`thread_id = $${index++}`)
|
|
275
411
|
params.push(input.threadId)
|
|
276
412
|
}
|
|
277
413
|
|
|
278
|
-
if (input.
|
|
279
|
-
whereClauses.push(`
|
|
280
|
-
params.push(input.
|
|
414
|
+
if (input.parentRunId) {
|
|
415
|
+
whereClauses.push(`parent_run_id = $${index++}`)
|
|
416
|
+
params.push(input.parentRunId)
|
|
281
417
|
}
|
|
282
418
|
|
|
283
419
|
index = appendRunListFilters(
|
|
@@ -336,6 +472,7 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
336
472
|
}
|
|
337
473
|
|
|
338
474
|
private async releaseThread(sql: SQLClient, run: AgentRunRow, completedAt: Date): Promise<void> {
|
|
475
|
+
if (run.kind !== "conversation") return
|
|
339
476
|
await sql`
|
|
340
477
|
UPDATE agent_threads SET active_run_id = ${null}, updated_at = ${completedAt}
|
|
341
478
|
WHERE project_id = ${run.project_id} AND id = ${run.thread_id} AND active_run_id = ${run.id}
|
package/src/agents/threads.ts
CHANGED
|
@@ -23,7 +23,6 @@ export class PgAgentThreadStore implements AgentThreadStore {
|
|
|
23
23
|
INSERT INTO agent_threads (
|
|
24
24
|
project_id,
|
|
25
25
|
id,
|
|
26
|
-
agent_id,
|
|
27
26
|
owner_principal_type,
|
|
28
27
|
owner_principal_id,
|
|
29
28
|
title,
|
|
@@ -33,7 +32,6 @@ export class PgAgentThreadStore implements AgentThreadStore {
|
|
|
33
32
|
) VALUES (
|
|
34
33
|
${input.projectId},
|
|
35
34
|
${input.id},
|
|
36
|
-
${input.agentId},
|
|
37
35
|
${input.ownerPrincipal.type},
|
|
38
36
|
${input.ownerPrincipal.id},
|
|
39
37
|
${input.title ?? null},
|
|
@@ -66,10 +64,7 @@ export class PgAgentThreadStore implements AgentThreadStore {
|
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
async list(input: ListAgentThreadsInput): Promise<ListAgentThreadsResult> {
|
|
69
|
-
if (
|
|
70
|
-
(input.statuses !== undefined && input.statuses.length === 0) ||
|
|
71
|
-
(input.agentIds !== undefined && input.agentIds.length === 0)
|
|
72
|
-
) {
|
|
67
|
+
if (input.statuses !== undefined && input.statuses.length === 0) {
|
|
73
68
|
return { threads: [], hasMore: false, total: 0 }
|
|
74
69
|
}
|
|
75
70
|
|
|
@@ -77,17 +72,6 @@ export class PgAgentThreadStore implements AgentThreadStore {
|
|
|
77
72
|
const params: SqlParameter[] = [input.projectId]
|
|
78
73
|
let index = 2
|
|
79
74
|
|
|
80
|
-
if (input.agentId) {
|
|
81
|
-
whereClauses.push(`agent_id = $${index++}`)
|
|
82
|
-
params.push(input.agentId)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (input.agentIds) {
|
|
86
|
-
const placeholders = input.agentIds.map(() => `$${index++}`)
|
|
87
|
-
whereClauses.push(`agent_id IN (${placeholders.join(", ")})`)
|
|
88
|
-
params.push(...input.agentIds)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
75
|
if (input.statuses) {
|
|
92
76
|
const placeholders = input.statuses.map(() => `$${index++}`)
|
|
93
77
|
whereClauses.push(`status IN (${placeholders.join(", ")})`)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AiModelCallGroupRow,
|
|
3
|
+
aiModelCallGroupFragmentFromRow,
|
|
4
|
+
buildAiModelCallGroupsFromFragments,
|
|
5
|
+
normalizeAiModelCallAccountingQuery,
|
|
6
|
+
} from "@sixb/core/internal/ai-cost-storage-provider"
|
|
7
|
+
import type { ListAiModelCallGroupsInput, ListAiModelCallGroupsResult } from "@sixb/core/storage"
|
|
8
|
+
import type { PgStoreClient } from "./transactions"
|
|
9
|
+
|
|
10
|
+
/** Paginate initiating executions, then aggregate their matching calls without loading usage payloads. */
|
|
11
|
+
export async function listAiModelCallGroups(
|
|
12
|
+
sql: PgStoreClient,
|
|
13
|
+
input: ListAiModelCallGroupsInput
|
|
14
|
+
): Promise<ListAiModelCallGroupsResult> {
|
|
15
|
+
const query = normalizeAiModelCallAccountingQuery(input)
|
|
16
|
+
const parameters = [
|
|
17
|
+
query.projectId,
|
|
18
|
+
query.from.toISOString(),
|
|
19
|
+
query.to.toISOString(),
|
|
20
|
+
query.providerId ?? null,
|
|
21
|
+
query.modelId ?? null,
|
|
22
|
+
query.valuationStatus ?? null,
|
|
23
|
+
]
|
|
24
|
+
const [count] = await sql.unsafe<{ total: string }[]>(COUNT_SQL, parameters)
|
|
25
|
+
const rows = await sql.unsafe<AiModelCallGroupRow[]>(PAGE_SQL, [
|
|
26
|
+
...parameters,
|
|
27
|
+
query.limit,
|
|
28
|
+
query.offset,
|
|
29
|
+
])
|
|
30
|
+
const total = Number(count?.total)
|
|
31
|
+
if (!Number.isSafeInteger(total) || total < 0)
|
|
32
|
+
throw new RangeError("[Sixb] Invalid model-call group count.")
|
|
33
|
+
const items = buildAiModelCallGroupsFromFragments(rows.map(aiModelCallGroupFragmentFromRow))
|
|
34
|
+
return { items, total, hasMore: query.offset + items.length < total }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const FILTERED_SQL = `
|
|
38
|
+
WITH filtered AS (
|
|
39
|
+
SELECT COALESCE(root_agent.execution_id, usage.execution_id) AS root_execution_id,
|
|
40
|
+
usage.execution_id, usage.provider_id, usage.requested_model_id, usage.occurred_at,
|
|
41
|
+
usage.total_tokens,
|
|
42
|
+
COALESCE(cost.status, 'unvalued') AS valuation_status,
|
|
43
|
+
cost.currency AS amount_currency, cost.amount_nanos,
|
|
44
|
+
root_agent.id AS root_agent_run_id,
|
|
45
|
+
root_agent.thread_id AS root_thread_id,
|
|
46
|
+
direct_agent.kind AS direct_kind,
|
|
47
|
+
direct_agent.id AS direct_run_id,
|
|
48
|
+
direct_agent.parent_run_id AS parent_run_id,
|
|
49
|
+
workflow_node.node_id AS workflow_agent_step_id,
|
|
50
|
+
workflow_agent.node_run_id AS workflow_node_run_id,
|
|
51
|
+
workflow_node.workflow_id AS workflow_id,
|
|
52
|
+
workflow_node.workflow_run_id AS workflow_run_id
|
|
53
|
+
FROM ai_model_call_usage AS usage
|
|
54
|
+
LEFT JOIN agent_runs AS direct_agent
|
|
55
|
+
ON direct_agent.project_id = usage.project_id AND direct_agent.execution_id = usage.execution_id
|
|
56
|
+
LEFT JOIN agent_runs AS root_agent
|
|
57
|
+
ON root_agent.project_id = usage.project_id
|
|
58
|
+
AND root_agent.id = COALESCE(direct_agent.parent_run_id, direct_agent.id)
|
|
59
|
+
LEFT JOIN workflow_agent_node_runs AS workflow_agent
|
|
60
|
+
ON workflow_agent.project_id = usage.project_id AND workflow_agent.execution_id = usage.execution_id
|
|
61
|
+
LEFT JOIN workflow_node_runs AS workflow_node
|
|
62
|
+
ON workflow_node.project_id = workflow_agent.project_id AND workflow_node.id = workflow_agent.node_run_id
|
|
63
|
+
LEFT JOIN ai_model_call_valuations AS cost
|
|
64
|
+
ON cost.project_id = usage.project_id AND cost.usage_record_id = usage.id
|
|
65
|
+
WHERE usage.project_id = $1
|
|
66
|
+
AND usage.occurred_at >= $2::timestamptz
|
|
67
|
+
AND usage.occurred_at < $3::timestamptz
|
|
68
|
+
AND ($4::text IS NULL OR usage.provider_id = $4)
|
|
69
|
+
AND ($5::text IS NULL OR usage.requested_model_id = $5)
|
|
70
|
+
AND ($6::text IS NULL OR COALESCE(cost.status, 'unvalued') = $6)
|
|
71
|
+
), roots AS (
|
|
72
|
+
SELECT root_execution_id, MAX(occurred_at) AS last_call_at
|
|
73
|
+
FROM filtered GROUP BY root_execution_id
|
|
74
|
+
)
|
|
75
|
+
`
|
|
76
|
+
const COUNT_SQL = `${FILTERED_SQL} SELECT CAST(COUNT(*) AS TEXT) AS total FROM roots`
|
|
77
|
+
const PAGE_SQL = `${FILTERED_SQL}, page AS (
|
|
78
|
+
SELECT root_execution_id FROM roots
|
|
79
|
+
ORDER BY last_call_at DESC, root_execution_id COLLATE "C" ASC LIMIT $7 OFFSET $8
|
|
80
|
+
)
|
|
81
|
+
SELECT filtered.root_execution_id, execution_id, provider_id, requested_model_id,
|
|
82
|
+
MIN(occurred_at) AS first_call_at, MAX(occurred_at) AS last_call_at,
|
|
83
|
+
CAST(COUNT(*) AS TEXT) AS call_count,
|
|
84
|
+
CAST(COUNT(total_tokens) AS TEXT) AS token_count,
|
|
85
|
+
CAST(SUM(total_tokens) AS TEXT) AS total_tokens,
|
|
86
|
+
valuation_status, amount_currency,
|
|
87
|
+
CAST(SUM(amount_nanos / 1000000000000) AS TEXT) AS amount_high,
|
|
88
|
+
CAST(SUM((amount_nanos / 1000000) % 1000000) AS TEXT) AS amount_middle,
|
|
89
|
+
CAST(SUM(amount_nanos % 1000000) AS TEXT) AS amount_low,
|
|
90
|
+
MAX(root_agent_run_id) AS root_agent_run_id,
|
|
91
|
+
MAX(root_thread_id) AS root_thread_id,
|
|
92
|
+
MAX(direct_kind) AS direct_kind,
|
|
93
|
+
MAX(direct_run_id) AS direct_run_id,
|
|
94
|
+
MAX(parent_run_id) AS parent_run_id,
|
|
95
|
+
MAX(workflow_agent_step_id) AS workflow_agent_step_id,
|
|
96
|
+
MAX(workflow_node_run_id) AS workflow_node_run_id,
|
|
97
|
+
MAX(workflow_id) AS workflow_id,
|
|
98
|
+
MAX(workflow_run_id) AS workflow_run_id
|
|
99
|
+
FROM filtered JOIN page ON page.root_execution_id = filtered.root_execution_id
|
|
100
|
+
GROUP BY filtered.root_execution_id, execution_id, provider_id, requested_model_id,
|
|
101
|
+
valuation_status, amount_currency
|
|
102
|
+
`
|