@cyanheads/nasa-power-mcp-server 0.4.0 → 0.4.2
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/AGENTS.md +2 -2
- package/CLAUDE.md +2 -2
- package/README.md +9 -5
- package/changelog/0.4.x/0.4.1.md +20 -0
- package/changelog/0.4.x/0.4.2.md +16 -0
- package/dist/config/server-config.d.ts +29 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +29 -0
- package/dist/config/server-config.js.map +1 -1
- package/dist/mcp-server/tools/definitions/comparison-contracts.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/comparison-contracts.js +10 -4
- package/dist/mcp-server/tools/definitions/comparison-contracts.js.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-compare-locations.tool.d.ts +17 -3
- package/dist/mcp-server/tools/definitions/nasa-power-compare-locations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-compare-locations.tool.js +19 -3
- package/dist/mcp-server/tools/definitions/nasa-power-compare-locations.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-describe.tool.d.ts +3 -3
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-describe.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-describe.tool.js +3 -3
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-describe.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-drop.tool.d.ts +3 -3
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-drop.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-drop.tool.js +3 -3
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-drop.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-query.tool.d.ts +3 -3
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-query.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-query.tool.js +3 -3
- package/dist/mcp-server/tools/definitions/nasa-power-dataframe-query.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.d.ts +37 -3
- package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.js +20 -4
- package/dist/mcp-server/tools/definitions/nasa-power-get-climatology.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.d.ts +17 -3
- package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.js +26 -6
- package/dist/mcp-server/tools/definitions/nasa-power-get-region.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-get-timeseries.tool.d.ts +37 -3
- package/dist/mcp-server/tools/definitions/nasa-power-get-timeseries.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-get-timeseries.tool.js +20 -4
- package/dist/mcp-server/tools/definitions/nasa-power-get-timeseries.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-list-reference.tool.js +3 -3
- package/dist/mcp-server/tools/definitions/nasa-power-list-reference.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-search-parameters.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/nasa-power-search-parameters.tool.js +14 -5
- package/dist/mcp-server/tools/definitions/nasa-power-search-parameters.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/point-contracts.d.ts +22 -2
- package/dist/mcp-server/tools/definitions/point-contracts.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/point-contracts.js +16 -6
- package/dist/mcp-server/tools/definitions/point-contracts.js.map +1 -1
- package/dist/services/canvas/canvas-service.d.ts +59 -6
- package/dist/services/canvas/canvas-service.d.ts.map +1 -1
- package/dist/services/canvas/canvas-service.js +172 -42
- package/dist/services/canvas/canvas-service.js.map +1 -1
- package/dist/services/canvas/errors.d.ts +12 -3
- package/dist/services/canvas/errors.d.ts.map +1 -1
- package/dist/services/canvas/errors.js +25 -5
- package/dist/services/canvas/errors.js.map +1 -1
- package/dist/services/canvas/inline-split.d.ts +73 -0
- package/dist/services/canvas/inline-split.d.ts.map +1 -0
- package/dist/services/canvas/inline-split.js +358 -0
- package/dist/services/canvas/inline-split.js.map +1 -0
- package/dist/services/canvas/producer-estimate.d.ts +13 -0
- package/dist/services/canvas/producer-estimate.d.ts.map +1 -0
- package/dist/services/canvas/producer-estimate.js +82 -0
- package/dist/services/canvas/producer-estimate.js.map +1 -0
- package/dist/services/power/normalize.d.ts.map +1 -1
- package/dist/services/power/normalize.js +4 -10
- package/dist/services/power/normalize.js.map +1 -1
- package/dist/services/power/power-service.d.ts +18 -2
- package/dist/services/power/power-service.d.ts.map +1 -1
- package/dist/services/power/power-service.js +89 -52
- package/dist/services/power/power-service.js.map +1 -1
- package/dist/services/power/presets.d.ts +7 -2
- package/dist/services/power/presets.d.ts.map +1 -1
- package/dist/services/power/presets.js +27 -2
- package/dist/services/power/presets.js.map +1 -1
- package/dist/services/power/unserved-parameters.d.ts +11 -0
- package/dist/services/power/unserved-parameters.d.ts.map +1 -0
- package/dist/services/power/unserved-parameters.js +47 -0
- package/dist/services/power/unserved-parameters.js.map +1 -0
- package/package.json +1 -1
- package/server.json +3 -3
- package/skills/nasa-power-guide/SKILL.md +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nasa-power-search-parameters.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/nasa-power-search-parameters.tool.ts"],"names":[],"mappings":"AAAA,6IAA6I;AAC7I,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"nasa-power-search-parameters.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/nasa-power-search-parameters.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
3
3
|
import { z } from '@cyanheads/mcp-ts-core';
|
|
4
4
|
import { type PowerRequest } from '../../../services/power/index.js';
|
|
5
|
-
/** Full scientific envelope with exact row count and optional staged
|
|
5
|
+
/** Full scientific envelope with exact row count and optional staged pointers. */
|
|
6
6
|
export declare const PointOutputSchema: z.ZodObject<{
|
|
7
7
|
request: z.ZodObject<{
|
|
8
8
|
community: z.ZodDefault<z.ZodEnum<{
|
|
@@ -175,7 +175,27 @@ export declare const PointOutputSchema: z.ZodObject<{
|
|
|
175
175
|
describeTool: z.ZodLiteral<"nasa_power_dataframe_describe">;
|
|
176
176
|
queryTool: z.ZodLiteral<"nasa_power_dataframe_query">;
|
|
177
177
|
}, z.core.$strip>>;
|
|
178
|
+
summaryCount: z.ZodOptional<z.ZodNumber>;
|
|
179
|
+
summaryDataset: z.ZodOptional<z.ZodObject<{
|
|
180
|
+
canvas_id: z.ZodString;
|
|
181
|
+
table: z.ZodString;
|
|
182
|
+
rowCount: z.ZodNumber;
|
|
183
|
+
expiresAt: z.ZodString;
|
|
184
|
+
columns: z.ZodArray<z.ZodObject<{
|
|
185
|
+
name: z.ZodString;
|
|
186
|
+
type: z.ZodString;
|
|
187
|
+
nullable: z.ZodBoolean;
|
|
188
|
+
units: z.ZodOptional<z.ZodString>;
|
|
189
|
+
valueKind: z.ZodOptional<z.ZodEnum<{
|
|
190
|
+
categorical: "categorical";
|
|
191
|
+
number: "number";
|
|
192
|
+
orientation: "orientation";
|
|
193
|
+
}>>;
|
|
194
|
+
}, z.core.$strip>>;
|
|
195
|
+
describeTool: z.ZodLiteral<"nasa_power_dataframe_describe">;
|
|
196
|
+
queryTool: z.ZodLiteral<"nasa_power_dataframe_query">;
|
|
197
|
+
}, z.core.$strip>>;
|
|
178
198
|
}, z.core.$strip>;
|
|
179
|
-
/** Source retrieval and presentation share one producer reservation and deadline
|
|
199
|
+
/** Source retrieval and presentation share one producer reservation and the deadline started at tool entry. */
|
|
180
200
|
export declare function presentPoint(request: PowerRequest, canvasId: string | undefined, ctx: Context, sourceTool: string): Promise<import("../../../services/canvas/index.js").PresentedDataset<import("../../../services/power/index.js").PowerDataset>>;
|
|
181
201
|
//# sourceMappingURL=point-contracts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"point-contracts.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/point-contracts.ts"],"names":[],"mappings":"AAAA,sHAAsH;AACtH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"point-contracts.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/point-contracts.ts"],"names":[],"mappings":"AAAA,sHAAsH;AACtH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,2BAA2B,CAAC;AAEnC,kFAAkF;AAClF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAa5B,CAAC;AAEH,+GAA+G;AAC/G,wBAAgB,YAAY,CAC1B,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,GAAG,EAAE,OAAO,EACZ,UAAU,EAAE,MAAM,oHAkBnB"}
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
import { z } from '@cyanheads/mcp-ts-core';
|
|
2
2
|
import { getCanvasService } from '../../../services/canvas/index.js';
|
|
3
|
+
import { estimatePointBytes } from '../../../services/canvas/producer-estimate.js';
|
|
3
4
|
import { DatasetHandleSchema, getPowerService, PowerDatasetSchema, } from '../../../services/power/index.js';
|
|
4
|
-
/** Full scientific envelope with exact row count and optional staged
|
|
5
|
+
/** Full scientific envelope with exact row count and optional staged pointers. */
|
|
5
6
|
export const PointOutputSchema = PowerDatasetSchema.extend({
|
|
6
7
|
rowCount: z.number().int().describe('Exact full normalized observation count'),
|
|
7
8
|
dataset: DatasetHandleSchema.nullable().describe('Full-table handle when observations exceed inline budget; null otherwise'),
|
|
9
|
+
summaryCount: z
|
|
10
|
+
.number()
|
|
11
|
+
.int()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Exact complete summary count when summaries are staged; summaries then holds one'),
|
|
14
|
+
summaryDataset: DatasetHandleSchema.optional().describe('Complete summaries ordered by summaryIndex; SQL NULL units restore an omitted source unit'),
|
|
8
15
|
});
|
|
9
|
-
/** Source retrieval and presentation share one producer reservation and deadline
|
|
16
|
+
/** Source retrieval and presentation share one producer reservation and the deadline started at tool entry. */
|
|
10
17
|
export function presentPoint(request, canvasId, ctx, sourceTool) {
|
|
11
|
-
|
|
18
|
+
const canvas = getCanvasService();
|
|
19
|
+
return canvas.withProducer(estimatePointBytes(request), ctx, async (producerCtx, lease) => {
|
|
12
20
|
const power = getPowerService();
|
|
13
|
-
const budget = power.createCallBudget(producerCtx
|
|
21
|
+
const budget = power.createCallBudget(producerCtx, false, {
|
|
22
|
+
deadlineMs: lease.remainingMs(),
|
|
23
|
+
onHeldBytes: lease.bodyBytes,
|
|
24
|
+
});
|
|
14
25
|
const callCtx = { ...producerCtx, signal: budget.signal };
|
|
15
|
-
|
|
16
|
-
return getCanvasService().present(dataset, canvasId, callCtx, sourceTool);
|
|
26
|
+
return canvas.presentPoint(await power.point(request, callCtx, budget), canvasId, callCtx, sourceTool, lease);
|
|
17
27
|
});
|
|
18
28
|
}
|
|
19
29
|
//# sourceMappingURL=point-contracts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"point-contracts.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/point-contracts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,GAEnB,MAAM,2BAA2B,CAAC;AAEnC,
|
|
1
|
+
{"version":3,"file":"point-contracts.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/point-contracts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,GAEnB,MAAM,2BAA2B,CAAC;AAEnC,kFAAkF;AAClF,MAAM,CAAC,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,MAAM,CAAC;IACzD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;IAC9E,OAAO,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAC9C,0EAA0E,CAC3E;IACD,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,CAAC,kFAAkF,CAAC;IAC/F,cAAc,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,QAAQ,CACrD,2FAA2F,CAC5F;CACF,CAAC,CAAC;AAEH,+GAA+G;AAC/G,MAAM,UAAU,YAAY,CAC1B,OAAqB,EACrB,QAA4B,EAC5B,GAAY,EACZ,UAAkB;IAElB,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAClC,OAAO,MAAM,CAAC,YAAY,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE;QACxF,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,KAAK,CAAC,gBAAgB,CAAC,WAAW,EAAE,KAAK,EAAE;YACxD,UAAU,EAAE,KAAK,CAAC,WAAW,EAAE;YAC/B,WAAW,EAAE,KAAK,CAAC,SAAS;SAC7B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QAC1D,OAAO,MAAM,CAAC,YAAY,CACxB,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAC3C,QAAQ,EACR,OAAO,EACP,UAAU,EACV,KAAK,CACN,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -3,13 +3,36 @@ import type { DataCanvas } from '@cyanheads/mcp-ts-core/canvas';
|
|
|
3
3
|
import type { AppConfig } from '@cyanheads/mcp-ts-core/config';
|
|
4
4
|
import type { StorageService } from '@cyanheads/mcp-ts-core/storage';
|
|
5
5
|
import { type ServerConfig } from '../../config/server-config.js';
|
|
6
|
+
import { type PowerDataset } from '../power/index.js';
|
|
7
|
+
import { type ComparisonShape, type RegionShape, type SplitPlanner } from './inline-split.js';
|
|
6
8
|
import { type ComparisonDataset, type PresentableDataset, type PresentedDataset, type RegionalSummaryDataset, type ScientificColumn } from './types.js';
|
|
7
9
|
type SummaryTable = {
|
|
8
|
-
|
|
10
|
+
/** `summaries` returns as `summaryDataset`; `buckets` and `locations` as `summaryDatasets`. */
|
|
11
|
+
key: 'buckets' | 'locations' | 'summaries';
|
|
9
12
|
table: string;
|
|
10
13
|
rows: Record<string, string | number | boolean | null | undefined>[];
|
|
11
14
|
columns: ScientificColumn[];
|
|
12
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* One admitted producing call. Its deadline started at tool entry, so admission wait, fetch,
|
|
18
|
+
* normalization and staging share it. The reservation starts at the caller's estimate and grows
|
|
19
|
+
* atomically when measured bytes exceed it; growth that would breach the tenant or process
|
|
20
|
+
* in-flight ceiling aborts the call with `producer_capacity_exhausted`.
|
|
21
|
+
*/
|
|
22
|
+
export type ProducerLease = {
|
|
23
|
+
/** Milliseconds left of the call deadline. */
|
|
24
|
+
remainingMs(): number;
|
|
25
|
+
/** Record the decoded upstream body bytes this call holds now; a discarded attempt's body is not held. */
|
|
26
|
+
bodyBytes(total: number): void;
|
|
27
|
+
/** Record the normalized row bytes this call holds so far. */
|
|
28
|
+
rowBytes(total: number): void;
|
|
29
|
+
};
|
|
30
|
+
/** Presentation inputs beyond the dataset; producers pass their lease and split planner. */
|
|
31
|
+
export type PresentOptions = {
|
|
32
|
+
lease?: ProducerLease | undefined;
|
|
33
|
+
/** Sizes the `canvas_required` refusal's split; defaults to the inline row-prefix capacity alone. */
|
|
34
|
+
split?: SplitPlanner;
|
|
35
|
+
};
|
|
13
36
|
/** Service dependencies; native lifecycle settings are supplied to the framework at startup. */
|
|
14
37
|
export type CanvasServiceOptions = {
|
|
15
38
|
canvas?: DataCanvas;
|
|
@@ -28,6 +51,7 @@ export declare class CanvasService {
|
|
|
28
51
|
private readonly canvasLimits;
|
|
29
52
|
private readonly records;
|
|
30
53
|
private readonly producers;
|
|
54
|
+
private readonly waiters;
|
|
31
55
|
private readonly operations;
|
|
32
56
|
private readonly shutdownController;
|
|
33
57
|
private queryCount;
|
|
@@ -37,15 +61,44 @@ export declare class CanvasService {
|
|
|
37
61
|
constructor(options: CanvasServiceOptions);
|
|
38
62
|
/** Availability is a configuration fact; native initialization can still fail. */
|
|
39
63
|
get enabled(): boolean;
|
|
40
|
-
/**
|
|
41
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Run one producing call under an in-flight reservation of `estimateBytes`, an upper bound the
|
|
66
|
+
* caller computes from its request before fetching (see `producer-estimate.ts`). The call's
|
|
67
|
+
* deadline starts here, at tool entry. When the producer count or the tenant or process byte
|
|
68
|
+
* ceiling is exhausted, admission waits up to the queue wait inside that deadline, admitting
|
|
69
|
+
* waiters in arrival order as capacity frees. A call that fits may overtake a waiting one until
|
|
70
|
+
* the oldest waiter has waited the overtake window; after that, calls queue behind it, so it
|
|
71
|
+
* cannot be starved by smaller calls that keep its bytes in use. A call still waiting is refused with
|
|
72
|
+
* `producer_capacity_exhausted`. A waiting call aborted by its caller rejects with the caller's
|
|
73
|
+
* reason and holds no reservation; shutdown rejects every waiter.
|
|
74
|
+
*/
|
|
75
|
+
withProducer<T>(estimateBytes: number, ctx: Context, run: (ctx: Context, lease: ProducerLease) => Promise<T>): Promise<T>;
|
|
76
|
+
/** Reserve atomically when both counts and byte ceilings allow it; otherwise nothing changes. */
|
|
77
|
+
private tryAdmit;
|
|
78
|
+
/** Admit at once, or queue until capacity frees, the caller aborts, the wait ends, or shutdown. */
|
|
79
|
+
private admit;
|
|
80
|
+
/** Whether a waiter has waited long enough that later calls may no longer overtake it. */
|
|
81
|
+
private overtakeClosed;
|
|
82
|
+
/**
|
|
83
|
+
* Release and waiter-exit path: admit each queued call, in arrival order, that now fits,
|
|
84
|
+
* stopping at a waiter that does not fit once later calls may no longer overtake it.
|
|
85
|
+
*/
|
|
86
|
+
private admitWaiters;
|
|
42
87
|
/** Stage comparison summaries with observations only when the scientific envelope overflows. */
|
|
43
|
-
presentComparison<T extends ComparisonDataset>(dataset: T, canvasId: string | undefined, ctx: Context, sourceTool: string): Promise<PresentedDataset<T>>;
|
|
88
|
+
presentComparison<T extends ComparisonDataset & ComparisonShape>(dataset: T, canvasId: string | undefined, ctx: Context, sourceTool: string, lease?: ProducerLease): Promise<PresentedDataset<T>>;
|
|
44
89
|
/** Stage complete cross-cell summaries when the no-observation regional envelope overflows. */
|
|
45
|
-
presentRegion<T extends RegionalSummaryDataset>(dataset: T, canvasId: string | undefined, ctx: Context, sourceTool: string): Promise<PresentedDataset<T>>;
|
|
90
|
+
presentRegion<T extends RegionalSummaryDataset & RegionShape>(dataset: T, canvasId: string | undefined, ctx: Context, sourceTool: string, lease?: ProducerLease): Promise<PresentedDataset<T>>;
|
|
91
|
+
/** Stage complete per-column summaries when the no-observation point envelope overflows. */
|
|
92
|
+
presentPoint<T extends PowerDataset>(dataset: T, canvasId: string | undefined, ctx: Context, sourceTool: string, lease?: ProducerLease): Promise<PresentedDataset<T>>;
|
|
93
|
+
/**
|
|
94
|
+
* Present unchanged when the envelope without observations fits. Otherwise one summary stays
|
|
95
|
+
* inline beside the exact `summaryCount`, and the complete summaries stage as a related table
|
|
96
|
+
* ordered by `summaryIndex`. The split planner always sees the complete dataset.
|
|
97
|
+
*/
|
|
98
|
+
private presentSummarized;
|
|
46
99
|
private summaryTable;
|
|
47
100
|
/** Preserve the scientific envelope with complete staging and bounded whole-row previews. */
|
|
48
|
-
present<T extends PresentableDataset>(dataset: T, canvasId: string | undefined, ctx: Context, sourceTool: string, related?: SummaryTable[]): Promise<PresentedDataset<T>>;
|
|
101
|
+
present<T extends PresentableDataset>(dataset: T, canvasId: string | undefined, ctx: Context, sourceTool: string, options?: PresentOptions, related?: SummaryTable[]): Promise<PresentedDataset<T>>;
|
|
49
102
|
/** Inspect only the supplied canvas, including original scientific provenance. */
|
|
50
103
|
describe(canvasId: string, input: {
|
|
51
104
|
table?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-service.d.ts","sourceRoot":"","sources":["../../../src/services/canvas/canvas-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAGV,UAAU,EAEX,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"canvas-service.d.ts","sourceRoot":"","sources":["../../../src/services/canvas/canvas-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ import { POWER_LIMITS } from '../../config/server-config.js';
|
|
|
5
5
|
import { powerError } from '../power/index.js';
|
|
6
6
|
import { canvasError, safeCanvasError } from './errors.js';
|
|
7
7
|
import { formatFaithfully } from './format.js';
|
|
8
|
+
import { comparisonSplit, pointSplit, prefixSplit, regionSplit, splitRecovery, } from './inline-split.js';
|
|
8
9
|
import { ProvenanceSchema, QueryResultSchema, } from './types.js';
|
|
9
10
|
/** Explicit token access, atomic admission, and native-lifecycle reconciliation. */
|
|
10
11
|
export class CanvasService {
|
|
@@ -15,6 +16,7 @@ export class CanvasService {
|
|
|
15
16
|
canvasLimits;
|
|
16
17
|
records = new Map();
|
|
17
18
|
producers = new Set();
|
|
19
|
+
waiters = [];
|
|
18
20
|
operations = new Set();
|
|
19
21
|
shutdownController = new AbortController();
|
|
20
22
|
queryCount = 0;
|
|
@@ -41,48 +43,151 @@ export class CanvasService {
|
|
|
41
43
|
get enabled() {
|
|
42
44
|
return this.canvas !== undefined;
|
|
43
45
|
}
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Run one producing call under an in-flight reservation of `estimateBytes`, an upper bound the
|
|
48
|
+
* caller computes from its request before fetching (see `producer-estimate.ts`). The call's
|
|
49
|
+
* deadline starts here, at tool entry. When the producer count or the tenant or process byte
|
|
50
|
+
* ceiling is exhausted, admission waits up to the queue wait inside that deadline, admitting
|
|
51
|
+
* waiters in arrival order as capacity frees. A call that fits may overtake a waiting one until
|
|
52
|
+
* the oldest waiter has waited the overtake window; after that, calls queue behind it, so it
|
|
53
|
+
* cannot be starved by smaller calls that keep its bytes in use. A call still waiting is refused with
|
|
54
|
+
* `producer_capacity_exhausted`. A waiting call aborted by its caller rejects with the caller's
|
|
55
|
+
* reason and holds no reservation; shutdown rejects every waiter.
|
|
56
|
+
*/
|
|
57
|
+
async withProducer(estimateBytes, ctx, run) {
|
|
46
58
|
this.assertRunning();
|
|
47
59
|
ctx.signal.throwIfAborted();
|
|
48
|
-
const
|
|
49
|
-
|
|
60
|
+
const tenant = this.tenant(ctx);
|
|
61
|
+
const entered = this.now();
|
|
62
|
+
const deadline = new AbortController();
|
|
63
|
+
const timer = setTimeout(() => deadline.abort(new DOMException('The operation timed out.', 'TimeoutError')), POWER_LIMITS.deadlineMs);
|
|
64
|
+
let producer;
|
|
65
|
+
try {
|
|
66
|
+
producer = await this.admit(tenant, estimateBytes, ctx.signal);
|
|
67
|
+
const admitted = producer;
|
|
68
|
+
const signal = AbortSignal.any([
|
|
69
|
+
ctx.signal,
|
|
70
|
+
admitted.controller.signal,
|
|
71
|
+
this.shutdownController.signal,
|
|
72
|
+
deadline.signal,
|
|
73
|
+
]);
|
|
74
|
+
const measured = { body: 0, rows: 0 };
|
|
75
|
+
const grow = () => {
|
|
76
|
+
const total = measured.body + measured.rows;
|
|
77
|
+
if (total <= admitted.bytes)
|
|
78
|
+
return;
|
|
79
|
+
const others = [...this.producers].filter((item) => item !== admitted);
|
|
80
|
+
const sum = (items) => items.reduce((acc, item) => acc + item.bytes, 0);
|
|
81
|
+
if (sum(others) + total > POWER_LIMITS.processInflightBytes ||
|
|
82
|
+
sum(others.filter((item) => item.tenant === admitted.tenant)) + total >
|
|
83
|
+
POWER_LIMITS.tenantInflightBytes) {
|
|
84
|
+
const error = canvasError('producer_capacity_exhausted', 'This data call outgrew its in-flight reservation while other data calls hold the remaining capacity.');
|
|
85
|
+
admitted.controller.abort(error);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
admitted.bytes = total;
|
|
89
|
+
};
|
|
90
|
+
return await run({ ...ctx, signal }, {
|
|
91
|
+
remainingMs: () => Math.max(0, entered + POWER_LIMITS.deadlineMs - this.now()),
|
|
92
|
+
bodyBytes: (total) => {
|
|
93
|
+
measured.body = total;
|
|
94
|
+
grow();
|
|
95
|
+
},
|
|
96
|
+
rowBytes: (total) => {
|
|
97
|
+
measured.rows = total;
|
|
98
|
+
grow();
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
clearTimeout(timer);
|
|
104
|
+
if (producer) {
|
|
105
|
+
this.producers.delete(producer);
|
|
106
|
+
producer.finish();
|
|
107
|
+
this.admitWaiters();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Reserve atomically when both counts and byte ceilings allow it; otherwise nothing changes. */
|
|
112
|
+
tryAdmit(tenant, bytes) {
|
|
50
113
|
const active = [...this.producers];
|
|
51
|
-
const total = active.reduce((sum,
|
|
52
|
-
const
|
|
53
|
-
.filter((
|
|
54
|
-
.reduce((sum,
|
|
114
|
+
const total = active.reduce((sum, item) => sum + item.bytes, 0);
|
|
115
|
+
const owned = active
|
|
116
|
+
.filter((item) => item.tenant === tenant)
|
|
117
|
+
.reduce((sum, item) => sum + item.bytes, 0);
|
|
55
118
|
if (active.length >= POWER_LIMITS.producers ||
|
|
56
119
|
total + bytes > POWER_LIMITS.processInflightBytes ||
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const controller = new AbortController();
|
|
60
|
-
const signal = AbortSignal.any([
|
|
61
|
-
ctx.signal,
|
|
62
|
-
controller.signal,
|
|
63
|
-
this.shutdownController.signal,
|
|
64
|
-
AbortSignal.timeout(POWER_LIMITS.deadlineMs),
|
|
65
|
-
]);
|
|
120
|
+
owned + bytes > POWER_LIMITS.tenantInflightBytes)
|
|
121
|
+
return undefined;
|
|
66
122
|
const { promise: settled, resolve: finish } = Promise.withResolvers();
|
|
67
123
|
const producer = {
|
|
68
|
-
tenant
|
|
124
|
+
tenant,
|
|
69
125
|
bytes,
|
|
70
|
-
controller,
|
|
126
|
+
controller: new AbortController(),
|
|
71
127
|
settled,
|
|
128
|
+
finish,
|
|
72
129
|
};
|
|
73
130
|
this.producers.add(producer);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
131
|
+
return producer;
|
|
132
|
+
}
|
|
133
|
+
/** Admit at once, or queue until capacity frees, the caller aborts, the wait ends, or shutdown. */
|
|
134
|
+
admit(tenant, bytes, signal) {
|
|
135
|
+
const head = this.waiters[0];
|
|
136
|
+
const immediate = !head || !this.overtakeClosed(head) ? this.tryAdmit(tenant, bytes) : undefined;
|
|
137
|
+
if (immediate)
|
|
138
|
+
return Promise.resolve(immediate);
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
const settle = () => {
|
|
141
|
+
clearTimeout(timer);
|
|
142
|
+
signal.removeEventListener('abort', onAbort);
|
|
143
|
+
this.waiters.splice(this.waiters.indexOf(waiter), 1);
|
|
144
|
+
};
|
|
145
|
+
const waiter = {
|
|
146
|
+
tenant,
|
|
147
|
+
bytes,
|
|
148
|
+
since: this.now(),
|
|
149
|
+
admit: (producer) => {
|
|
150
|
+
settle();
|
|
151
|
+
resolve(producer);
|
|
152
|
+
},
|
|
153
|
+
refuse: (error) => {
|
|
154
|
+
settle();
|
|
155
|
+
reject(error);
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
/** A waiter that leaves may have been the head holding later calls that already fit. */
|
|
159
|
+
const leave = (error) => {
|
|
160
|
+
waiter.refuse(error);
|
|
161
|
+
this.admitWaiters();
|
|
162
|
+
};
|
|
163
|
+
const onAbort = () => leave(signal.reason);
|
|
164
|
+
const timer = setTimeout(() => leave(canvasError('producer_capacity_exhausted', 'Other data calls held the producer capacity for the whole admission wait.')), POWER_LIMITS.queueWaitMs);
|
|
165
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
166
|
+
this.waiters.push(waiter);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/** Whether a waiter has waited long enough that later calls may no longer overtake it. */
|
|
170
|
+
overtakeClosed(waiter) {
|
|
171
|
+
return this.now() - waiter.since >= POWER_LIMITS.queueOvertakeMs;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Release and waiter-exit path: admit each queued call, in arrival order, that now fits,
|
|
175
|
+
* stopping at a waiter that does not fit once later calls may no longer overtake it.
|
|
176
|
+
*/
|
|
177
|
+
admitWaiters() {
|
|
178
|
+
for (const waiter of [...this.waiters]) {
|
|
179
|
+
const producer = this.tryAdmit(waiter.tenant, waiter.bytes);
|
|
180
|
+
if (producer)
|
|
181
|
+
waiter.admit(producer);
|
|
182
|
+
else if (this.overtakeClosed(waiter))
|
|
183
|
+
return;
|
|
80
184
|
}
|
|
81
185
|
}
|
|
82
186
|
/** Stage comparison summaries with observations only when the scientific envelope overflows. */
|
|
83
|
-
presentComparison(dataset, canvasId, ctx, sourceTool) {
|
|
187
|
+
presentComparison(dataset, canvasId, ctx, sourceTool, lease) {
|
|
188
|
+
const options = { lease, split: comparisonSplit(dataset) };
|
|
84
189
|
if (this.fits({ ...dataset, rows: [], rowCount: dataset.rows.length, dataset: null }))
|
|
85
|
-
return this.present(dataset, canvasId, ctx, sourceTool);
|
|
190
|
+
return this.present(dataset, canvasId, ctx, sourceTool, options);
|
|
86
191
|
const summaryRows = {
|
|
87
192
|
buckets: dataset.summaries.map((summary, summaryIndex) => ({ ...summary, summaryIndex })),
|
|
88
193
|
locations: dataset.locations
|
|
@@ -99,18 +204,36 @@ export class CanvasService {
|
|
|
99
204
|
summaryCount: location.summaries.length,
|
|
100
205
|
})),
|
|
101
206
|
};
|
|
102
|
-
return this.present(compact, canvasId, ctx, sourceTool, tables);
|
|
207
|
+
return this.present(compact, canvasId, ctx, sourceTool, options, tables);
|
|
103
208
|
}
|
|
104
209
|
/** Stage complete cross-cell summaries when the no-observation regional envelope overflows. */
|
|
105
|
-
presentRegion(dataset, canvasId, ctx, sourceTool) {
|
|
210
|
+
presentRegion(dataset, canvasId, ctx, sourceTool, lease) {
|
|
211
|
+
return this.presentSummarized(dataset, canvasId, ctx, sourceTool, {
|
|
212
|
+
lease,
|
|
213
|
+
split: regionSplit(dataset),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/** Stage complete per-column summaries when the no-observation point envelope overflows. */
|
|
217
|
+
presentPoint(dataset, canvasId, ctx, sourceTool, lease) {
|
|
218
|
+
return this.presentSummarized(dataset, canvasId, ctx, sourceTool, {
|
|
219
|
+
lease,
|
|
220
|
+
split: pointSplit(dataset),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Present unchanged when the envelope without observations fits. Otherwise one summary stays
|
|
225
|
+
* inline beside the exact `summaryCount`, and the complete summaries stage as a related table
|
|
226
|
+
* ordered by `summaryIndex`. The split planner always sees the complete dataset.
|
|
227
|
+
*/
|
|
228
|
+
presentSummarized(dataset, canvasId, ctx, sourceTool, options) {
|
|
106
229
|
if (this.fits({ ...dataset, rows: [], rowCount: dataset.rows.length, dataset: null }))
|
|
107
|
-
return this.present(dataset, canvasId, ctx, sourceTool);
|
|
108
|
-
const summaries = this.summaryTable('
|
|
230
|
+
return this.present(dataset, canvasId, ctx, sourceTool, options);
|
|
231
|
+
const summaries = this.summaryTable('summaries', dataset.summaries.map((summary, summaryIndex) => ({ ...summary, summaryIndex })));
|
|
109
232
|
return this.present({
|
|
110
233
|
...dataset,
|
|
111
234
|
summaries: dataset.summaries.slice(0, 1),
|
|
112
235
|
summaryCount: dataset.summaries.length,
|
|
113
|
-
}, canvasId, ctx, sourceTool, [summaries]);
|
|
236
|
+
}, canvasId, ctx, sourceTool, options, [summaries]);
|
|
114
237
|
}
|
|
115
238
|
summaryTable(key, rows) {
|
|
116
239
|
const strings = new Set([
|
|
@@ -146,7 +269,7 @@ export class CanvasService {
|
|
|
146
269
|
};
|
|
147
270
|
}
|
|
148
271
|
/** Preserve the scientific envelope with complete staging and bounded whole-row previews. */
|
|
149
|
-
async present(dataset, canvasId, ctx, sourceTool, related = []) {
|
|
272
|
+
async present(dataset, canvasId, ctx, sourceTool, options = {}, related = []) {
|
|
150
273
|
this.assertRunning();
|
|
151
274
|
ctx.signal.throwIfAborted();
|
|
152
275
|
const serialized = JSON.stringify(dataset.rows);
|
|
@@ -157,6 +280,7 @@ export class CanvasService {
|
|
|
157
280
|
related.some((item) => item.rows.length > POWER_LIMITS.rows) ||
|
|
158
281
|
dataset.parameters.length > POWER_LIMITS.columns)
|
|
159
282
|
throw powerError('dataset_limit', 'Normalized dataset exceeds the analytical workload limit.');
|
|
283
|
+
options.lease?.rowBytes(bytes + relatedBytes);
|
|
160
284
|
const inline = { ...dataset, rowCount: dataset.rows.length, dataset: null };
|
|
161
285
|
if (!related.length && this.fits(inline))
|
|
162
286
|
return inline;
|
|
@@ -174,10 +298,10 @@ export class CanvasService {
|
|
|
174
298
|
queryTool: 'nasa_power_dataframe_query',
|
|
175
299
|
});
|
|
176
300
|
const relatedHandles = (id, expiresAt) => {
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
301
|
+
const summaries = related.find((item) => item.key === 'summaries');
|
|
302
|
+
if (summaries)
|
|
179
303
|
return {
|
|
180
|
-
summaryDataset: handle(id, expiresAt,
|
|
304
|
+
summaryDataset: handle(id, expiresAt, summaries.table, summaries.rows.length, summaries.columns),
|
|
181
305
|
};
|
|
182
306
|
const buckets = related.find((item) => item.key === 'buckets');
|
|
183
307
|
const locations = related.find((item) => item.key === 'locations');
|
|
@@ -199,8 +323,11 @@ export class CanvasService {
|
|
|
199
323
|
});
|
|
200
324
|
const preview = this.preview(dataset.rows, (rows) => output('0000000000', new Date(this.now()).toISOString(), rows));
|
|
201
325
|
const canvas = this.canvas;
|
|
202
|
-
if (!canvas)
|
|
203
|
-
|
|
326
|
+
if (!canvas) {
|
|
327
|
+
const rowCount = dataset.rows.length;
|
|
328
|
+
const split = (options.split ?? prefixSplit(dataset))((output) => this.fits(output));
|
|
329
|
+
throw canvasError('canvas_required', 'This complete dataset requires an analytical workspace.', splitRecovery(rowCount, split), { rowCount, inlineRows: split.inlineRows, calls: split.calls });
|
|
330
|
+
}
|
|
204
331
|
await this.reconcile();
|
|
205
332
|
ctx.signal.throwIfAborted();
|
|
206
333
|
const record = this.reserve(canvasId, ctx, table, bytes, columns, dataset, sourceTool, related);
|
|
@@ -293,7 +420,7 @@ export class CanvasService {
|
|
|
293
420
|
? 'The effective row limit is 1000; larger requested limits are capped.'
|
|
294
421
|
: undefined;
|
|
295
422
|
if (this.queryCount >= POWER_LIMITS.queries)
|
|
296
|
-
throw canvasError('
|
|
423
|
+
throw canvasError('canvas_busy', 'Analytical query concurrency is exhausted.');
|
|
297
424
|
this.queryCount++;
|
|
298
425
|
try {
|
|
299
426
|
return await this.operate(canvasId, ctx, async (instance) => {
|
|
@@ -358,6 +485,8 @@ export class CanvasService {
|
|
|
358
485
|
this.stopped = true;
|
|
359
486
|
clearInterval(this.timer);
|
|
360
487
|
this.shutdownController.abort();
|
|
488
|
+
for (const waiter of [...this.waiters])
|
|
489
|
+
waiter.refuse(new McpError(JsonRpcErrorCode.ServiceUnavailable, 'Analytical workspace is shutting down.'));
|
|
361
490
|
for (const producer of this.producers)
|
|
362
491
|
producer.controller.abort();
|
|
363
492
|
await Promise.allSettled([...this.producers].map((producer) => producer.settled));
|
|
@@ -446,10 +575,11 @@ export class CanvasService {
|
|
|
446
575
|
throw canvasError('canvas_capacity_exhausted', 'The process or tenant canvas-count limit is exhausted.', this.config.dataframeDropEnabled
|
|
447
576
|
? 'Reuse an explicit live canvas_id, drop all tables from a canvas, or wait for canvas expiry.'
|
|
448
577
|
: 'Reuse an explicit live canvas_id, or wait for canvas expiry.');
|
|
578
|
+
if (record?.busy)
|
|
579
|
+
throw canvasError('canvas_busy', 'Another operation is active on this canvas.');
|
|
449
580
|
if (retained(records) + bytes + relatedBytes > POWER_LIMITS.processRetainedBytes ||
|
|
450
581
|
retained(tenantRecords) + bytes + relatedBytes > POWER_LIMITS.tenantRetainedBytes ||
|
|
451
|
-
(record &&
|
|
452
|
-
(record.busy || record.tables.size + 1 + related.length > POWER_LIMITS.tablesPerCanvas)))
|
|
582
|
+
(record && record.tables.size + 1 + related.length > POWER_LIMITS.tablesPerCanvas))
|
|
453
583
|
throw canvasError('canvas_capacity_exhausted', 'Analytical workspace memory or table capacity is exhausted.');
|
|
454
584
|
const { rows: _rows, ...envelope } = dataset;
|
|
455
585
|
const provenance = ProvenanceSchema.parse({
|
|
@@ -582,7 +712,7 @@ export class CanvasService {
|
|
|
582
712
|
if (!record || record.tenant !== ctx.tenantId || record.pending)
|
|
583
713
|
throw canvasError('canvas_not_found', 'Canvas not found or expired.', drop ? 'The canvas has expired or is absent; there is nothing left to remove.' : undefined);
|
|
584
714
|
if (record.busy)
|
|
585
|
-
throw canvasError('
|
|
715
|
+
throw canvasError('canvas_busy', 'Another operation is active on this canvas.');
|
|
586
716
|
record.busy = true;
|
|
587
717
|
const operation = (async () => {
|
|
588
718
|
try {
|