@dexcost/sdk 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 (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/adapters/_netbytes.d.ts +31 -0
  4. package/dist/adapters/_netbytes.d.ts.map +1 -0
  5. package/dist/adapters/_netbytes.js +154 -0
  6. package/dist/adapters/_netbytes.js.map +1 -0
  7. package/dist/adapters/aws-lambda.d.ts +41 -0
  8. package/dist/adapters/aws-lambda.d.ts.map +1 -0
  9. package/dist/adapters/aws-lambda.js +65 -0
  10. package/dist/adapters/aws-lambda.js.map +1 -0
  11. package/dist/adapters/browser.d.ts +52 -0
  12. package/dist/adapters/browser.d.ts.map +1 -0
  13. package/dist/adapters/browser.js +127 -0
  14. package/dist/adapters/browser.js.map +1 -0
  15. package/dist/adapters/compute-wrap.d.ts +33 -0
  16. package/dist/adapters/compute-wrap.d.ts.map +1 -0
  17. package/dist/adapters/compute-wrap.js +188 -0
  18. package/dist/adapters/compute-wrap.js.map +1 -0
  19. package/dist/adapters/data/aws_lambda_pricing.json +61 -0
  20. package/dist/adapters/gpu-wrap.d.ts +31 -0
  21. package/dist/adapters/gpu-wrap.d.ts.map +1 -0
  22. package/dist/adapters/gpu-wrap.js +147 -0
  23. package/dist/adapters/gpu-wrap.js.map +1 -0
  24. package/dist/adapters/http.d.ts +58 -0
  25. package/dist/adapters/http.d.ts.map +1 -0
  26. package/dist/adapters/http.js +769 -0
  27. package/dist/adapters/http.js.map +1 -0
  28. package/dist/adapters/index.d.ts +11 -0
  29. package/dist/adapters/index.d.ts.map +1 -0
  30. package/dist/adapters/index.js +12 -0
  31. package/dist/adapters/index.js.map +1 -0
  32. package/dist/adapters/network-accountant.d.ts +63 -0
  33. package/dist/adapters/network-accountant.d.ts.map +1 -0
  34. package/dist/adapters/network-accountant.js +153 -0
  35. package/dist/adapters/network-accountant.js.map +1 -0
  36. package/dist/cli/index.d.ts +13 -0
  37. package/dist/cli/index.d.ts.map +1 -0
  38. package/dist/cli/index.js +225 -0
  39. package/dist/cli/index.js.map +1 -0
  40. package/dist/cli/scanner.d.ts +39 -0
  41. package/dist/cli/scanner.d.ts.map +1 -0
  42. package/dist/cli/scanner.js +480 -0
  43. package/dist/cli/scanner.js.map +1 -0
  44. package/dist/clients.d.ts +54 -0
  45. package/dist/clients.d.ts.map +1 -0
  46. package/dist/clients.js +240 -0
  47. package/dist/clients.js.map +1 -0
  48. package/dist/cloud-detect.d.ts +96 -0
  49. package/dist/cloud-detect.d.ts.map +1 -0
  50. package/dist/cloud-detect.js +545 -0
  51. package/dist/cloud-detect.js.map +1 -0
  52. package/dist/core/auto-task.d.ts +20 -0
  53. package/dist/core/auto-task.d.ts.map +1 -0
  54. package/dist/core/auto-task.js +34 -0
  55. package/dist/core/auto-task.js.map +1 -0
  56. package/dist/core/cgroup-reader.d.ts +45 -0
  57. package/dist/core/cgroup-reader.d.ts.map +1 -0
  58. package/dist/core/cgroup-reader.js +124 -0
  59. package/dist/core/cgroup-reader.js.map +1 -0
  60. package/dist/core/cgroup-walker.d.ts +60 -0
  61. package/dist/core/cgroup-walker.d.ts.map +1 -0
  62. package/dist/core/cgroup-walker.js +166 -0
  63. package/dist/core/cgroup-walker.js.map +1 -0
  64. package/dist/core/compute-accountant.d.ts +51 -0
  65. package/dist/core/compute-accountant.d.ts.map +1 -0
  66. package/dist/core/compute-accountant.js +179 -0
  67. package/dist/core/compute-accountant.js.map +1 -0
  68. package/dist/core/compute-runtime.d.ts +42 -0
  69. package/dist/core/compute-runtime.d.ts.map +1 -0
  70. package/dist/core/compute-runtime.js +80 -0
  71. package/dist/core/compute-runtime.js.map +1 -0
  72. package/dist/core/config.d.ts +44 -0
  73. package/dist/core/config.d.ts.map +1 -0
  74. package/dist/core/config.js +66 -0
  75. package/dist/core/config.js.map +1 -0
  76. package/dist/core/context.d.ts +76 -0
  77. package/dist/core/context.d.ts.map +1 -0
  78. package/dist/core/context.js +91 -0
  79. package/dist/core/context.js.map +1 -0
  80. package/dist/core/fargate-metadata.d.ts +27 -0
  81. package/dist/core/fargate-metadata.d.ts.map +1 -0
  82. package/dist/core/fargate-metadata.js +102 -0
  83. package/dist/core/fargate-metadata.js.map +1 -0
  84. package/dist/core/gpu-accountant.d.ts +104 -0
  85. package/dist/core/gpu-accountant.d.ts.map +1 -0
  86. package/dist/core/gpu-accountant.js +383 -0
  87. package/dist/core/gpu-accountant.js.map +1 -0
  88. package/dist/core/gpu-runtime.d.ts +58 -0
  89. package/dist/core/gpu-runtime.d.ts.map +1 -0
  90. package/dist/core/gpu-runtime.js +131 -0
  91. package/dist/core/gpu-runtime.js.map +1 -0
  92. package/dist/core/heuristics.d.ts +74 -0
  93. package/dist/core/heuristics.d.ts.map +1 -0
  94. package/dist/core/heuristics.js +182 -0
  95. package/dist/core/heuristics.js.map +1 -0
  96. package/dist/core/models.d.ts +149 -0
  97. package/dist/core/models.d.ts.map +1 -0
  98. package/dist/core/models.js +226 -0
  99. package/dist/core/models.js.map +1 -0
  100. package/dist/core/nvml-reader.d.ts +114 -0
  101. package/dist/core/nvml-reader.d.ts.map +1 -0
  102. package/dist/core/nvml-reader.js +323 -0
  103. package/dist/core/nvml-reader.js.map +1 -0
  104. package/dist/core/session.d.ts +48 -0
  105. package/dist/core/session.d.ts.map +1 -0
  106. package/dist/core/session.js +123 -0
  107. package/dist/core/session.js.map +1 -0
  108. package/dist/core/tracker.d.ts +364 -0
  109. package/dist/core/tracker.d.ts.map +1 -0
  110. package/dist/core/tracker.js +1073 -0
  111. package/dist/core/tracker.js.map +1 -0
  112. package/dist/data/compute_prices.json +180 -0
  113. package/dist/data/egress_prices.json +418 -0
  114. package/dist/data/gpu_prices.json +412 -0
  115. package/dist/data/service_prices.json +2595 -0
  116. package/dist/dev-console.d.ts +12 -0
  117. package/dist/dev-console.d.ts.map +1 -0
  118. package/dist/dev-console.js +60 -0
  119. package/dist/dev-console.js.map +1 -0
  120. package/dist/index.d.ts +52 -0
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +61 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/instruments/anthropic.d.ts +26 -0
  125. package/dist/instruments/anthropic.d.ts.map +1 -0
  126. package/dist/instruments/anthropic.js +242 -0
  127. package/dist/instruments/anthropic.js.map +1 -0
  128. package/dist/instruments/bedrock.d.ts +29 -0
  129. package/dist/instruments/bedrock.d.ts.map +1 -0
  130. package/dist/instruments/bedrock.js +215 -0
  131. package/dist/instruments/bedrock.js.map +1 -0
  132. package/dist/instruments/cohere.d.ts +29 -0
  133. package/dist/instruments/cohere.d.ts.map +1 -0
  134. package/dist/instruments/cohere.js +237 -0
  135. package/dist/instruments/cohere.js.map +1 -0
  136. package/dist/instruments/gemini.d.ts +30 -0
  137. package/dist/instruments/gemini.d.ts.map +1 -0
  138. package/dist/instruments/gemini.js +247 -0
  139. package/dist/instruments/gemini.js.map +1 -0
  140. package/dist/instruments/index.d.ts +35 -0
  141. package/dist/instruments/index.d.ts.map +1 -0
  142. package/dist/instruments/index.js +54 -0
  143. package/dist/instruments/index.js.map +1 -0
  144. package/dist/instruments/mcp.d.ts +24 -0
  145. package/dist/instruments/mcp.d.ts.map +1 -0
  146. package/dist/instruments/mcp.js +459 -0
  147. package/dist/instruments/mcp.js.map +1 -0
  148. package/dist/instruments/openai.d.ts +26 -0
  149. package/dist/instruments/openai.d.ts.map +1 -0
  150. package/dist/instruments/openai.js +221 -0
  151. package/dist/instruments/openai.js.map +1 -0
  152. package/dist/instruments/vercel-ai.d.ts +28 -0
  153. package/dist/instruments/vercel-ai.d.ts.map +1 -0
  154. package/dist/instruments/vercel-ai.js +192 -0
  155. package/dist/instruments/vercel-ai.js.map +1 -0
  156. package/dist/integrations/langchain.d.ts +65 -0
  157. package/dist/integrations/langchain.d.ts.map +1 -0
  158. package/dist/integrations/langchain.js +165 -0
  159. package/dist/integrations/langchain.js.map +1 -0
  160. package/dist/middleware/express.d.ts +55 -0
  161. package/dist/middleware/express.d.ts.map +1 -0
  162. package/dist/middleware/express.js +101 -0
  163. package/dist/middleware/express.js.map +1 -0
  164. package/dist/middleware/index.d.ts +6 -0
  165. package/dist/middleware/index.d.ts.map +1 -0
  166. package/dist/middleware/index.js +5 -0
  167. package/dist/middleware/index.js.map +1 -0
  168. package/dist/pricing/compute-pricing.d.ts +57 -0
  169. package/dist/pricing/compute-pricing.d.ts.map +1 -0
  170. package/dist/pricing/compute-pricing.js +627 -0
  171. package/dist/pricing/compute-pricing.js.map +1 -0
  172. package/dist/pricing/cost_map.json +37665 -0
  173. package/dist/pricing/egress-pricing.d.ts +55 -0
  174. package/dist/pricing/egress-pricing.d.ts.map +1 -0
  175. package/dist/pricing/egress-pricing.js +226 -0
  176. package/dist/pricing/egress-pricing.js.map +1 -0
  177. package/dist/pricing/engine.d.ts +24 -0
  178. package/dist/pricing/engine.d.ts.map +1 -0
  179. package/dist/pricing/engine.js +148 -0
  180. package/dist/pricing/engine.js.map +1 -0
  181. package/dist/pricing/gpu-pricing.d.ts +63 -0
  182. package/dist/pricing/gpu-pricing.d.ts.map +1 -0
  183. package/dist/pricing/gpu-pricing.js +484 -0
  184. package/dist/pricing/gpu-pricing.js.map +1 -0
  185. package/dist/pricing/rates.d.ts +17 -0
  186. package/dist/pricing/rates.d.ts.map +1 -0
  187. package/dist/pricing/rates.js +102 -0
  188. package/dist/pricing/rates.js.map +1 -0
  189. package/dist/pricing/service-catalog.d.ts +87 -0
  190. package/dist/pricing/service-catalog.d.ts.map +1 -0
  191. package/dist/pricing/service-catalog.js +406 -0
  192. package/dist/pricing/service-catalog.js.map +1 -0
  193. package/dist/schema/dexcost-event.v1.json +111 -0
  194. package/dist/schema/dexcost-task.v1.json +160 -0
  195. package/dist/schema/validate.d.ts +15 -0
  196. package/dist/schema/validate.d.ts.map +1 -0
  197. package/dist/schema/validate.js +87 -0
  198. package/dist/schema/validate.js.map +1 -0
  199. package/dist/security/redaction.d.ts +55 -0
  200. package/dist/security/redaction.d.ts.map +1 -0
  201. package/dist/security/redaction.js +144 -0
  202. package/dist/security/redaction.js.map +1 -0
  203. package/dist/transport/buffer.d.ts +117 -0
  204. package/dist/transport/buffer.d.ts.map +1 -0
  205. package/dist/transport/buffer.js +759 -0
  206. package/dist/transport/buffer.js.map +1 -0
  207. package/dist/transport/pusher.d.ts +89 -0
  208. package/dist/transport/pusher.d.ts.map +1 -0
  209. package/dist/transport/pusher.js +323 -0
  210. package/dist/transport/pusher.js.map +1 -0
  211. package/package.json +93 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Per-task GPU accountant — Phase 2 v1 capture.
3
+ *
4
+ * One per dexcost task. Lives on Task as `_gpu` (mirrors `_compute` /
5
+ * `_network`). Holds the start cgroup snapshot, the per-device NVML
6
+ * start-snapshot timestamps (Decision #8 persistent state), and the
7
+ * device indexes.
8
+ *
9
+ * At task finalize:
10
+ *
11
+ * 1. Snapshots NVML utilization across all devices.
12
+ * 2. Walks the cgroup PIDs (Decision #1) and accumulates SM-time across
13
+ * them per device.
14
+ * 3. Computes the window-averaged `sm_util_pct` per Decision #3 (NOT a
15
+ * point sample at finalize).
16
+ * 4. Resolves a coarse GPU SKU hint from the productName.
17
+ * 5. Emits ONE gpu_cost event (cost_pending=true; back-filled at task
18
+ * finalize) AND N gpu_utilization_signal events (one per device that
19
+ * the task's cgroup touched).
20
+ *
21
+ * Idempotent — second call to snapshotEndAndBuild returns
22
+ * `{ costDetails: null, signalEvents: null }` per capture spec §5.3.
23
+ *
24
+ * **TS deviation**: accepts a `GpuAccountantHooks` object so tests can
25
+ * inject deterministic stubs without monkeypatching ESM module imports
26
+ * (matches the gpu-runtime pattern). Default hooks delegate to the
27
+ * production nvml-reader + cgroup-walker modules.
28
+ *
29
+ * Browser safety: snapshotStart short-circuits off-Node; snapshotEndAndBuild
30
+ * returns nulls.
31
+ *
32
+ * Mirrors python/src/dexcost/gpu_accountant.py.
33
+ */
34
+ import { GpuRuntimeKind } from "./gpu-runtime.js";
35
+ import type { CloudEnv } from "../cloud-detect.js";
36
+ import { type CgroupScope } from "./cgroup-walker.js";
37
+ import { type MemInfo, type UtilSample } from "./nvml-reader.js";
38
+ export declare function _resetWarningStateForTests(): void;
39
+ export interface GpuAccountantHooks {
40
+ initNvml: () => boolean;
41
+ getDeviceCount: () => number | null;
42
+ getProductName: (index: number) => string | null;
43
+ getMigMode: (index: number) => boolean;
44
+ getMemoryInfo: (index: number) => MemInfo | null;
45
+ classifyScope: () => CgroupScope;
46
+ enumeratePids: (scope: CgroupScope) => number[] | null;
47
+ getProcessUtilization: (index: number, lastSeen: Record<number, number>) => Record<number, UtilSample[]> | null;
48
+ }
49
+ export interface GpuCostDetails {
50
+ billing_model: string;
51
+ gpu_vendor: string;
52
+ gpu_sku: string | null;
53
+ gpu_count: number;
54
+ region: string | null;
55
+ duration_ms: number;
56
+ gpu_seconds_used: number;
57
+ instance_type: string | null;
58
+ vgpu_profile: string | null;
59
+ mig_profile: string | null;
60
+ cost_pending: true;
61
+ _nvml_product_name_lower?: string;
62
+ _cgroup_scope_fallback?: string;
63
+ }
64
+ export interface GpuUtilizationSignal {
65
+ gpu_index: number;
66
+ gpu_sku: string | null;
67
+ sm_util_pct: number | null;
68
+ mem_util_pct: number | null;
69
+ vram_used_peak_bytes: number;
70
+ vram_total_bytes: number;
71
+ process_count: number;
72
+ sample_count: number;
73
+ task_duration_ms: number;
74
+ }
75
+ export declare class GpuAccountant {
76
+ readonly runtime: GpuRuntimeKind;
77
+ readonly cloudEnv: CloudEnv;
78
+ private readonly hooks;
79
+ private _frozen;
80
+ private _scope;
81
+ private _initialPids;
82
+ private _initialTimestamps;
83
+ private _deviceIndexes;
84
+ private _deviceProductNames;
85
+ private _deviceMigModes;
86
+ private _vramTotal;
87
+ private _vramUsedPeak;
88
+ private _pidsTouchedPerDevice;
89
+ constructor(runtime: GpuRuntimeKind, cloudEnv: CloudEnv, hooks?: Partial<GpuAccountantHooks>);
90
+ /** Initialize NVML, snapshot cgroup PIDs, capture baseline NVML timestamps. */
91
+ snapshotStart(): void;
92
+ /**
93
+ * Build (gpu_cost_event, [gpu_utilization_signal_events]) at task finalize.
94
+ *
95
+ * Returns nulls when frozen, NVML unavailable, or 0 devices touched.
96
+ */
97
+ snapshotEndAndBuild(durationMs: number): {
98
+ costDetails: GpuCostDetails | null;
99
+ signalEvents: GpuUtilizationSignal[] | null;
100
+ };
101
+ private buildCostEvent;
102
+ private resolveVgpuProfile;
103
+ }
104
+ //# sourceMappingURL=gpu-accountant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu-accountant.d.ts","sourceRoot":"","sources":["../../src/core/gpu-accountant.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Per-task GPU accountant — Phase 2 v1 capture.
3
+ *
4
+ * One per dexcost task. Lives on Task as `_gpu` (mirrors `_compute` /
5
+ * `_network`). Holds the start cgroup snapshot, the per-device NVML
6
+ * start-snapshot timestamps (Decision #8 persistent state), and the
7
+ * device indexes.
8
+ *
9
+ * At task finalize:
10
+ *
11
+ * 1. Snapshots NVML utilization across all devices.
12
+ * 2. Walks the cgroup PIDs (Decision #1) and accumulates SM-time across
13
+ * them per device.
14
+ * 3. Computes the window-averaged `sm_util_pct` per Decision #3 (NOT a
15
+ * point sample at finalize).
16
+ * 4. Resolves a coarse GPU SKU hint from the productName.
17
+ * 5. Emits ONE gpu_cost event (cost_pending=true; back-filled at task
18
+ * finalize) AND N gpu_utilization_signal events (one per device that
19
+ * the task's cgroup touched).
20
+ *
21
+ * Idempotent — second call to snapshotEndAndBuild returns
22
+ * `{ costDetails: null, signalEvents: null }` per capture spec §5.3.
23
+ *
24
+ * **TS deviation**: accepts a `GpuAccountantHooks` object so tests can
25
+ * inject deterministic stubs without monkeypatching ESM module imports
26
+ * (matches the gpu-runtime pattern). Default hooks delegate to the
27
+ * production nvml-reader + cgroup-walker modules.
28
+ *
29
+ * Browser safety: snapshotStart short-circuits off-Node; snapshotEndAndBuild
30
+ * returns nulls.
31
+ *
32
+ * Mirrors python/src/dexcost/gpu_accountant.py.
33
+ */
34
+ import { GpuRuntimeKind } from "./gpu-runtime.js";
35
+ import { classifyScope as defaultClassifyScope, enumeratePids as defaultEnumeratePids, fallbackLabelFor, } from "./cgroup-walker.js";
36
+ import { getDeviceCount as defaultGetDeviceCount, getMemoryInfo as defaultGetMemoryInfo, getMigMode as defaultGetMigMode, getProcessUtilization as defaultGetProcessUtilization, getProductName as defaultGetProductName, initNvml as defaultInitNvml, } from "./nvml-reader.js";
37
+ // ─── Warn-once state ────────────────────────────────────────────────────────
38
+ const _warnedModes = new Set();
39
+ export function _resetWarningStateForTests() {
40
+ _warnedModes.clear();
41
+ }
42
+ function _warnOnce(mode, message) {
43
+ if (_warnedModes.has(mode))
44
+ return;
45
+ _warnedModes.add(mode);
46
+ // eslint-disable-next-line no-console
47
+ console.warn(message);
48
+ }
49
+ function defaultHooks() {
50
+ return {
51
+ initNvml: defaultInitNvml,
52
+ getDeviceCount: defaultGetDeviceCount,
53
+ getProductName: defaultGetProductName,
54
+ getMigMode: defaultGetMigMode,
55
+ getMemoryInfo: defaultGetMemoryInfo,
56
+ classifyScope: defaultClassifyScope,
57
+ enumeratePids: defaultEnumeratePids,
58
+ getProcessUtilization: defaultGetProcessUtilization,
59
+ };
60
+ }
61
+ // Map GpuRuntimeKind → details.billing_model for pricing dispatch.
62
+ const BILLING_MODEL_FOR_RUNTIME = {
63
+ [GpuRuntimeKind.Modal]: "per_gpu_second_active",
64
+ [GpuRuntimeKind.RunPod]: "per_gpu_second_active",
65
+ [GpuRuntimeKind.Replicate]: "per_gpu_second_active",
66
+ [GpuRuntimeKind.LambdaLabs]: "per_gpu_hour_reserved",
67
+ [GpuRuntimeKind.CoreWeave]: "per_gpu_hour_reserved",
68
+ [GpuRuntimeKind.GcpGceN1Attached]: "per_gpu_hour_reserved",
69
+ [GpuRuntimeKind.AwsEc2Gpu]: "per_instance_hour",
70
+ [GpuRuntimeKind.GcpGceBundled]: "per_instance_hour",
71
+ [GpuRuntimeKind.AzureVmGpu]: "per_instance_hour",
72
+ [GpuRuntimeKind.AzureVmVgpu]: "per_vgpu_hour",
73
+ };
74
+ // ─── Helpers ────────────────────────────────────────────────────────────────
75
+ function _isNode() {
76
+ return typeof process !== "undefined" && !!process.versions?.node;
77
+ }
78
+ function resolveSkuFromProductName(name) {
79
+ if (!name)
80
+ return null;
81
+ if (name.includes("h100"))
82
+ return "h100-80gb-sxm5";
83
+ if (name.includes("h200"))
84
+ return "h200-141gb-sxm5";
85
+ if (name.includes("a100")) {
86
+ if (name.includes("40gb"))
87
+ return "a100-40gb-sxm4";
88
+ return "a100-80gb-sxm4";
89
+ }
90
+ if (name.includes("a10g"))
91
+ return "a10g-24gb";
92
+ if (name.includes("a10-4q"))
93
+ return "a10-vgpu-1of6";
94
+ if (name.includes("a10-8q"))
95
+ return "a10-vgpu-1of3";
96
+ if (name.includes("a10-12q"))
97
+ return "a10-vgpu-1of2";
98
+ if (name.includes("a10-24q") || name.includes("a10"))
99
+ return "a10";
100
+ if (name.includes("l40s"))
101
+ return "l40s-48gb";
102
+ if (name.includes("l4"))
103
+ return "l4-24gb";
104
+ if (name.includes("tesla t4") || name.includes("nvidia t4"))
105
+ return "t4-16gb";
106
+ if (name.includes("rtx 6000"))
107
+ return "rtx-6000-24gb";
108
+ return null;
109
+ }
110
+ const AZURE_VGPU_PROFILE_BY_INSTANCE = {
111
+ Standard_NV6ads_A10_v5: "1/6 A10",
112
+ Standard_NV12ads_A10_v5: "1/3 A10",
113
+ Standard_NV18ads_A10_v5: "1/2 A10",
114
+ Standard_NV36ads_A10_v5: "full A10",
115
+ Standard_NV72ads_A10_v5: "2x A10",
116
+ };
117
+ // ═════════════════════════════════════════════════════════════════════════════
118
+ // GpuAccountant
119
+ // ═════════════════════════════════════════════════════════════════════════════
120
+ export class GpuAccountant {
121
+ runtime;
122
+ cloudEnv;
123
+ hooks;
124
+ _frozen = false;
125
+ _scope = null;
126
+ _initialPids = new Set();
127
+ // Decision #8: per-device-per-PID lastSeenTimeStamp persisted across calls.
128
+ _initialTimestamps = {};
129
+ _deviceIndexes = [];
130
+ _deviceProductNames = [];
131
+ _deviceMigModes = [];
132
+ // Per-device peak VRAM tracker.
133
+ _vramTotal = {};
134
+ _vramUsedPeak = {};
135
+ // Per-device PID set observed across the task.
136
+ _pidsTouchedPerDevice = {};
137
+ constructor(runtime, cloudEnv, hooks) {
138
+ this.runtime = runtime;
139
+ this.cloudEnv = cloudEnv;
140
+ this.hooks = { ...defaultHooks(), ...(hooks ?? {}) };
141
+ }
142
+ /** Initialize NVML, snapshot cgroup PIDs, capture baseline NVML timestamps. */
143
+ snapshotStart() {
144
+ if (!_isNode())
145
+ return;
146
+ if (!this.hooks.initNvml())
147
+ return;
148
+ const count = this.hooks.getDeviceCount() ?? 0;
149
+ if (count <= 0)
150
+ return;
151
+ this._deviceIndexes = Array.from({ length: count }, (_, i) => i);
152
+ for (const i of this._deviceIndexes) {
153
+ const name = this.hooks.getProductName(i);
154
+ this._deviceProductNames.push(name);
155
+ const mig = this.hooks.getMigMode(i);
156
+ this._deviceMigModes.push(mig);
157
+ if (mig) {
158
+ _warnOnce(`gpu_mig_detected_full_billing_applied:device${i}`, `NVML reports MIG enabled on device ${i} (productName=${String(name)}); ` +
159
+ `Decision #2 — full-GPU rate applied. details.mig_profile populated ` +
160
+ `for v1.1 forward-compat.`);
161
+ }
162
+ const mem = this.hooks.getMemoryInfo(i);
163
+ if (mem) {
164
+ this._vramTotal[i] = mem.totalBytes;
165
+ this._vramUsedPeak[i] = mem.usedBytes;
166
+ }
167
+ this._initialTimestamps[i] = {};
168
+ this._pidsTouchedPerDevice[i] = new Set();
169
+ // Baseline NVML sample — captures the per-PID lastSeenTimeStamp.
170
+ const baseline = this.hooks.getProcessUtilization(i, this._initialTimestamps[i]);
171
+ if (baseline) {
172
+ for (const pidKey of Object.keys(baseline)) {
173
+ this._pidsTouchedPerDevice[i].add(Number(pidKey));
174
+ }
175
+ }
176
+ }
177
+ // Snapshot cgroup PIDs (Decision #1 scope classification).
178
+ this._scope = this.hooks.classifyScope();
179
+ const pids = this.hooks.enumeratePids(this._scope);
180
+ this._initialPids = new Set(pids ?? [process.pid]);
181
+ }
182
+ /**
183
+ * Build (gpu_cost_event, [gpu_utilization_signal_events]) at task finalize.
184
+ *
185
+ * Returns nulls when frozen, NVML unavailable, or 0 devices touched.
186
+ */
187
+ snapshotEndAndBuild(durationMs) {
188
+ if (this._frozen)
189
+ return { costDetails: null, signalEvents: null };
190
+ this._frozen = true;
191
+ if (this._deviceIndexes.length === 0) {
192
+ return { costDetails: null, signalEvents: null };
193
+ }
194
+ // End-snapshot cgroup walk + Decision #1 fallback label.
195
+ const scope = this._scope ?? this.hooks.classifyScope();
196
+ const endPidsList = this.hooks.enumeratePids(scope);
197
+ let fallbackLabel;
198
+ let currentPids;
199
+ if (endPidsList === null) {
200
+ fallbackLabel = "self_pid_only";
201
+ currentPids = new Set([process.pid]);
202
+ }
203
+ else {
204
+ fallbackLabel = fallbackLabelFor(scope);
205
+ currentPids = new Set(endPidsList);
206
+ }
207
+ // Union of start + end PIDs.
208
+ const cgroupPidUnion = new Set([
209
+ ...this._initialPids,
210
+ ...currentPids,
211
+ ]);
212
+ // Canonical SKU from first non-null productName (homogeneous-device assumption).
213
+ const canonicalProductName = this._deviceProductNames.find((n) => n !== null && n !== undefined) ?? null;
214
+ const gpuSku = resolveSkuFromProductName(canonicalProductName);
215
+ // Decision #2 transparency: MIG presence surfaced regardless of cgroup-touch.
216
+ let migProfile = null;
217
+ if (this._deviceMigModes.some((m) => m)) {
218
+ migProfile = "mig_detected";
219
+ }
220
+ const degenerateWindow = durationMs <= 0;
221
+ const perDeviceGpuSeconds = {};
222
+ const signalEvents = [];
223
+ let anyPidTouched = false;
224
+ for (const i of this._deviceIndexes) {
225
+ // Sprint 2 Theme C / §3.1.1 (B2 TS port) — snapshot per-PID
226
+ // baseline timestamps BEFORE the end NVML call mutates
227
+ // `_initialTimestamps[i]` in place. Reading the mutated map
228
+ // afterwards would zero every PID's first-sample dt.
229
+ const baselineTsPerPid = {
230
+ ...this._initialTimestamps[i],
231
+ };
232
+ const endSamples = this.hooks.getProcessUtilization(i, this._initialTimestamps[i]) ?? {};
233
+ for (const pidKey of Object.keys(endSamples)) {
234
+ this._pidsTouchedPerDevice[i].add(Number(pidKey));
235
+ }
236
+ const mem = this.hooks.getMemoryInfo(i);
237
+ if (mem) {
238
+ this._vramUsedPeak[i] = Math.max(this._vramUsedPeak[i] ?? 0, mem.usedBytes);
239
+ }
240
+ // Filter to cgroup-PID union. Each value is a list of samples.
241
+ const relevantByPid = {};
242
+ for (const [pidKey, samples] of Object.entries(endSamples)) {
243
+ const pid = Number(pidKey);
244
+ if (cgroupPidUnion.has(pid) && samples.length > 0) {
245
+ relevantByPid[pid] = samples;
246
+ }
247
+ }
248
+ const relevantPidCount = Object.keys(relevantByPid).length;
249
+ if (relevantPidCount > 0) {
250
+ anyPidTouched = true;
251
+ // B2: integrate sm_util × dt across the sample sequence.
252
+ // Two semantics for "first sample of a PID with no baseline":
253
+ // - Device had ZERO PIDs at start → first sample's window
254
+ // extends back to the derived task_start_ts.
255
+ // - Other PIDs were active but this one wasn't → PID
256
+ // joined mid-task; first-sample dt is 0.
257
+ const deviceHadBaselinePids = Object.keys(baselineTsPerPid).length > 0;
258
+ let maxSampleTs = 0;
259
+ for (const samples of Object.values(relevantByPid)) {
260
+ for (const s of samples) {
261
+ if (s.timeStamp > maxSampleTs)
262
+ maxSampleTs = s.timeStamp;
263
+ }
264
+ }
265
+ const taskStartTs = Math.max(0, maxSampleTs - durationMs * 1000);
266
+ let gpuSecondsForDevice = 0;
267
+ let memUtilSum = 0;
268
+ let memUtilN = 0;
269
+ for (const [pidKey, samples] of Object.entries(relevantByPid)) {
270
+ const pid = Number(pidKey);
271
+ let baselineForPid;
272
+ if (pid in baselineTsPerPid) {
273
+ baselineForPid = baselineTsPerPid[pid];
274
+ }
275
+ else if (deviceHadBaselinePids) {
276
+ baselineForPid = samples[0].timeStamp;
277
+ }
278
+ else {
279
+ baselineForPid = taskStartTs;
280
+ }
281
+ let prevTs = baselineForPid;
282
+ for (const s of samples) {
283
+ const dtUs = Math.max(0, s.timeStamp - prevTs);
284
+ gpuSecondsForDevice +=
285
+ (s.smUtil / 100.0) * (dtUs / 1_000_000.0);
286
+ prevTs = s.timeStamp;
287
+ memUtilSum += s.memUtil;
288
+ memUtilN += 1;
289
+ }
290
+ }
291
+ perDeviceGpuSeconds[i] = gpuSecondsForDevice;
292
+ let smUtilPct;
293
+ if (durationMs > 0) {
294
+ const windowS = durationMs / 1000.0;
295
+ smUtilPct = Math.min(100.0, (gpuSecondsForDevice / windowS) * 100.0);
296
+ }
297
+ else {
298
+ smUtilPct = null;
299
+ }
300
+ const memUtilAvg = memUtilN > 0 ? memUtilSum / memUtilN : 0;
301
+ signalEvents.push({
302
+ gpu_index: i,
303
+ gpu_sku: gpuSku,
304
+ sm_util_pct: smUtilPct,
305
+ mem_util_pct: memUtilAvg,
306
+ vram_used_peak_bytes: this._vramUsedPeak[i] ?? 0,
307
+ vram_total_bytes: this._vramTotal[i] ?? 0,
308
+ process_count: this._pidsTouchedPerDevice[i].size,
309
+ sample_count: memUtilN,
310
+ task_duration_ms: durationMs,
311
+ });
312
+ }
313
+ else if (degenerateWindow) {
314
+ signalEvents.push({
315
+ gpu_index: i,
316
+ gpu_sku: gpuSku,
317
+ sm_util_pct: null,
318
+ mem_util_pct: null,
319
+ vram_used_peak_bytes: this._vramUsedPeak[i] ?? 0,
320
+ vram_total_bytes: this._vramTotal[i] ?? 0,
321
+ process_count: this._pidsTouchedPerDevice[i].size,
322
+ sample_count: 0,
323
+ task_duration_ms: durationMs,
324
+ });
325
+ }
326
+ }
327
+ const shouldEmitCost = anyPidTouched ||
328
+ fallbackLabel !== null ||
329
+ degenerateWindow ||
330
+ this._deviceMigModes.some((m) => m);
331
+ if (!shouldEmitCost) {
332
+ return { costDetails: null, signalEvents: null };
333
+ }
334
+ const totalGpuSeconds = Object.values(perDeviceGpuSeconds).reduce((a, b) => a + b, 0);
335
+ const costDetails = this.buildCostEvent({
336
+ durationMs,
337
+ gpuSku,
338
+ gpuCount: this._deviceIndexes.length,
339
+ gpuSecondsUsed: totalGpuSeconds,
340
+ migProfile,
341
+ fallbackLabel,
342
+ });
343
+ return {
344
+ costDetails,
345
+ signalEvents: signalEvents.length > 0 ? signalEvents : null,
346
+ };
347
+ }
348
+ // ------------------------------------------------------------------
349
+ // Event builders
350
+ // ------------------------------------------------------------------
351
+ buildCostEvent(args) {
352
+ const details = {
353
+ billing_model: BILLING_MODEL_FOR_RUNTIME[this.runtime] ?? "per_gpu_second_active",
354
+ gpu_vendor: "nvidia", // Decision #5
355
+ gpu_sku: args.gpuSku,
356
+ gpu_count: args.gpuCount,
357
+ region: this.cloudEnv.region,
358
+ duration_ms: args.durationMs,
359
+ gpu_seconds_used: args.gpuSecondsUsed,
360
+ instance_type: this.cloudEnv.instanceType ?? null,
361
+ vgpu_profile: this.resolveVgpuProfile(),
362
+ mig_profile: args.migProfile,
363
+ cost_pending: true,
364
+ };
365
+ const productName = this._deviceProductNames.find((n) => n !== null && n !== undefined);
366
+ if (productName) {
367
+ details._nvml_product_name_lower = productName;
368
+ }
369
+ if (args.fallbackLabel) {
370
+ details._cgroup_scope_fallback = args.fallbackLabel;
371
+ }
372
+ return details;
373
+ }
374
+ resolveVgpuProfile() {
375
+ if (this.runtime !== GpuRuntimeKind.AzureVmVgpu)
376
+ return null;
377
+ const it = this.cloudEnv.instanceType ?? null;
378
+ if (!it)
379
+ return null;
380
+ return AZURE_VGPU_PROFILE_BY_INSTANCE[it] ?? null;
381
+ }
382
+ }
383
+ //# sourceMappingURL=gpu-accountant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu-accountant.js","sourceRoot":"","sources":["../../src/core/gpu-accountant.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Active GPU runtime resolver — Phase 2 Task 3.
3
+ *
4
+ * Sibling of `compute-runtime.ts`. Coexists without modification —
5
+ * compute-runtime answers "which compute billing model" and gpu-runtime
6
+ * answers "which GPU billing model (if any)".
7
+ *
8
+ * Cascade priority (capture spec §5.5):
9
+ *
10
+ * 1. Serverless GPU env vars (MODAL_TASK_ID / RUNPOD_POD_ID /
11
+ * REPLICATE_MODEL) — win immediately when NVML is available.
12
+ * 2. IaaS GPU via cloud_detect — provider AWS/GCP/Azure AND instanceType
13
+ * matches a GPU-family regex AND NVML reports ≥1 device. Classifies as
14
+ * AWS_EC2_GPU / GCP_GCE_BUNDLED / GCP_GCE_N1_ATTACHED (Decision #9) /
15
+ * AZURE_VM_GPU / AZURE_VM_VGPU (Decision #10).
16
+ * 3. Reserved-GPU providers — Lambda Labs / CoreWeave from cloud_detect
17
+ * AND NVML reports ≥1 device.
18
+ * 4. NONE when NVML unavailable, 0 devices, or unmatched runtime.
19
+ *
20
+ * **String values match Python EXACTLY** — cross-SDK event portability.
21
+ *
22
+ * **TS deviation**: takes an `Options` object with hook functions. This
23
+ * lets tests inject deterministic stubs without monkeypatching module-
24
+ * level imports (ESM bindings are immutable). The default options use the
25
+ * production nvml-reader + cloud-detect modules.
26
+ *
27
+ * Mirrors python/src/dexcost/gpu_runtime.py.
28
+ */
29
+ import { type CloudEnv } from "../cloud-detect.js";
30
+ /** GPU runtime kind discriminator. String values pinned to Python. */
31
+ export declare const GpuRuntimeKind: {
32
+ readonly Modal: "modal";
33
+ readonly RunPod: "runpod";
34
+ readonly Replicate: "replicate";
35
+ readonly LambdaLabs: "lambda_labs";
36
+ readonly CoreWeave: "coreweave";
37
+ readonly AwsEc2Gpu: "aws_ec2_gpu";
38
+ readonly GcpGceBundled: "gcp_gce_bundled";
39
+ readonly GcpGceN1Attached: "gcp_gce_n1_attached";
40
+ readonly AzureVmGpu: "azure_vm_gpu";
41
+ readonly AzureVmVgpu: "azure_vm_vgpu";
42
+ readonly None: "none";
43
+ };
44
+ export type GpuRuntimeKind = (typeof GpuRuntimeKind)[keyof typeof GpuRuntimeKind];
45
+ export interface ResolveGpuRuntimeOptions {
46
+ nvmlAvailable?: () => boolean;
47
+ getDeviceCount?: () => number | null;
48
+ getCloudEnv?: () => CloudEnv;
49
+ }
50
+ /**
51
+ * Return the active GPU runtime, or NONE when there's no GPU.
52
+ *
53
+ * The cascade short-circuits on the FIRST positive match. If NVML can't
54
+ * initialize or reports 0 devices, returns NONE regardless of env-var
55
+ * signals — a Modal task on a CPU-only Modal function emits no GPU events.
56
+ */
57
+ export declare function resolveGpuRuntime(options?: ResolveGpuRuntimeOptions): GpuRuntimeKind;
58
+ //# sourceMappingURL=gpu-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu-runtime.d.ts","sourceRoot":"","sources":["../../src/core/gpu-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAqC,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAMtF,sEAAsE;AACtE,eAAO,MAAM,cAAc;;;;;;;;;;;;CAYjB,CAAC;AAEX,MAAM,MAAM,cAAc,GACxB,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAEvD,MAAM,WAAW,wBAAwB;IACvC,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,QAAQ,CAAC;CAC9B;AA0CD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,CAAC,EAAE,wBAAwB,GACjC,cAAc,CA0ChB"}