@duckcodeailabs/dql-agent 1.6.33 → 1.7.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.
- package/dist/agent-run-engine.d.ts +39 -8
- package/dist/agent-run-engine.d.ts.map +1 -1
- package/dist/agent-run-engine.js +169 -10
- package/dist/agent-run-engine.js.map +1 -1
- package/dist/agent-run-gates.d.ts.map +1 -1
- package/dist/agent-run-gates.js +21 -3
- package/dist/agent-run-gates.js.map +1 -1
- package/dist/agent-run-planner.d.ts.map +1 -1
- package/dist/agent-run-planner.js +14 -3
- package/dist/agent-run-planner.js.map +1 -1
- package/dist/agentic/answer-contract.d.ts +37 -0
- package/dist/agentic/answer-contract.d.ts.map +1 -0
- package/dist/agentic/answer-contract.js +47 -0
- package/dist/agentic/answer-contract.js.map +1 -0
- package/dist/agentic/tool-loop.d.ts +48 -0
- package/dist/agentic/tool-loop.d.ts.map +1 -0
- package/dist/agentic/tool-loop.js +181 -0
- package/dist/agentic/tool-loop.js.map +1 -0
- package/dist/agentic/toolset.d.ts +40 -0
- package/dist/agentic/toolset.d.ts.map +1 -0
- package/dist/agentic/toolset.js +265 -0
- package/dist/agentic/toolset.js.map +1 -0
- package/dist/answer-loop.d.ts +82 -6
- package/dist/answer-loop.d.ts.map +1 -1
- package/dist/answer-loop.js +799 -112
- package/dist/answer-loop.js.map +1 -1
- package/dist/app-builder.d.ts.map +1 -1
- package/dist/app-builder.js +18 -10
- package/dist/app-builder.js.map +1 -1
- package/dist/cascade/budgets.d.ts +20 -2
- package/dist/cascade/budgets.d.ts.map +1 -1
- package/dist/cascade/budgets.js +83 -45
- package/dist/cascade/budgets.js.map +1 -1
- package/dist/cascade/route-policy.d.ts +1 -1
- package/dist/cascade/route-policy.d.ts.map +1 -1
- package/dist/cascade/route-policy.js +6 -0
- package/dist/cascade/route-policy.js.map +1 -1
- package/dist/domain-context.d.ts +30 -0
- package/dist/domain-context.d.ts.map +1 -0
- package/dist/domain-context.js +68 -0
- package/dist/domain-context.js.map +1 -0
- package/dist/embeddings/provider.d.ts +67 -4
- package/dist/embeddings/provider.d.ts.map +1 -1
- package/dist/embeddings/provider.js +167 -6
- package/dist/embeddings/provider.js.map +1 -1
- package/dist/hints/correction-eval.d.ts +30 -0
- package/dist/hints/correction-eval.d.ts.map +1 -0
- package/dist/hints/correction-eval.js +63 -0
- package/dist/hints/correction-eval.js.map +1 -0
- package/dist/hints/git-store.d.ts +39 -3
- package/dist/hints/git-store.d.ts.map +1 -1
- package/dist/hints/git-store.js +172 -2
- package/dist/hints/git-store.js.map +1 -1
- package/dist/hints/staleness.d.ts +30 -0
- package/dist/hints/staleness.d.ts.map +1 -0
- package/dist/hints/staleness.js +38 -0
- package/dist/hints/staleness.js.map +1 -0
- package/dist/hints/store.d.ts.map +1 -1
- package/dist/hints/store.js +3 -32
- package/dist/hints/store.js.map +1 -1
- package/dist/hints/types.d.ts +41 -0
- package/dist/hints/types.d.ts.map +1 -1
- package/dist/hints/types.js +1 -0
- package/dist/hints/types.js.map +1 -1
- package/dist/index.d.ts +46 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +180 -9
- package/dist/index.js.map +1 -1
- package/dist/intent-controller.d.ts.map +1 -1
- package/dist/intent-controller.js +15 -3
- package/dist/intent-controller.js.map +1 -1
- package/dist/kg/build.d.ts.map +1 -1
- package/dist/kg/build.js +196 -4
- package/dist/kg/build.js.map +1 -1
- package/dist/kg/sqlite-fts.d.ts +13 -0
- package/dist/kg/sqlite-fts.d.ts.map +1 -1
- package/dist/kg/sqlite-fts.js +82 -49
- package/dist/kg/sqlite-fts.js.map +1 -1
- package/dist/kg/types.d.ts +9 -2
- package/dist/kg/types.d.ts.map +1 -1
- package/dist/memory/fts-query.d.ts +37 -4
- package/dist/memory/fts-query.d.ts.map +1 -1
- package/dist/memory/fts-query.js +53 -12
- package/dist/memory/fts-query.js.map +1 -1
- package/dist/metadata/analysis-planner.d.ts.map +1 -1
- package/dist/metadata/analysis-planner.js +179 -28
- package/dist/metadata/analysis-planner.js.map +1 -1
- package/dist/metadata/analytical-policy.d.ts +51 -0
- package/dist/metadata/analytical-policy.d.ts.map +1 -0
- package/dist/metadata/analytical-policy.js +309 -0
- package/dist/metadata/analytical-policy.js.map +1 -0
- package/dist/metadata/block-adapt.d.ts +37 -0
- package/dist/metadata/block-adapt.d.ts.map +1 -0
- package/dist/metadata/block-adapt.js +89 -0
- package/dist/metadata/block-adapt.js.map +1 -0
- package/dist/metadata/block-fit.js +37 -4
- package/dist/metadata/block-fit.js.map +1 -1
- package/dist/metadata/catalog.d.ts +85 -1
- package/dist/metadata/catalog.d.ts.map +1 -1
- package/dist/metadata/catalog.js +674 -92
- package/dist/metadata/catalog.js.map +1 -1
- package/dist/metadata/dbt-first-safety.d.ts +19 -0
- package/dist/metadata/dbt-first-safety.d.ts.map +1 -0
- package/dist/metadata/dbt-first-safety.js +17 -0
- package/dist/metadata/dbt-first-safety.js.map +1 -0
- package/dist/metadata/example-match.d.ts +30 -0
- package/dist/metadata/example-match.d.ts.map +1 -0
- package/dist/metadata/example-match.js +69 -0
- package/dist/metadata/example-match.js.map +1 -0
- package/dist/metadata/grain-ledger.d.ts +51 -0
- package/dist/metadata/grain-ledger.d.ts.map +1 -0
- package/dist/metadata/grain-ledger.js +174 -0
- package/dist/metadata/grain-ledger.js.map +1 -0
- package/dist/metadata/metric-match.d.ts +21 -1
- package/dist/metadata/metric-match.d.ts.map +1 -1
- package/dist/metadata/metric-match.js +157 -21
- package/dist/metadata/metric-match.js.map +1 -1
- package/dist/metadata/sql-context-validation.d.ts.map +1 -1
- package/dist/metadata/sql-context-validation.js +42 -2
- package/dist/metadata/sql-context-validation.js.map +1 -1
- package/dist/metadata/sql-grounding.d.ts +4 -2
- package/dist/metadata/sql-grounding.d.ts.map +1 -1
- package/dist/metadata/sql-grounding.js +29 -4
- package/dist/metadata/sql-grounding.js.map +1 -1
- package/dist/metadata/sql-retrieval.d.ts +5 -2
- package/dist/metadata/sql-retrieval.d.ts.map +1 -1
- package/dist/metadata/sql-retrieval.js +28 -6
- package/dist/metadata/sql-retrieval.js.map +1 -1
- package/dist/propose/build-from-prompt.d.ts +34 -0
- package/dist/propose/build-from-prompt.d.ts.map +1 -1
- package/dist/propose/build-from-prompt.js +205 -19
- package/dist/propose/build-from-prompt.js.map +1 -1
- package/dist/propose/failure-analysis.d.ts +48 -0
- package/dist/propose/failure-analysis.d.ts.map +1 -0
- package/dist/propose/failure-analysis.js +71 -0
- package/dist/propose/failure-analysis.js.map +1 -0
- package/dist/propose/join-mining.d.ts +18 -0
- package/dist/propose/join-mining.d.ts.map +1 -0
- package/dist/propose/join-mining.js +74 -0
- package/dist/propose/join-mining.js.map +1 -0
- package/dist/providers/claude.d.ts.map +1 -1
- package/dist/providers/claude.js +36 -1
- package/dist/providers/claude.js.map +1 -1
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +34 -1
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/reasoning-effort.d.ts +27 -0
- package/dist/providers/reasoning-effort.d.ts.map +1 -1
- package/dist/providers/reasoning-effort.js +32 -0
- package/dist/providers/reasoning-effort.js.map +1 -1
- package/dist/providers/types.d.ts +2 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/research-loop.d.ts.map +1 -1
- package/dist/research-loop.js +9 -2
- package/dist/research-loop.js.map +1 -1
- package/dist/semantic-bridge/compose.d.ts.map +1 -1
- package/dist/semantic-bridge/compose.js +316 -25
- package/dist/semantic-bridge/compose.js.map +1 -1
- package/dist/skills/bootstrap.d.ts +57 -0
- package/dist/skills/bootstrap.d.ts.map +1 -0
- package/dist/skills/bootstrap.js +260 -0
- package/dist/skills/bootstrap.js.map +1 -0
- package/dist/skills/defaults.d.ts +2 -2
- package/dist/skills/defaults.js +2 -2
- package/dist/skills/domain-skills.d.ts +24 -0
- package/dist/skills/domain-skills.d.ts.map +1 -0
- package/dist/skills/domain-skills.js +76 -0
- package/dist/skills/domain-skills.js.map +1 -0
- package/dist/skills/loader.d.ts +56 -8
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +257 -41
- package/dist/skills/loader.js.map +1 -1
- package/dist/synthesize.d.ts.map +1 -1
- package/dist/synthesize.js +152 -16
- package/dist/synthesize.js.map +1 -1
- package/dist/tools/registry.d.ts +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +54 -12
- package/dist/tools/registry.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Deterministic manifest-v3 analytical path planning and SQL validation. */
|
|
2
|
+
import { type DQLManifest, type ManifestModelRelationship } from '@duckcodeailabs/dql-core';
|
|
3
|
+
import type { DomainContextEnvelope } from '../domain-context.js';
|
|
4
|
+
export type AnalyticalPolicyCode = 'attribution_policy_required' | 'stale_certification' | 'relationship_not_certified' | 'relationship_evidence_missing' | 'relationship_evidence_expired' | 'relationship_proof_invalid' | 'relationship_not_exported' | 'unsafe_relationship' | 'unbound_relation' | 'unplanned_join' | 'join_key_mismatch' | 'purpose_not_allowed';
|
|
5
|
+
export interface AnalyticalJoinPlanEdge {
|
|
6
|
+
relationshipId: string;
|
|
7
|
+
fromEntity: string;
|
|
8
|
+
toEntity: string;
|
|
9
|
+
fromRelation?: string;
|
|
10
|
+
toRelation?: string;
|
|
11
|
+
keys: Array<{
|
|
12
|
+
from: string;
|
|
13
|
+
to: string;
|
|
14
|
+
}>;
|
|
15
|
+
cardinality: string;
|
|
16
|
+
fanout: string;
|
|
17
|
+
importRefs: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface AnalyticalPathPlan {
|
|
20
|
+
safe: boolean;
|
|
21
|
+
code?: AnalyticalPolicyCode;
|
|
22
|
+
message?: string;
|
|
23
|
+
entities: string[];
|
|
24
|
+
relationshipIds: string[];
|
|
25
|
+
edges: AnalyticalJoinPlanEdge[];
|
|
26
|
+
}
|
|
27
|
+
export interface AnalyticalPathRequest {
|
|
28
|
+
entityIds: string[];
|
|
29
|
+
ownerDomain?: string;
|
|
30
|
+
purpose?: string;
|
|
31
|
+
/** Server-resolved authorization boundary. Raw client domain hints are insufficient. */
|
|
32
|
+
domainContext?: DomainContextEnvelope;
|
|
33
|
+
measureEntities?: string[];
|
|
34
|
+
dimensionEntities?: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface AnalyticalRelationshipDecision {
|
|
37
|
+
executable: boolean;
|
|
38
|
+
code?: AnalyticalPolicyCode;
|
|
39
|
+
message: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Re-check the complete executable-join invariant at the point of use.
|
|
43
|
+
*
|
|
44
|
+
* `automaticJoinAllowed` is the compiler's summary bit and remains mandatory,
|
|
45
|
+
* but consumers also validate its source proof so stale/corrupt hand-authored
|
|
46
|
+
* manifests cannot turn a review-only relationship into SQL authorization.
|
|
47
|
+
*/
|
|
48
|
+
export declare function assessAnalyticalRelationship(relationship: ManifestModelRelationship, manifest?: DQLManifest, now?: Date): AnalyticalRelationshipDecision;
|
|
49
|
+
export declare function planAnalyticalPath(manifest: DQLManifest, request: AnalyticalPathRequest): AnalyticalPathPlan;
|
|
50
|
+
export declare function validateAnalyticalSql(sql: string, manifest: DQLManifest, dialect?: string, purpose?: string, domainContext?: DomainContextEnvelope): AnalyticalPathPlan;
|
|
51
|
+
//# sourceMappingURL=analytical-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytical-policy.d.ts","sourceRoot":"","sources":["../../src/metadata/analytical-policy.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,OAAO,EAGL,KAAK,WAAW,EAEhB,KAAK,yBAAyB,EAC/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,MAAM,MAAM,oBAAoB,GAC5B,6BAA6B,GAC7B,qBAAqB,GACrB,4BAA4B,GAC5B,+BAA+B,GAC/B,+BAA+B,GAC/B,4BAA4B,GAC5B,2BAA2B,GAC3B,qBAAqB,GACrB,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GACnB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE,sBAAsB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,8BAA8B;IAC7C,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,yBAAyB,EACvC,QAAQ,CAAC,EAAE,WAAW,EACtB,GAAG,OAAa,GACf,8BAA8B,CA8DhC;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,qBAAqB,GAAG,kBAAkB,CAsE5G;AAiED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,WAAW,EACrB,OAAO,SAAW,EAClB,OAAO,CAAC,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,qBAAqB,GACpC,kBAAkB,CAwCpB"}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/** Deterministic manifest-v3 analytical path planning and SQL validation. */
|
|
2
|
+
import { analyzeSqlReferences, relationshipValidationProofFingerprint, } from '@duckcodeailabs/dql-core';
|
|
3
|
+
/**
|
|
4
|
+
* Re-check the complete executable-join invariant at the point of use.
|
|
5
|
+
*
|
|
6
|
+
* `automaticJoinAllowed` is the compiler's summary bit and remains mandatory,
|
|
7
|
+
* but consumers also validate its source proof so stale/corrupt hand-authored
|
|
8
|
+
* manifests cannot turn a review-only relationship into SQL authorization.
|
|
9
|
+
*/
|
|
10
|
+
export function assessAnalyticalRelationship(relationship, manifest, now = new Date()) {
|
|
11
|
+
if (relationship.status !== 'certified') {
|
|
12
|
+
return denied('relationship_not_certified', `Relationship "${relationship.id}" is ${relationship.status}, not certified.`);
|
|
13
|
+
}
|
|
14
|
+
if (relationship.staleCertification) {
|
|
15
|
+
return denied('stale_certification', `Relationship "${relationship.id}" is stale and cannot authorize generated SQL.`);
|
|
16
|
+
}
|
|
17
|
+
if (!relationship.certificationFingerprint?.trim()) {
|
|
18
|
+
return denied('relationship_proof_invalid', `Relationship "${relationship.id}" has no valid certification fingerprint.`);
|
|
19
|
+
}
|
|
20
|
+
const validation = relationship.validation;
|
|
21
|
+
if (!validation) {
|
|
22
|
+
return denied('relationship_evidence_missing', `Relationship "${relationship.id}" has no warehouse validation evidence.`);
|
|
23
|
+
}
|
|
24
|
+
if (validation.status !== 'passed') {
|
|
25
|
+
return denied('relationship_proof_invalid', `Relationship "${relationship.id}" warehouse validation is ${validation.status}, not passed.`);
|
|
26
|
+
}
|
|
27
|
+
if (!validDate(validation.checkedAt) || !validation.queryFingerprint?.trim() || !validValidationCounts(validation)) {
|
|
28
|
+
return denied('relationship_proof_invalid', `Relationship "${relationship.id}" has invalid warehouse validation proof.`);
|
|
29
|
+
}
|
|
30
|
+
const modeling = manifest?.modeling;
|
|
31
|
+
const fromEntity = modeling?.entities[relationship.from];
|
|
32
|
+
const toEntity = modeling?.entities[relationship.to];
|
|
33
|
+
const fromRelation = manifest?.dbtProvenance?.nodes[fromEntity?.dbtUniqueId ?? '']?.relation;
|
|
34
|
+
const toRelation = manifest?.dbtProvenance?.nodes[toEntity?.dbtUniqueId ?? '']?.relation;
|
|
35
|
+
if (!validation.proofFingerprint?.trim()) {
|
|
36
|
+
return denied('relationship_proof_invalid', `Relationship "${relationship.id}" has legacy or unbound warehouse evidence; run relationship validation again.`);
|
|
37
|
+
}
|
|
38
|
+
if (!manifest || !fromEntity || !toEntity) {
|
|
39
|
+
return denied('relationship_proof_invalid', `Relationship "${relationship.id}" validation proof cannot be bound to its manifest-v3 entities.`);
|
|
40
|
+
}
|
|
41
|
+
const expectedProof = relationshipValidationProofFingerprint({
|
|
42
|
+
fromRelation,
|
|
43
|
+
toRelation,
|
|
44
|
+
keys: relationship.keys,
|
|
45
|
+
cardinality: relationship.cardinality,
|
|
46
|
+
fanout: relationship.fanout,
|
|
47
|
+
queryFingerprint: validation.queryFingerprint,
|
|
48
|
+
});
|
|
49
|
+
if (validation.proofFingerprint !== expectedProof) {
|
|
50
|
+
return denied('relationship_proof_invalid', `Relationship "${relationship.id}" validation proof no longer matches its query, relations, keys, cardinality, or fanout policy.`);
|
|
51
|
+
}
|
|
52
|
+
if (relationship.evidenceExpiresAt) {
|
|
53
|
+
const expiresAt = Date.parse(relationship.evidenceExpiresAt);
|
|
54
|
+
if (!Number.isFinite(expiresAt)) {
|
|
55
|
+
return denied('relationship_proof_invalid', `Relationship "${relationship.id}" has an invalid evidence expiry timestamp.`);
|
|
56
|
+
}
|
|
57
|
+
if (expiresAt <= now.getTime()) {
|
|
58
|
+
return denied('relationship_evidence_expired', `Relationship "${relationship.id}" warehouse validation evidence expired at ${relationship.evidenceExpiresAt}.`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (relationship.keys.length === 0 || relationship.keys.some((key) => !key.from.trim() || !key.to.trim())) {
|
|
62
|
+
return denied('relationship_proof_invalid', `Relationship "${relationship.id}" has no valid physical join-key proof.`);
|
|
63
|
+
}
|
|
64
|
+
if (relationship.fanout !== 'safe'
|
|
65
|
+
|| !['one_to_one', 'one_to_many', 'many_to_one'].includes(relationship.cardinality)) {
|
|
66
|
+
return denied('unsafe_relationship', `Relationship "${relationship.id}" is not fanout-safe for automatic joins.`);
|
|
67
|
+
}
|
|
68
|
+
if (!relationship.automaticJoinAllowed) {
|
|
69
|
+
return denied('unsafe_relationship', `Relationship "${relationship.id}" is not authorized for automatic joins.`);
|
|
70
|
+
}
|
|
71
|
+
return { executable: true, message: `Relationship "${relationship.id}" is certified for automatic joins.` };
|
|
72
|
+
}
|
|
73
|
+
export function planAnalyticalPath(manifest, request) {
|
|
74
|
+
const modeling = manifest.modeling;
|
|
75
|
+
if (manifest.manifestVersion !== 3 || !modeling)
|
|
76
|
+
return allowed([], []);
|
|
77
|
+
const requested = [...new Set(request.entityIds)];
|
|
78
|
+
const resolvedEntities = requested.map((id) => {
|
|
79
|
+
if (modeling.entities[id])
|
|
80
|
+
return id;
|
|
81
|
+
const ownerDomain = request.domainContext?.activeDomain ?? request.ownerDomain;
|
|
82
|
+
const matches = Object.entries(modeling.entities).filter(([, entity]) => entity.qualifiedId === id
|
|
83
|
+
|| (Boolean(ownerDomain) && entity.domain === ownerDomain && entity.localId === id));
|
|
84
|
+
return matches.length === 1 ? matches[0][0] : undefined;
|
|
85
|
+
});
|
|
86
|
+
const unresolved = requested.filter((_id, index) => !resolvedEntities[index]);
|
|
87
|
+
if (unresolved.length > 0) {
|
|
88
|
+
return blocked('unbound_relation', [], [], `Unknown or ambiguous qualified analytical entity id(s): ${unresolved.join(', ')}.`);
|
|
89
|
+
}
|
|
90
|
+
const entities = resolvedEntities.filter((id) => Boolean(id)).sort();
|
|
91
|
+
if (entities.length < 2)
|
|
92
|
+
return allowed(entities, []);
|
|
93
|
+
const relationships = Object.values(modeling.relationships);
|
|
94
|
+
const directlyRelevant = relationships.filter((relationship) => entities.includes(relationship.from) || entities.includes(relationship.to));
|
|
95
|
+
const attribution = directlyRelevant.find((relationship) => relationship.fanout === 'attribution_required'
|
|
96
|
+
&& entities.includes(relationship.from) && entities.includes(relationship.to));
|
|
97
|
+
if (attribution)
|
|
98
|
+
return blocked('attribution_policy_required', entities, [attribution], `Relationship "${attribution.id}" requires an attribution policy or allocation policy before DQL can join these entities.`);
|
|
99
|
+
const adjacency = new Map();
|
|
100
|
+
for (const entity of Object.keys(modeling.entities))
|
|
101
|
+
adjacency.set(entity, []);
|
|
102
|
+
for (const relationship of relationships.filter((value) => assessAnalyticalRelationship(value, manifest).executable)) {
|
|
103
|
+
adjacency.get(relationship.from)?.push({ next: relationship.to, relationship });
|
|
104
|
+
adjacency.get(relationship.to)?.push({ next: relationship.from, relationship });
|
|
105
|
+
}
|
|
106
|
+
const selected = new Map();
|
|
107
|
+
const connected = new Set([entities[0]]);
|
|
108
|
+
for (const target of entities.slice(1)) {
|
|
109
|
+
const path = shortestPath([...connected], target, adjacency);
|
|
110
|
+
if (!path) {
|
|
111
|
+
const candidate = relationships.find((relationship) => (connected.has(relationship.from) && relationship.to === target)
|
|
112
|
+
|| (connected.has(relationship.to) && relationship.from === target));
|
|
113
|
+
if (candidate) {
|
|
114
|
+
const decision = assessAnalyticalRelationship(candidate, manifest);
|
|
115
|
+
if (!decision.executable && decision.code !== 'unsafe_relationship') {
|
|
116
|
+
return blocked(decision.code ?? 'unsafe_relationship', entities, [candidate], decision.message);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (candidate?.crossDomain)
|
|
120
|
+
return blocked('relationship_not_exported', entities, candidate ? [candidate] : [], 'The cross-domain path is missing a certified export/import interface or valid purpose.');
|
|
121
|
+
return blocked('unsafe_relationship', entities, candidate ? [candidate] : [], `No certified, validated, fanout-safe DQL path connects ${entities.join(', ')}.`);
|
|
122
|
+
}
|
|
123
|
+
for (const relationship of path) {
|
|
124
|
+
selected.set(relationship.qualifiedId ?? relationship.id, relationship);
|
|
125
|
+
connected.add(relationship.from);
|
|
126
|
+
connected.add(relationship.to);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
for (const relationship of selected.values()) {
|
|
130
|
+
if (relationship.crossDomain) {
|
|
131
|
+
const authorization = authorizeCrossDomainRelationship(manifest, relationship, request.domainContext, request.purpose);
|
|
132
|
+
if (!authorization.safe)
|
|
133
|
+
return blocked(authorization.code, entities, [relationship], authorization.message);
|
|
134
|
+
}
|
|
135
|
+
if (relationship.aggregation) {
|
|
136
|
+
const missingMeasure = request.measureEntities?.find((entity) => !relationship.aggregation?.measuresFrom.includes(entity));
|
|
137
|
+
const missingDimension = request.dimensionEntities?.find((entity) => !relationship.aggregation?.dimensionsFrom.includes(entity));
|
|
138
|
+
if (missingMeasure || missingDimension) {
|
|
139
|
+
return blocked('unsafe_relationship', entities, [relationship], `Relationship "${relationship.id}" does not authorize the requested measure/dimension direction.`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return allowed(entities, [...selected.values()], manifest);
|
|
144
|
+
}
|
|
145
|
+
function denied(code, message) {
|
|
146
|
+
return { executable: false, code, message };
|
|
147
|
+
}
|
|
148
|
+
function authorizeCrossDomainRelationship(manifest, relationship, context, requestedPurpose) {
|
|
149
|
+
const purpose = context?.purpose?.trim();
|
|
150
|
+
if (!context || !purpose) {
|
|
151
|
+
return { safe: false, code: 'purpose_not_allowed', message: `Cross-domain relationship "${relationship.localId}" requires an explicit analytical purpose.` };
|
|
152
|
+
}
|
|
153
|
+
if (requestedPurpose?.trim() && requestedPurpose.trim() !== purpose) {
|
|
154
|
+
return { safe: false, code: 'purpose_not_allowed', message: `Requested purpose "${requestedPurpose}" does not match the resolved domain context purpose "${purpose}".` };
|
|
155
|
+
}
|
|
156
|
+
if (!context.activeDomain || (relationship.ownerDomain && context.activeDomain !== relationship.ownerDomain)) {
|
|
157
|
+
return { safe: false, code: 'relationship_not_exported', message: `Cross-domain relationship "${relationship.localId}" is not owned by the active domain context.` };
|
|
158
|
+
}
|
|
159
|
+
const importRefs = relationship.importRefs ?? [];
|
|
160
|
+
if (importRefs.length === 0) {
|
|
161
|
+
return { safe: false, code: 'relationship_not_exported', message: `Cross-domain relationship "${relationship.localId}" has no explicit import interface.` };
|
|
162
|
+
}
|
|
163
|
+
const exports = manifest.modeling?.interfaces?.exports ?? {};
|
|
164
|
+
const contracts = Object.values(manifest.modeling?.contracts ?? {});
|
|
165
|
+
for (const exportRef of importRefs) {
|
|
166
|
+
const allowed = context.allowedImports.find((value) => value.exportRef === exportRef && value.purpose === purpose);
|
|
167
|
+
const exported = exports[exportRef];
|
|
168
|
+
if (!allowed || !exported || exported.status !== 'certified') {
|
|
169
|
+
return { safe: false, code: 'relationship_not_exported', message: `Cross-domain relationship "${relationship.localId}" is missing a certified allowed import for "${exportRef}" and purpose "${purpose}".` };
|
|
170
|
+
}
|
|
171
|
+
const contract = exported.contract
|
|
172
|
+
? contracts.find((value) => value.qualifiedId === exported.contract
|
|
173
|
+
|| value.id === exported.contract
|
|
174
|
+
|| (value.domain === exported.domain && value.localId === exported.contract))
|
|
175
|
+
: undefined;
|
|
176
|
+
const contractEntityCompatible = !exported.entity || Boolean(contract?.entities.includes(exported.entity));
|
|
177
|
+
const contractPurposeCompatible = !contract?.purpose || contract.purpose === purpose;
|
|
178
|
+
if (!contract || contract.status !== 'certified' || !contractEntityCompatible || !contractPurposeCompatible) {
|
|
179
|
+
return { safe: false, code: 'relationship_not_exported', message: `Cross-domain export "${exportRef}" has no compatible certified contract for purpose "${purpose}".` };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return { safe: true };
|
|
183
|
+
}
|
|
184
|
+
function validDate(value) {
|
|
185
|
+
return value.trim().length > 0 && Number.isFinite(Date.parse(value));
|
|
186
|
+
}
|
|
187
|
+
function validValidationCounts(validation) {
|
|
188
|
+
return [
|
|
189
|
+
validation.fromRows,
|
|
190
|
+
validation.toRows,
|
|
191
|
+
validation.joinedRows,
|
|
192
|
+
validation.fromNullKeys,
|
|
193
|
+
validation.toNullKeys,
|
|
194
|
+
validation.unmatchedFrom,
|
|
195
|
+
validation.maxFromPerKey,
|
|
196
|
+
validation.maxToPerKey,
|
|
197
|
+
].every((value) => Number.isFinite(value) && value >= 0);
|
|
198
|
+
}
|
|
199
|
+
export function validateAnalyticalSql(sql, manifest, dialect = 'duckdb', purpose, domainContext) {
|
|
200
|
+
if (manifest.manifestVersion !== 3 || !manifest.modeling || !manifest.dbtProvenance)
|
|
201
|
+
return allowed([], []);
|
|
202
|
+
const analysis = analyzeSqlReferences(sql, dialect);
|
|
203
|
+
if (!analysis.parsed)
|
|
204
|
+
return blocked('unplanned_join', [], [], `DQL could not parse generated SQL for analytical policy validation: ${analysis.error ?? 'unknown parse error'}`);
|
|
205
|
+
const relationEntities = new Map();
|
|
206
|
+
for (const relation of analysis.tables) {
|
|
207
|
+
const entity = entityForRelation(relation, manifest);
|
|
208
|
+
const key = entity ? entityRecordKey(manifest, entity) : undefined;
|
|
209
|
+
if (entity && key)
|
|
210
|
+
relationEntities.set(normalizeRelation(relation), { key, entity });
|
|
211
|
+
}
|
|
212
|
+
if (analysis.joins.length > 0) {
|
|
213
|
+
const unbound = analysis.tables.filter((relation) => !entityForRelation(relation, manifest));
|
|
214
|
+
if (unbound.length > 0)
|
|
215
|
+
return blocked('unbound_relation', [...new Set([...relationEntities.values()].map((value) => value.key))], [], `Generated join references unbound relation(s): ${unbound.join(', ')}.`);
|
|
216
|
+
}
|
|
217
|
+
const plan = planAnalyticalPath(manifest, {
|
|
218
|
+
entityIds: [...new Set([...relationEntities.values()].map((value) => value.key))],
|
|
219
|
+
purpose,
|
|
220
|
+
domainContext,
|
|
221
|
+
});
|
|
222
|
+
if (!plan.safe)
|
|
223
|
+
return plan;
|
|
224
|
+
for (const join of analysis.joins) {
|
|
225
|
+
const left = join.leftRelation ? entityForRelation(join.leftRelation, manifest) : undefined;
|
|
226
|
+
const right = join.rightRelation ? entityForRelation(join.rightRelation, manifest) : undefined;
|
|
227
|
+
if (!left || !right)
|
|
228
|
+
return blocked('unbound_relation', plan.entities, [], 'Every generated join endpoint must bind to a DQL analytical entity.');
|
|
229
|
+
const leftKey = entityRecordKey(manifest, left);
|
|
230
|
+
const rightKey = entityRecordKey(manifest, right);
|
|
231
|
+
if (!leftKey || !rightKey)
|
|
232
|
+
return blocked('unbound_relation', plan.entities, [], 'Every generated join endpoint must resolve to a qualified DQL analytical entity.');
|
|
233
|
+
const relationship = plan.relationshipIds
|
|
234
|
+
.map((id) => manifest.modeling?.relationships[id])
|
|
235
|
+
.find((value) => value && ((value.from === leftKey && value.to === rightKey) || (value.from === rightKey && value.to === leftKey)));
|
|
236
|
+
if (!relationship)
|
|
237
|
+
return blocked('unplanned_join', plan.entities, [], `No planned relationship authorizes ${left.id} to ${right.id}.`);
|
|
238
|
+
const matches = relationship.keys.some((key) => {
|
|
239
|
+
const direct = relationship.from === leftKey && normalizeColumn(join.leftColumn) === normalizeColumn(key.from) && normalizeColumn(join.rightColumn) === normalizeColumn(key.to);
|
|
240
|
+
const reverse = relationship.from === rightKey && normalizeColumn(join.leftColumn) === normalizeColumn(key.to) && normalizeColumn(join.rightColumn) === normalizeColumn(key.from);
|
|
241
|
+
return direct || reverse;
|
|
242
|
+
});
|
|
243
|
+
if (!matches)
|
|
244
|
+
return blocked('join_key_mismatch', plan.entities, [relationship], `Join predicate ${join.leftColumn} = ${join.rightColumn} does not match certified keys for "${relationship.id}".`);
|
|
245
|
+
}
|
|
246
|
+
return plan;
|
|
247
|
+
}
|
|
248
|
+
function entityRecordKey(manifest, entity) {
|
|
249
|
+
return Object.entries(manifest.modeling?.entities ?? {}).find(([, candidate]) => candidate === entity || (Boolean(entity.qualifiedId) && candidate.qualifiedId === entity.qualifiedId))?.[0];
|
|
250
|
+
}
|
|
251
|
+
function shortestPath(starts, target, adjacency) {
|
|
252
|
+
const queue = starts.map((entity) => ({ entity, path: [] }));
|
|
253
|
+
const seen = new Set(starts);
|
|
254
|
+
while (queue.length > 0) {
|
|
255
|
+
const current = queue.shift();
|
|
256
|
+
if (current.entity === target)
|
|
257
|
+
return current.path;
|
|
258
|
+
for (const edge of adjacency.get(current.entity) ?? []) {
|
|
259
|
+
if (seen.has(edge.next))
|
|
260
|
+
continue;
|
|
261
|
+
seen.add(edge.next);
|
|
262
|
+
queue.push({ entity: edge.next, path: [...current.path, edge.relationship] });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
function entityForRelation(relation, manifest) {
|
|
268
|
+
const normalized = normalizeRelation(relation);
|
|
269
|
+
return Object.values(manifest.modeling?.entities ?? {}).find((entity) => {
|
|
270
|
+
const node = manifest.dbtProvenance?.nodes[entity.dbtUniqueId];
|
|
271
|
+
return [node?.name, node?.relation].filter(Boolean).some((value) => {
|
|
272
|
+
const candidate = normalizeRelation(String(value));
|
|
273
|
+
return candidate === normalized || candidate.endsWith(`.${normalized}`) || normalized.endsWith(`.${candidate}`);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
function allowed(entities, relationships, manifest) {
|
|
278
|
+
return {
|
|
279
|
+
safe: true,
|
|
280
|
+
entities,
|
|
281
|
+
relationshipIds: relationships.map((relationship) => relationshipRecordKey(manifest, relationship)),
|
|
282
|
+
edges: relationships.map((relationship) => ({
|
|
283
|
+
relationshipId: relationship.id,
|
|
284
|
+
fromEntity: relationship.from,
|
|
285
|
+
toEntity: relationship.to,
|
|
286
|
+
fromRelation: manifest?.dbtProvenance?.nodes[manifest.modeling?.entities[relationship.from]?.dbtUniqueId ?? '']?.relation,
|
|
287
|
+
toRelation: manifest?.dbtProvenance?.nodes[manifest.modeling?.entities[relationship.to]?.dbtUniqueId ?? '']?.relation,
|
|
288
|
+
keys: relationship.keys,
|
|
289
|
+
cardinality: relationship.cardinality,
|
|
290
|
+
fanout: relationship.fanout,
|
|
291
|
+
importRefs: relationship.importRefs ?? [],
|
|
292
|
+
})),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function relationshipRecordKey(manifest, relationship) {
|
|
296
|
+
return Object.entries(manifest?.modeling?.relationships ?? {}).find(([, candidate]) => candidate === relationship || (Boolean(relationship.qualifiedId) && candidate.qualifiedId === relationship.qualifiedId))?.[0]
|
|
297
|
+
?? relationship.qualifiedId
|
|
298
|
+
?? relationship.id;
|
|
299
|
+
}
|
|
300
|
+
function blocked(code, entities, relationships, message) {
|
|
301
|
+
return { safe: false, code, message, entities, relationshipIds: relationships.map((relationship) => relationship.id), edges: [] };
|
|
302
|
+
}
|
|
303
|
+
function normalizeRelation(value) {
|
|
304
|
+
return value.replace(/["`\[\]]/g, '').trim().toLowerCase();
|
|
305
|
+
}
|
|
306
|
+
function normalizeColumn(value) {
|
|
307
|
+
return value.replace(/["`\[\]]/g, '').split('.').at(-1)?.trim().toLowerCase() ?? value.toLowerCase();
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=analytical-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytical-policy.js","sourceRoot":"","sources":["../../src/metadata/analytical-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CertifiedBlockFit } from './block-fit.js';
|
|
2
|
+
export interface CertifiedAdaptation {
|
|
3
|
+
/** The adapted SQL to execute. */
|
|
4
|
+
sql: string;
|
|
5
|
+
/** Trust qualifier composed onto the base `reviewed` label. */
|
|
6
|
+
trustQualifier: string;
|
|
7
|
+
/** Human-facing provenance line naming the donor block and the transform. */
|
|
8
|
+
provenance: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Adapt a certified block's SQL by filtering its OUTPUT to a single column=value.
|
|
12
|
+
* Returns null when the filter column is not one of `outputColumns` (so the wrapper
|
|
13
|
+
* would reference a non-existent column) or when inputs are blank.
|
|
14
|
+
*/
|
|
15
|
+
export declare function adaptCertifiedSqlWithFilter(input: {
|
|
16
|
+
certifiedSql: string;
|
|
17
|
+
filterColumn: string;
|
|
18
|
+
filterValue: string;
|
|
19
|
+
outputColumns: string[];
|
|
20
|
+
blockName: string;
|
|
21
|
+
}): CertifiedAdaptation | null;
|
|
22
|
+
/**
|
|
23
|
+
* Decide whether a context-only certified block can be safely adapted to answer the
|
|
24
|
+
* question with exactly ONE added filter. Returns the adaptation, or null when the
|
|
25
|
+
* block is not a single-delta fit (extra dimensions/outputs/grain mismatch, more
|
|
26
|
+
* than one filter), or the filter value does not resolve to exactly one block-output
|
|
27
|
+
* column. Null ⇒ the caller falls through to generation.
|
|
28
|
+
*/
|
|
29
|
+
export declare function planCertifiedAdaptation(input: {
|
|
30
|
+
blockFit: CertifiedBlockFit;
|
|
31
|
+
certifiedSql: string;
|
|
32
|
+
blockName: string;
|
|
33
|
+
blockOutputs: string[];
|
|
34
|
+
/** Resolve a filter VALUE (e.g. "food") to the physical column(s) that carry it. */
|
|
35
|
+
resolveFilterColumn: (value: string) => string[];
|
|
36
|
+
}): CertifiedAdaptation | null;
|
|
37
|
+
//# sourceMappingURL=block-adapt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-adapt.d.ts","sourceRoot":"","sources":["../../src/metadata/block-adapt.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,cAAc,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;CACpB;AAUD;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,mBAAmB,GAAG,IAAI,CAyB7B;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,oFAAoF;IACpF,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;CAClD,GAAG,mBAAmB,GAAG,IAAI,CAoB7B"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Certified-block adaptation lane (W2.2).
|
|
3
|
+
*
|
|
4
|
+
* When a certified block is CLOSE to answering but the question adds exactly ONE
|
|
5
|
+
* small delta — most commonly "the same certified breakdown, but just the <value>
|
|
6
|
+
* row" — regenerating from scratch throws away governed logic. Instead we adapt the
|
|
7
|
+
* certified SQL with a single, mechanically-safe transform and serve it at a trust
|
|
8
|
+
* tier BELOW certified (base `reviewed`, qualifier "adapted from certified block X")
|
|
9
|
+
* — never `certified`, because the adapted query itself was not human-reviewed.
|
|
10
|
+
*
|
|
11
|
+
* v1 handles the add-filter delta via an OUTPUT subquery wrapper:
|
|
12
|
+
* SELECT * FROM ( <certified sql> ) AS certified_derived WHERE <col> = <value>
|
|
13
|
+
* This is provably safe (it only RESTRICTS rows of the already-certified result)
|
|
14
|
+
* and dialect-neutral, and is valid exactly when the filter column is one of the
|
|
15
|
+
* block's OUTPUT columns (a dimension the certified block already groups by). If
|
|
16
|
+
* the filter would need to reach INPUT rows and re-aggregate, the column is not in
|
|
17
|
+
* the outputs and we return null → the caller falls through to generation.
|
|
18
|
+
*
|
|
19
|
+
* Adding a GROUP-BY dimension (re-graining the inner query) is intentionally NOT
|
|
20
|
+
* attempted in v1 — it cannot be done as a safe output wrapper.
|
|
21
|
+
*/
|
|
22
|
+
import { analyzeSqlReferences } from '@duckcodeailabs/dql-core';
|
|
23
|
+
function quoteAdaptValue(value) {
|
|
24
|
+
return /^-?\d+(\.\d+)?$/.test(value.trim()) ? value.trim() : `'${value.replace(/'/g, "''")}'`;
|
|
25
|
+
}
|
|
26
|
+
function normalizeColumn(name) {
|
|
27
|
+
return name.replace(/["`]/g, '').trim().toLowerCase();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Adapt a certified block's SQL by filtering its OUTPUT to a single column=value.
|
|
31
|
+
* Returns null when the filter column is not one of `outputColumns` (so the wrapper
|
|
32
|
+
* would reference a non-existent column) or when inputs are blank.
|
|
33
|
+
*/
|
|
34
|
+
export function adaptCertifiedSqlWithFilter(input) {
|
|
35
|
+
const certifiedSql = input.certifiedSql?.trim();
|
|
36
|
+
const filterColumn = input.filterColumn?.trim();
|
|
37
|
+
const filterValue = input.filterValue?.trim();
|
|
38
|
+
if (!certifiedSql || !filterColumn || !filterValue)
|
|
39
|
+
return null;
|
|
40
|
+
// The filter column MUST be an output of the certified query, else the wrapper
|
|
41
|
+
// references a column that does not exist in the subquery result.
|
|
42
|
+
const outputs = new Set(input.outputColumns.map(normalizeColumn));
|
|
43
|
+
if (!outputs.has(normalizeColumn(filterColumn)))
|
|
44
|
+
return null;
|
|
45
|
+
// The certified SQL must be a single parseable read-only SELECT/WITH so wrapping
|
|
46
|
+
// it in a subquery is safe.
|
|
47
|
+
const analysis = analyzeSqlReferences(certifiedSql);
|
|
48
|
+
if (!analysis.parsed)
|
|
49
|
+
return null;
|
|
50
|
+
const stripped = certifiedSql.replace(/;\s*$/, '');
|
|
51
|
+
const sql = `SELECT * FROM (\n${stripped}\n) AS certified_derived\nWHERE ${filterColumn} = ${quoteAdaptValue(filterValue)}`;
|
|
52
|
+
return {
|
|
53
|
+
sql,
|
|
54
|
+
trustQualifier: `adapted from certified block ${input.blockName}`,
|
|
55
|
+
provenance: `Derived from certified block "${input.blockName}" by filtering its result to ` +
|
|
56
|
+
`${filterColumn} = ${quoteAdaptValue(filterValue)}. Not itself certified — review before stakeholder reuse.`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Decide whether a context-only certified block can be safely adapted to answer the
|
|
61
|
+
* question with exactly ONE added filter. Returns the adaptation, or null when the
|
|
62
|
+
* block is not a single-delta fit (extra dimensions/outputs/grain mismatch, more
|
|
63
|
+
* than one filter), or the filter value does not resolve to exactly one block-output
|
|
64
|
+
* column. Null ⇒ the caller falls through to generation.
|
|
65
|
+
*/
|
|
66
|
+
export function planCertifiedAdaptation(input) {
|
|
67
|
+
const fit = input.blockFit;
|
|
68
|
+
// The ONLY delta may be a single unsupported filter — nothing else missing.
|
|
69
|
+
if (fit.kind !== 'context_only')
|
|
70
|
+
return null;
|
|
71
|
+
if (fit.missingDimensions.length > 0 || fit.missingOutputs.length > 0 || fit.grainMismatch)
|
|
72
|
+
return null;
|
|
73
|
+
if (fit.unsupportedFilters.length !== 1)
|
|
74
|
+
return null;
|
|
75
|
+
const filterValue = fit.unsupportedFilters[0];
|
|
76
|
+
const outputs = new Set(input.blockOutputs.map((column) => normalizeColumn(column)));
|
|
77
|
+
const columns = input.resolveFilterColumn(filterValue).filter((column) => outputs.has(normalizeColumn(column)));
|
|
78
|
+
// Exactly one block-output column must carry the value; ambiguous or input-only ⇒ fall through.
|
|
79
|
+
if (columns.length !== 1)
|
|
80
|
+
return null;
|
|
81
|
+
return adaptCertifiedSqlWithFilter({
|
|
82
|
+
certifiedSql: input.certifiedSql,
|
|
83
|
+
filterColumn: columns[0],
|
|
84
|
+
filterValue,
|
|
85
|
+
outputColumns: input.blockOutputs,
|
|
86
|
+
blockName: input.blockName,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=block-adapt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-adapt.js","sourceRoot":"","sources":["../../src/metadata/block-adapt.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,7 +30,7 @@ export function evaluateCertifiedBlockFit(input) {
|
|
|
30
30
|
const missingOutputs = uniqueStrings(requiredOutputs.filter((output) => !outputRequirementCovered(output, block)));
|
|
31
31
|
const measureMatch = requestedMeasures.length === 0
|
|
32
32
|
|| requestedMeasures.some((measure) => blockMeasures.has(measure) || block.textTokens.has(measure) || outputHasEntity(blockOutputs, measure));
|
|
33
|
-
const unsupportedFilters = unsupportedRequestedFilters(requested, block);
|
|
33
|
+
const unsupportedFilters = unsupportedRequestedFilters(requested, block, input.question);
|
|
34
34
|
const grainMismatch = requested.grain && block.grain && canonicalToken(requested.grain) !== block.grain
|
|
35
35
|
&& !blockDimensions.has(canonicalToken(requested.grain))
|
|
36
36
|
? `certified block grain=${block.grain} does not cover requested grain=${canonicalToken(requested.grain)}`
|
|
@@ -135,6 +135,8 @@ function blockShape(block) {
|
|
|
135
135
|
...stringArray(record.allowedFilters),
|
|
136
136
|
...filterBindingNames(payload.filterBindings),
|
|
137
137
|
...filterBindingNames(record.filterBindings),
|
|
138
|
+
...parameterNames(payload.parameters),
|
|
139
|
+
...parameterNames(record.parameters),
|
|
138
140
|
].map(canonicalToken).filter(Boolean));
|
|
139
141
|
const grain = canonicalToken(stringValue(payload.grain) ?? stringValue(record.grain) ?? explicitDimensions[0] ?? '');
|
|
140
142
|
const relevance = outputs.length + dimensions.length + measures.length;
|
|
@@ -150,6 +152,13 @@ function blockShape(block) {
|
|
|
150
152
|
inferredContract: stringArray(payload.declaredOutputs).length === 0 && stringArray(record.declaredOutputs).length === 0,
|
|
151
153
|
};
|
|
152
154
|
}
|
|
155
|
+
function parameterNames(value) {
|
|
156
|
+
if (!Array.isArray(value))
|
|
157
|
+
return [];
|
|
158
|
+
return value.flatMap((item) => item && typeof item === 'object' && typeof item.name === 'string'
|
|
159
|
+
? [item.name]
|
|
160
|
+
: []);
|
|
161
|
+
}
|
|
153
162
|
function inferredTextDimensions(text) {
|
|
154
163
|
const normalized = text.replace(/[_-]+/g, ' ').toLowerCase();
|
|
155
164
|
const inferred = [];
|
|
@@ -164,16 +173,40 @@ function inferredTextDimensions(text) {
|
|
|
164
173
|
}
|
|
165
174
|
return uniqueStrings(inferred);
|
|
166
175
|
}
|
|
167
|
-
function unsupportedRequestedFilters(requested, block) {
|
|
176
|
+
function unsupportedRequestedFilters(requested, block, question) {
|
|
168
177
|
const requestedFilters = uniqueStrings([
|
|
169
178
|
...requested.filters,
|
|
170
179
|
...requested.followUpReferences.flatMap((ref) => ref.resolvedValues ?? []),
|
|
171
|
-
].map(canonicalToken).filter(Boolean));
|
|
172
|
-
if (requestedFilters.length === 0
|
|
180
|
+
].map(canonicalToken).filter((filter) => Boolean(filter) && !isTemporalFilter(filter)));
|
|
181
|
+
if (requestedFilters.length === 0)
|
|
173
182
|
return [];
|
|
183
|
+
// A filtered question cannot be answered exactly by an unparameterized block.
|
|
184
|
+
// Returning [] here used to erase the user's restriction and let a broad
|
|
185
|
+
// certified ranking (for example all-customer lifetime spend) answer a
|
|
186
|
+
// category-specific question. A block may still be useful as context, but it
|
|
187
|
+
// cannot terminate the certified lane unless it exposes the requested value
|
|
188
|
+
// through a filter or dimension contract.
|
|
189
|
+
if (block.filters.length === 0) {
|
|
190
|
+
// A certified breakdown can be safely narrowed on one of its own output
|
|
191
|
+
// dimensions when the question explicitly names that binding ("by segment
|
|
192
|
+
// for Enterprise"). The adaptation lane applies the actual restriction.
|
|
193
|
+
const explicitlyFilterable = block.dimensions.some((dimension) => {
|
|
194
|
+
const words = dimension.replace(/_/g, '[ _-]+');
|
|
195
|
+
return new RegExp(`\\b(?:by|per)\\s+${words}\\b`, 'i').test(question)
|
|
196
|
+
|| new RegExp(`\\b${words}\\s*(?:=|:|is|equals)`, 'i').test(question);
|
|
197
|
+
});
|
|
198
|
+
if (explicitlyFilterable)
|
|
199
|
+
return [];
|
|
200
|
+
return requestedFilters.filter((filter) => !block.dimensions.includes(filter));
|
|
201
|
+
}
|
|
174
202
|
const blockFilters = new Set(block.filters);
|
|
175
203
|
return requestedFilters.filter((filter) => !blockFilters.has(filter) && !block.dimensions.includes(filter));
|
|
176
204
|
}
|
|
205
|
+
function isTemporalFilter(filter) {
|
|
206
|
+
return /^(?:day|week|month|quarter|year|season)$/.test(filter)
|
|
207
|
+
|| /^(?:last|this|next|previous|prior|current)_(?:day|week|month|quarter|year|season)$/.test(filter)
|
|
208
|
+
|| /^\d{4}$/.test(filter);
|
|
209
|
+
}
|
|
177
210
|
function topNFitAction(requested, block) {
|
|
178
211
|
if (!requested.topN)
|
|
179
212
|
return 'none';
|