@hypequery/datasets 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/catalog.d.ts +68 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +105 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +15 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/tools.d.ts +53 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +322 -0
- package/package.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AnyDatasetInstance, DimensionDefinition, MeasureDefinition, MetricHandle, RelationshipDefinition, SemanticFilterDefinition, DatasetLimits, TimeGrain } from './types.js';
|
|
2
|
+
export interface DimensionCatalogEntry {
|
|
3
|
+
type: DimensionDefinition['fieldType'];
|
|
4
|
+
column?: string;
|
|
5
|
+
sql?: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
filterable: boolean;
|
|
9
|
+
groupable: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface MeasureCatalogEntry {
|
|
12
|
+
aggregation: MeasureDefinition['aggregation'];
|
|
13
|
+
field: string;
|
|
14
|
+
sql?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
filterCount: number;
|
|
18
|
+
}
|
|
19
|
+
export interface FilterCatalogEntry {
|
|
20
|
+
field: string;
|
|
21
|
+
label?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
operators: SemanticFilterDefinition['operators'];
|
|
24
|
+
valueType?: DimensionDefinition['fieldType'];
|
|
25
|
+
}
|
|
26
|
+
export interface MetricCatalogEntry {
|
|
27
|
+
kind: ReturnType<MetricHandle['contract']>['kind'];
|
|
28
|
+
dataset: string;
|
|
29
|
+
valueType: 'number';
|
|
30
|
+
label?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
dimensions: string[];
|
|
33
|
+
measures?: string[];
|
|
34
|
+
filters: string[];
|
|
35
|
+
grains: string[];
|
|
36
|
+
grain?: string;
|
|
37
|
+
requires?: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface RelationshipCatalogEntry {
|
|
40
|
+
kind: RelationshipDefinition['kind'];
|
|
41
|
+
target: string;
|
|
42
|
+
from: string;
|
|
43
|
+
to: string;
|
|
44
|
+
execution: 'metadata_only';
|
|
45
|
+
}
|
|
46
|
+
export interface DatasetCatalog {
|
|
47
|
+
name: string;
|
|
48
|
+
source: string;
|
|
49
|
+
tenantKey?: string;
|
|
50
|
+
timeKey?: string;
|
|
51
|
+
dimensions: Record<string, DimensionCatalogEntry>;
|
|
52
|
+
measures: Record<string, MeasureCatalogEntry>;
|
|
53
|
+
metrics: Record<string, MetricCatalogEntry>;
|
|
54
|
+
filters: Record<string, FilterCatalogEntry>;
|
|
55
|
+
relationships: Record<string, RelationshipCatalogEntry>;
|
|
56
|
+
limits?: DatasetLimits;
|
|
57
|
+
requiresTenant: boolean;
|
|
58
|
+
supportedGrains: TimeGrain[];
|
|
59
|
+
orderableFields: string[];
|
|
60
|
+
maxLimit?: number;
|
|
61
|
+
}
|
|
62
|
+
export type DatasetCatalogMap = Record<string, DatasetCatalog>;
|
|
63
|
+
export type DatasetCatalogSource = AnyDatasetInstance & {
|
|
64
|
+
metrics?: Record<string, MetricHandle>;
|
|
65
|
+
};
|
|
66
|
+
export declare function getDatasetCatalog(dataset: DatasetCatalogSource): DatasetCatalog;
|
|
67
|
+
export declare function getDatasetCatalogs(datasets: Record<string, DatasetCatalogSource>): DatasetCatalogMap;
|
|
68
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { SEMANTIC_FILTER_OPERATORS, SUPPORTED_TIME_GRAINS } from './constants.js';
|
|
2
|
+
function dimensionToCatalog(dimension) {
|
|
3
|
+
return {
|
|
4
|
+
type: dimension.fieldType,
|
|
5
|
+
column: dimension.column,
|
|
6
|
+
sql: dimension.sql,
|
|
7
|
+
label: dimension.label,
|
|
8
|
+
description: dimension.description,
|
|
9
|
+
filterable: dimension.filterable !== false,
|
|
10
|
+
groupable: dimension.groupable !== false,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function measureToCatalog(measure) {
|
|
14
|
+
return {
|
|
15
|
+
aggregation: measure.aggregation,
|
|
16
|
+
field: measure.field,
|
|
17
|
+
sql: measure.sql,
|
|
18
|
+
label: measure.label,
|
|
19
|
+
description: measure.description,
|
|
20
|
+
filterCount: measure.filters?.length ?? 0,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function filterToCatalog(filter, dimensions) {
|
|
24
|
+
return {
|
|
25
|
+
field: filter.field,
|
|
26
|
+
label: filter.label,
|
|
27
|
+
description: filter.description,
|
|
28
|
+
operators: filter.operators ? [...filter.operators] : [...SEMANTIC_FILTER_OPERATORS],
|
|
29
|
+
valueType: dimensions[filter.field]?.fieldType,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function metricToCatalog(metric) {
|
|
33
|
+
const contract = metric.contract();
|
|
34
|
+
return {
|
|
35
|
+
kind: contract.kind,
|
|
36
|
+
dataset: contract.dataset,
|
|
37
|
+
valueType: contract.valueType,
|
|
38
|
+
label: contract.label,
|
|
39
|
+
description: contract.description,
|
|
40
|
+
dimensions: contract.dimensions,
|
|
41
|
+
measures: contract.measures,
|
|
42
|
+
filters: contract.filters,
|
|
43
|
+
grains: contract.grains,
|
|
44
|
+
grain: contract.grain,
|
|
45
|
+
requires: contract.requires,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function relationshipToCatalog(relationship) {
|
|
49
|
+
return {
|
|
50
|
+
kind: relationship.kind,
|
|
51
|
+
target: relationship.target().name,
|
|
52
|
+
from: relationship.from,
|
|
53
|
+
to: relationship.to,
|
|
54
|
+
execution: 'metadata_only',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function getDatasetCatalog(dataset) {
|
|
58
|
+
const dimensionNames = Object.keys(dataset.dimensions);
|
|
59
|
+
const measureNames = Object.keys(dataset.measures);
|
|
60
|
+
const metricNames = Object.keys(dataset.metrics ?? {});
|
|
61
|
+
const supportedGrains = dataset.timeKey ? [...SUPPORTED_TIME_GRAINS] : [];
|
|
62
|
+
const maxLimit = dataset.limits?.maxResultSize;
|
|
63
|
+
return {
|
|
64
|
+
name: dataset.name,
|
|
65
|
+
source: dataset.source,
|
|
66
|
+
tenantKey: dataset.tenantKey,
|
|
67
|
+
timeKey: dataset.timeKey,
|
|
68
|
+
dimensions: Object.fromEntries(Object.entries(dataset.dimensions).map(([name, dimension]) => [
|
|
69
|
+
name,
|
|
70
|
+
dimensionToCatalog(dimension),
|
|
71
|
+
])),
|
|
72
|
+
measures: Object.fromEntries(Object.entries(dataset.measures).map(([name, measure]) => [
|
|
73
|
+
name,
|
|
74
|
+
measureToCatalog(measure),
|
|
75
|
+
])),
|
|
76
|
+
metrics: Object.fromEntries(Object.entries(dataset.metrics ?? {}).map(([name, metric]) => [
|
|
77
|
+
name,
|
|
78
|
+
metricToCatalog(metric),
|
|
79
|
+
])),
|
|
80
|
+
filters: Object.fromEntries(Object.entries(dataset.filters).map(([name, filter]) => [
|
|
81
|
+
name,
|
|
82
|
+
filterToCatalog(filter, dataset.dimensions),
|
|
83
|
+
])),
|
|
84
|
+
relationships: Object.fromEntries(Object.entries(dataset.relationships).map(([name, relationship]) => [
|
|
85
|
+
name,
|
|
86
|
+
relationshipToCatalog(relationship),
|
|
87
|
+
])),
|
|
88
|
+
limits: dataset.limits,
|
|
89
|
+
requiresTenant: !!dataset.tenantKey,
|
|
90
|
+
supportedGrains,
|
|
91
|
+
orderableFields: [
|
|
92
|
+
...dimensionNames,
|
|
93
|
+
...measureNames,
|
|
94
|
+
...metricNames,
|
|
95
|
+
...(dataset.timeKey ? ['period'] : []),
|
|
96
|
+
],
|
|
97
|
+
maxLimit,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function getDatasetCatalogs(datasets) {
|
|
101
|
+
return Object.fromEntries(Object.entries(datasets).map(([name, dataset]) => [
|
|
102
|
+
name,
|
|
103
|
+
getDatasetCatalog(dataset),
|
|
104
|
+
]));
|
|
105
|
+
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ export declare const GRAIN_FUNCTIONS: Record<TimeGrain, string>;
|
|
|
11
11
|
* {@link GRAIN_FUNCTIONS} so the two never drift apart.
|
|
12
12
|
*/
|
|
13
13
|
export declare const SUPPORTED_TIME_GRAINS: TimeGrain[];
|
|
14
|
+
/**
|
|
15
|
+
* The filter operators accepted by semantic dataset and metric inputs.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SEMANTIC_FILTER_OPERATORS: readonly ["eq", "neq", "gt", "gte", "lt", "lte", "in", "notIn", "between", "like"];
|
|
14
18
|
/**
|
|
15
19
|
* Narrowing guard for a runtime-provided grain value.
|
|
16
20
|
*/
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAMrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAmC,SAAS,EAAE,CAAC;AAEjF;;GAEG;AACH,eAAO,MAAM,yBAAyB,oFAWkB,CAAC;AAEzD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAEvE"}
|
package/dist/constants.js
CHANGED
|
@@ -16,6 +16,21 @@ export const GRAIN_FUNCTIONS = {
|
|
|
16
16
|
* {@link GRAIN_FUNCTIONS} so the two never drift apart.
|
|
17
17
|
*/
|
|
18
18
|
export const SUPPORTED_TIME_GRAINS = Object.keys(GRAIN_FUNCTIONS);
|
|
19
|
+
/**
|
|
20
|
+
* The filter operators accepted by semantic dataset and metric inputs.
|
|
21
|
+
*/
|
|
22
|
+
export const SEMANTIC_FILTER_OPERATORS = [
|
|
23
|
+
'eq',
|
|
24
|
+
'neq',
|
|
25
|
+
'gt',
|
|
26
|
+
'gte',
|
|
27
|
+
'lt',
|
|
28
|
+
'lte',
|
|
29
|
+
'in',
|
|
30
|
+
'notIn',
|
|
31
|
+
'between',
|
|
32
|
+
'like',
|
|
33
|
+
];
|
|
19
34
|
/**
|
|
20
35
|
* Narrowing guard for a runtime-provided grain value.
|
|
21
36
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export { sum, count, countDistinct, avg, min, max } from './aggregations.js';
|
|
|
6
6
|
export { divide, multiply, subtract, add, nullIfZero, coalesce, round, floor, ceil, } from './formulas.js';
|
|
7
7
|
export { eq, neq, gt, gte, lt, lte, inList, notInList, between, like, asc, desc, filter, order, } from './query-helpers.js';
|
|
8
8
|
export { createDatasetRegistry } from './registry.js';
|
|
9
|
+
export { getDatasetCatalog, getDatasetCatalogs } from './catalog.js';
|
|
10
|
+
export type { DatasetCatalog, DatasetCatalogMap, DimensionCatalogEntry, MeasureCatalogEntry, MetricCatalogEntry, FilterCatalogEntry, RelationshipCatalogEntry, } from './catalog.js';
|
|
11
|
+
export { generateDatasetTools, toOpenAITools, toAISDKTools, toMcpTools, } from './tools.js';
|
|
12
|
+
export type { AISDKToolDefinition, DatasetToolAnalytics, DatasetToolMode, GenerateDatasetToolsOptions, JsonSchema, McpToolDefinition, OpenAIToolDefinition, SemanticToolDefinition, } from './tools.js';
|
|
9
13
|
export { createDatasetClient } from './executor.js';
|
|
10
14
|
export type { DatasetClient, CreateDatasetClientOptions } from './executor.js';
|
|
11
15
|
export { createInMemoryBackend } from './in-memory-backend.js';
|
|
@@ -15,6 +19,6 @@ export type { ValidationResult } from './validation.js';
|
|
|
15
19
|
export { validateFilterValue, matchesFieldType } from './validation.js';
|
|
16
20
|
export type { QueryBuilderLike, QueryBuilderFactoryLike } from './query-builder-protocol.js';
|
|
17
21
|
export { validateSQLIdentifier, isSafeSQLIdentifier, quoteSQLIdentifier } from './sql-utils.js';
|
|
18
|
-
export { GRAIN_FUNCTIONS } from './constants.js';
|
|
22
|
+
export { GRAIN_FUNCTIONS, SEMANTIC_FILTER_OPERATORS, SUPPORTED_TIME_GRAINS } from './constants.js';
|
|
19
23
|
export type { FieldType, DimensionType, DimensionOptions, DimensionDefinition, MeasureOptions, MeasureDefinition, InferDimensionType, RelationshipKind, RelationshipDefinition, AggregationType, MeasureAggregation, AggregationSpec, FormulaExpr, DerivedMetricSpec, TimeGrain, MetricRef, BaseMetricRef, DerivedMetricRef, GrainedMetricRef, MetricContract, MetricFilter, MetricOrderBy, MetricQuery, DatasetQuery, MetricResultMeta, MetricResult, DatasetQueryResult, MetricHandle, ExecutionContext, SemanticExecutionRuntime, SemanticTenantRuntime, SemanticFilterDefinition, SemanticFiltersDefinition, DatasetConfig, DatasetLimits, DatasetInstance, AnyDatasetInstance, BaseMetricConfig, DerivedMetricConfig, DatasetRegistryInstance, DatasetFieldNames, DatasetDimensionNames, DatasetMeasureNames, DatasetOrderableNames, DatasetQueryFor, DatasetRow, DatasetQueryResultFor, MetricQueryFor, MetricRow, MetricResultFor, } from './types.js';
|
|
20
24
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGhE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAG7E,OAAO,EACL,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAC/B,UAAU,EAAE,QAAQ,EACpB,KAAK,EAAE,KAAK,EAAE,IAAI,GACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EACzB,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAChC,GAAG,EAAE,IAAI,EACT,MAAM,EAAE,KAAK,GACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC5E,YAAY,EACV,QAAQ,EACR,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxE,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAG7F,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGhG,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,10 @@ export { divide, multiply, subtract, add, nullIfZero, coalesce, round, floor, ce
|
|
|
14
14
|
export { eq, neq, gt, gte, lt, lte, inList, notInList, between, like, asc, desc, filter, order, } from './query-helpers.js';
|
|
15
15
|
// Registry
|
|
16
16
|
export { createDatasetRegistry } from './registry.js';
|
|
17
|
+
// Catalog
|
|
18
|
+
export { getDatasetCatalog, getDatasetCatalogs } from './catalog.js';
|
|
19
|
+
// Agent/tool metadata
|
|
20
|
+
export { generateDatasetTools, toOpenAITools, toAISDKTools, toMcpTools, } from './tools.js';
|
|
17
21
|
// Dataset client
|
|
18
22
|
export { createDatasetClient } from './executor.js';
|
|
19
23
|
export { createInMemoryBackend } from './in-memory-backend.js';
|
|
@@ -21,4 +25,4 @@ export { validateFilterValue, matchesFieldType } from './validation.js';
|
|
|
21
25
|
// SQL utilities
|
|
22
26
|
export { validateSQLIdentifier, isSafeSQLIdentifier, quoteSQLIdentifier } from './sql-utils.js';
|
|
23
27
|
// Constants
|
|
24
|
-
export { GRAIN_FUNCTIONS } from './constants.js';
|
|
28
|
+
export { GRAIN_FUNCTIONS, SEMANTIC_FILTER_OPERATORS, SUPPORTED_TIME_GRAINS } from './constants.js';
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AnyDatasetInstance, DatasetQuery, ExecutionContext, MetricHandle, MetricQuery } from './types.js';
|
|
2
|
+
import { getDatasetCatalog, type DatasetCatalogSource } from './catalog.js';
|
|
3
|
+
export type JsonSchema = {
|
|
4
|
+
type?: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
properties?: Record<string, JsonSchema>;
|
|
7
|
+
items?: JsonSchema;
|
|
8
|
+
required?: string[];
|
|
9
|
+
enum?: string[];
|
|
10
|
+
minimum?: number;
|
|
11
|
+
maximum?: number;
|
|
12
|
+
additionalProperties?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export interface SemanticToolDefinition<TInput = Record<string, unknown>, TResult = unknown> {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
parameters: JsonSchema;
|
|
18
|
+
execute(input: TInput, context?: ExecutionContext): Promise<TResult>;
|
|
19
|
+
}
|
|
20
|
+
export type DatasetToolMode = 'catalog' | 'per-dataset' | 'per-metric';
|
|
21
|
+
export interface DatasetToolAnalytics {
|
|
22
|
+
execute(target: AnyDatasetInstance | MetricHandle, query?: DatasetQuery | MetricQuery, context?: ExecutionContext): Promise<unknown>;
|
|
23
|
+
}
|
|
24
|
+
export interface GenerateDatasetToolsOptions {
|
|
25
|
+
datasets: Record<string, DatasetCatalogSource>;
|
|
26
|
+
analytics: DatasetToolAnalytics;
|
|
27
|
+
mode?: DatasetToolMode;
|
|
28
|
+
includeSql?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface OpenAIToolDefinition {
|
|
31
|
+
type: 'function';
|
|
32
|
+
function: {
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
parameters: JsonSchema;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export interface AISDKToolDefinition {
|
|
39
|
+
description: string;
|
|
40
|
+
parameters: JsonSchema;
|
|
41
|
+
execute(input: Record<string, unknown>): Promise<unknown>;
|
|
42
|
+
}
|
|
43
|
+
export interface McpToolDefinition {
|
|
44
|
+
name: string;
|
|
45
|
+
description: string;
|
|
46
|
+
inputSchema: JsonSchema;
|
|
47
|
+
}
|
|
48
|
+
export declare function generateDatasetTools(options: GenerateDatasetToolsOptions): SemanticToolDefinition[];
|
|
49
|
+
export declare function toOpenAITools(tools: SemanticToolDefinition[]): OpenAIToolDefinition[];
|
|
50
|
+
export declare function toAISDKTools(tools: SemanticToolDefinition[]): Record<string, AISDKToolDefinition>;
|
|
51
|
+
export declare function toMcpTools(tools: SemanticToolDefinition[]): McpToolDefinition[];
|
|
52
|
+
export { getDatasetCatalog };
|
|
53
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,WAAW,EAGZ,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EAIjB,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AAGtB,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxC,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,MAAM,WAAW,sBAAsB,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtE;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,aAAa,GAAG,YAAY,CAAC;AAEvE,MAAM,WAAW,oBAAoB;IACnC,OAAO,CACL,MAAM,EAAE,kBAAkB,GAAG,YAAY,EACzC,KAAK,CAAC,EAAE,YAAY,GAAG,WAAW,EAClC,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC/C,SAAS,EAAE,oBAAoB,CAAC;IAChC,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,UAAU,CAAC;KACxB,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3D;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;CACzB;AA8VD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,sBAAsB,EAAE,CAenG;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,sBAAsB,EAAE,GAAG,oBAAoB,EAAE,CASrF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,sBAAsB,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAWjG;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,sBAAsB,EAAE,GAAG,iBAAiB,EAAE,CAM/E;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { getDatasetCatalog, getDatasetCatalogs, } from './catalog.js';
|
|
2
|
+
import { SEMANTIC_FILTER_OPERATORS } from './constants.js';
|
|
3
|
+
function enumSchema(values, description) {
|
|
4
|
+
const unique = Array.from(new Set(values));
|
|
5
|
+
return {
|
|
6
|
+
type: 'string',
|
|
7
|
+
...(unique.length > 0 ? { enum: unique } : {}),
|
|
8
|
+
...(description ? { description } : {}),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function toolNamePart(name) {
|
|
12
|
+
const normalized = name.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
13
|
+
return /^[A-Za-z_]/.test(normalized) ? normalized : `_${normalized}`;
|
|
14
|
+
}
|
|
15
|
+
function arrayEnumSchema(values, description) {
|
|
16
|
+
return {
|
|
17
|
+
type: 'array',
|
|
18
|
+
items: enumSchema(values),
|
|
19
|
+
...(description ? { description } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function limitSchema(catalogs) {
|
|
23
|
+
const finiteLimits = catalogs
|
|
24
|
+
.map(catalog => catalog.maxLimit)
|
|
25
|
+
.filter((limit) => typeof limit === 'number');
|
|
26
|
+
return {
|
|
27
|
+
type: 'integer',
|
|
28
|
+
minimum: 0,
|
|
29
|
+
...(finiteLimits.length > 0 ? { maximum: Math.max(...finiteLimits) } : {}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function querySchema(catalogs, includeDataset) {
|
|
33
|
+
const datasetNames = catalogs.map(catalog => catalog.name);
|
|
34
|
+
const dimensionNames = Array.from(new Set(catalogs.flatMap(catalog => Object.keys(catalog.dimensions))));
|
|
35
|
+
const measureNames = Array.from(new Set(catalogs.flatMap(catalog => Object.keys(catalog.measures))));
|
|
36
|
+
const filterNames = Array.from(new Set(catalogs.flatMap(catalog => Object.keys(catalog.filters))));
|
|
37
|
+
const grainNames = Array.from(new Set(catalogs.flatMap(catalog => catalog.supportedGrains)));
|
|
38
|
+
const orderFields = Array.from(new Set(catalogs.flatMap(catalog => catalog.orderableFields)));
|
|
39
|
+
const properties = {
|
|
40
|
+
dimensions: arrayEnumSchema(dimensionNames, 'Dimensions to group by.'),
|
|
41
|
+
measures: arrayEnumSchema(measureNames, 'Measures to aggregate.'),
|
|
42
|
+
filters: {
|
|
43
|
+
type: 'array',
|
|
44
|
+
items: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
field: enumSchema(filterNames),
|
|
48
|
+
operator: enumSchema([...SEMANTIC_FILTER_OPERATORS]),
|
|
49
|
+
value: {},
|
|
50
|
+
},
|
|
51
|
+
required: ['field', 'operator', 'value'],
|
|
52
|
+
additionalProperties: false,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
orderBy: {
|
|
56
|
+
type: 'array',
|
|
57
|
+
items: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
field: enumSchema(orderFields),
|
|
61
|
+
direction: enumSchema(['asc', 'desc']),
|
|
62
|
+
},
|
|
63
|
+
required: ['field', 'direction'],
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
by: enumSchema(grainNames, 'Optional time grain.'),
|
|
68
|
+
limit: limitSchema(catalogs),
|
|
69
|
+
offset: {
|
|
70
|
+
type: 'integer',
|
|
71
|
+
minimum: 0,
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
if (includeDataset) {
|
|
75
|
+
properties.dataset = enumSchema(datasetNames);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties,
|
|
80
|
+
required: includeDataset ? ['dataset'] : [],
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function assertStringArray(input, name) {
|
|
85
|
+
if (input == null)
|
|
86
|
+
return [];
|
|
87
|
+
if (!Array.isArray(input) || input.some(value => typeof value !== 'string')) {
|
|
88
|
+
throw new Error(`Invalid ${name}: expected an array of strings.`);
|
|
89
|
+
}
|
|
90
|
+
return input;
|
|
91
|
+
}
|
|
92
|
+
function assertFilters(input) {
|
|
93
|
+
if (input == null)
|
|
94
|
+
return [];
|
|
95
|
+
if (!Array.isArray(input)) {
|
|
96
|
+
throw new Error('Invalid filters: expected an array.');
|
|
97
|
+
}
|
|
98
|
+
return input.map((filter, index) => {
|
|
99
|
+
if (!filter || typeof filter !== 'object') {
|
|
100
|
+
throw new Error(`Invalid filters[${index}]: expected an object.`);
|
|
101
|
+
}
|
|
102
|
+
const candidate = filter;
|
|
103
|
+
if (typeof candidate.field !== 'string') {
|
|
104
|
+
throw new Error(`Invalid filters[${index}].field: expected a string.`);
|
|
105
|
+
}
|
|
106
|
+
if (!SEMANTIC_FILTER_OPERATORS.includes(candidate.operator)) {
|
|
107
|
+
throw new Error(`Invalid filters[${index}].operator: expected a supported operator.`);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
field: candidate.field,
|
|
111
|
+
operator: candidate.operator,
|
|
112
|
+
value: candidate.value,
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function assertOrderBy(input) {
|
|
117
|
+
if (input == null)
|
|
118
|
+
return [];
|
|
119
|
+
if (!Array.isArray(input)) {
|
|
120
|
+
throw new Error('Invalid orderBy: expected an array.');
|
|
121
|
+
}
|
|
122
|
+
return input.map((order, index) => {
|
|
123
|
+
if (!order || typeof order !== 'object') {
|
|
124
|
+
throw new Error(`Invalid orderBy[${index}]: expected an object.`);
|
|
125
|
+
}
|
|
126
|
+
const candidate = order;
|
|
127
|
+
if (typeof candidate.field !== 'string') {
|
|
128
|
+
throw new Error(`Invalid orderBy[${index}].field: expected a string.`);
|
|
129
|
+
}
|
|
130
|
+
if (candidate.direction !== 'asc' && candidate.direction !== 'desc') {
|
|
131
|
+
throw new Error(`Invalid orderBy[${index}].direction: expected "asc" or "desc".`);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
field: candidate.field,
|
|
135
|
+
direction: candidate.direction,
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function assertNonNegativeInteger(input, name) {
|
|
140
|
+
if (input == null)
|
|
141
|
+
return undefined;
|
|
142
|
+
if (!Number.isInteger(input) || input < 0) {
|
|
143
|
+
throw new Error(`Invalid ${name}: expected a non-negative integer.`);
|
|
144
|
+
}
|
|
145
|
+
return input;
|
|
146
|
+
}
|
|
147
|
+
function assertAllowedValues(values, allowed, label) {
|
|
148
|
+
const invalid = values.filter(value => !allowed.includes(value));
|
|
149
|
+
if (invalid.length > 0) {
|
|
150
|
+
throw new Error(`Invalid ${label}: ${invalid.join(', ')}. Available: ${allowed.join(', ') || '(none)'}.`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function normalizeDatasetQuery(input, catalog, options = {}) {
|
|
154
|
+
const dimensions = assertStringArray(input.dimensions, 'dimensions');
|
|
155
|
+
const measures = assertStringArray(input.measures, 'measures');
|
|
156
|
+
const filters = assertFilters(input.filters);
|
|
157
|
+
const orderBy = assertOrderBy(input.orderBy);
|
|
158
|
+
const limit = assertNonNegativeInteger(input.limit, 'limit');
|
|
159
|
+
const offset = assertNonNegativeInteger(input.offset, 'offset');
|
|
160
|
+
let by;
|
|
161
|
+
assertAllowedValues(dimensions, Object.keys(catalog.dimensions), 'dimensions');
|
|
162
|
+
assertAllowedValues(measures, Object.keys(catalog.measures), 'measures');
|
|
163
|
+
assertAllowedValues(filters.map(filter => filter.field), Object.keys(catalog.filters), 'filter fields');
|
|
164
|
+
assertAllowedValues(orderBy.map(order => order.field), options.orderableFields ?? catalog.orderableFields, 'orderBy fields');
|
|
165
|
+
for (const filter of filters) {
|
|
166
|
+
const allowedOperators = catalog.filters[filter.field]?.operators ?? [];
|
|
167
|
+
if (!allowedOperators.includes(filter.operator)) {
|
|
168
|
+
throw new Error(`Invalid filter operator for "${filter.field}": ${filter.operator}. Allowed: ${allowedOperators.join(', ')}.`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (typeof input.by === 'string') {
|
|
172
|
+
assertAllowedValues([input.by], catalog.supportedGrains, 'time grain');
|
|
173
|
+
by = input.by;
|
|
174
|
+
}
|
|
175
|
+
else if (input.by != null) {
|
|
176
|
+
throw new Error('Invalid by: expected a time grain string.');
|
|
177
|
+
}
|
|
178
|
+
if (limit != null && catalog.maxLimit != null && limit > catalog.maxLimit) {
|
|
179
|
+
throw new Error(`Invalid limit: ${limit}. Max: ${catalog.maxLimit}.`);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
...(dimensions.length > 0 ? { dimensions } : {}),
|
|
183
|
+
...(measures.length > 0 ? { measures } : {}),
|
|
184
|
+
...(filters.length > 0 ? { filters } : {}),
|
|
185
|
+
...(orderBy.length > 0 ? { orderBy } : {}),
|
|
186
|
+
...(by ? { by } : {}),
|
|
187
|
+
...(limit != null ? { limit } : {}),
|
|
188
|
+
...(offset != null ? { offset } : {}),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function redactSql(result, includeSql) {
|
|
192
|
+
if (includeSql || !result || typeof result !== 'object') {
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
const resultObject = result;
|
|
196
|
+
if (!resultObject.meta || typeof resultObject.meta !== 'object' || !('sql' in resultObject.meta)) {
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
const { sql: _sql, ...meta } = resultObject.meta;
|
|
200
|
+
return {
|
|
201
|
+
...resultObject,
|
|
202
|
+
meta,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function buildCatalogTool(datasets, catalogs, analytics, includeSql) {
|
|
206
|
+
const catalogList = Object.values(catalogs);
|
|
207
|
+
return {
|
|
208
|
+
name: 'query_dataset',
|
|
209
|
+
description: 'Query governed analytics datasets by selecting dimensions, measures, filters, and time grains.',
|
|
210
|
+
parameters: querySchema(catalogList, true),
|
|
211
|
+
async execute(input, context) {
|
|
212
|
+
if (typeof input.dataset !== 'string' || !datasets[input.dataset]) {
|
|
213
|
+
throw new Error(`Invalid dataset: ${String(input.dataset)}. Available: ${Object.keys(datasets).join(', ')}.`);
|
|
214
|
+
}
|
|
215
|
+
const catalog = catalogs[input.dataset];
|
|
216
|
+
const query = normalizeDatasetQuery(input, catalog);
|
|
217
|
+
const result = await analytics.execute(datasets[input.dataset], query, context);
|
|
218
|
+
return redactSql(result, includeSql);
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function buildDatasetTools(datasets, catalogs, analytics, includeSql) {
|
|
223
|
+
return Object.entries(datasets).map(([datasetName, dataset]) => {
|
|
224
|
+
const catalog = catalogs[datasetName];
|
|
225
|
+
return {
|
|
226
|
+
name: `query_${toolNamePart(datasetName)}`,
|
|
227
|
+
description: `Query the ${datasetName} analytics dataset.`,
|
|
228
|
+
parameters: querySchema([catalog], false),
|
|
229
|
+
async execute(input, context) {
|
|
230
|
+
const query = normalizeDatasetQuery(input, catalog);
|
|
231
|
+
const result = await analytics.execute(dataset, query, context);
|
|
232
|
+
return redactSql(result, includeSql);
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function metricQuerySchema(catalog, metricName) {
|
|
238
|
+
const schema = querySchema([catalog], false);
|
|
239
|
+
const properties = schema.properties ?? {};
|
|
240
|
+
delete properties.measures;
|
|
241
|
+
properties.orderBy = {
|
|
242
|
+
type: 'array',
|
|
243
|
+
items: {
|
|
244
|
+
type: 'object',
|
|
245
|
+
properties: {
|
|
246
|
+
field: enumSchema([...Object.keys(catalog.dimensions), metricName, ...(catalog.timeKey ? ['period'] : [])]),
|
|
247
|
+
direction: enumSchema(['asc', 'desc']),
|
|
248
|
+
},
|
|
249
|
+
required: ['field', 'direction'],
|
|
250
|
+
additionalProperties: false,
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
return {
|
|
254
|
+
...schema,
|
|
255
|
+
properties,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function buildMetricTools(datasets, catalogs, analytics, includeSql) {
|
|
259
|
+
const tools = [];
|
|
260
|
+
for (const [datasetName, dataset] of Object.entries(datasets)) {
|
|
261
|
+
const catalog = catalogs[datasetName];
|
|
262
|
+
for (const [metricName, metric] of Object.entries(dataset.metrics ?? {})) {
|
|
263
|
+
tools.push({
|
|
264
|
+
name: `query_${toolNamePart(metricName)}`,
|
|
265
|
+
description: `Query the ${metricName} metric from the ${datasetName} dataset.`,
|
|
266
|
+
parameters: metricQuerySchema(catalog, metricName),
|
|
267
|
+
async execute(input, context) {
|
|
268
|
+
const orderableFields = [
|
|
269
|
+
...Object.keys(catalog.dimensions),
|
|
270
|
+
metricName,
|
|
271
|
+
...(catalog.timeKey ? ['period'] : []),
|
|
272
|
+
];
|
|
273
|
+
const query = normalizeDatasetQuery({ ...input, measures: [] }, catalog, { orderableFields });
|
|
274
|
+
const result = await analytics.execute(metric, query, context);
|
|
275
|
+
return redactSql(result, includeSql);
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return tools;
|
|
281
|
+
}
|
|
282
|
+
export function generateDatasetTools(options) {
|
|
283
|
+
const mode = options.mode ?? 'catalog';
|
|
284
|
+
const catalogs = getDatasetCatalogs(options.datasets);
|
|
285
|
+
if (mode === 'catalog') {
|
|
286
|
+
return [
|
|
287
|
+
buildCatalogTool(options.datasets, catalogs, options.analytics, options.includeSql ?? false),
|
|
288
|
+
];
|
|
289
|
+
}
|
|
290
|
+
if (mode === 'per-dataset') {
|
|
291
|
+
return buildDatasetTools(options.datasets, catalogs, options.analytics, options.includeSql ?? false);
|
|
292
|
+
}
|
|
293
|
+
return buildMetricTools(options.datasets, catalogs, options.analytics, options.includeSql ?? false);
|
|
294
|
+
}
|
|
295
|
+
export function toOpenAITools(tools) {
|
|
296
|
+
return tools.map(tool => ({
|
|
297
|
+
type: 'function',
|
|
298
|
+
function: {
|
|
299
|
+
name: tool.name,
|
|
300
|
+
description: tool.description,
|
|
301
|
+
parameters: tool.parameters,
|
|
302
|
+
},
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
export function toAISDKTools(tools) {
|
|
306
|
+
return Object.fromEntries(tools.map(tool => [
|
|
307
|
+
tool.name,
|
|
308
|
+
{
|
|
309
|
+
description: tool.description,
|
|
310
|
+
parameters: tool.parameters,
|
|
311
|
+
execute: input => tool.execute(input),
|
|
312
|
+
},
|
|
313
|
+
]));
|
|
314
|
+
}
|
|
315
|
+
export function toMcpTools(tools) {
|
|
316
|
+
return tools.map(tool => ({
|
|
317
|
+
name: tool.name,
|
|
318
|
+
description: tool.description,
|
|
319
|
+
inputSchema: tool.parameters,
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
export { getDatasetCatalog };
|