@trilogy-data/trilogy-studio-components 0.1.24 → 0.1.26
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/README.md +5 -0
- package/dist/AssetAutoImporter-CBzvBaOt.js +405 -0
- package/dist/ChatCreatorModal-CL-jBnc3.js +218 -0
- package/dist/ChatModelSelect-Do-VRUFc.js +40 -0
- package/dist/{CommunityModels-CsDDXyIL.js → CommunityModels-Cmro0hV3.js} +101 -70
- package/dist/{Dashboard-BJfgRfRy.js → Dashboard-BH9ZwMVJ.js} +8 -8
- package/dist/{DashboardCreatorInline-wAlx6oH7.js → DashboardCreatorInline-CDU7kLy3.js} +3 -3
- package/dist/{DashboardImportSelector-D6ufVZIk.js → DashboardImportSelector-DQSvu9T-.js} +1 -1
- package/dist/{DashboardMobile-DoOyBHN7.js → DashboardMobile-BUPtdN2V.js} +4 -4
- package/dist/{Editor-Bs-h7edF.js → Editor-DQsYpkwl.js} +6 -6
- package/dist/{GlobalChatPanel-YsDZGxg8.js → GlobalChatPanel-DhnOaige.js} +155 -119
- package/dist/{IDE-CLF-KH2r.js → IDE-HLa8vybk.js} +9 -9
- package/dist/{JobsView-D1pp2xi5.js → JobsView-B97HBoIE.js} +8 -8
- package/dist/{LLMChat-CwM_brSg.js → LLMChat-CxJcPLEG.js} +1 -1
- package/dist/{LLMView-BiwCZb3Q.js → LLMView-C1MUf0c2.js} +21 -21
- package/dist/{MobileIDE-DrWh_OlB.js → MobileIDE-BpqXoaMR.js} +258 -253
- package/dist/{ModelCard-CsW4UwZI.js → ModelCard-BhN25jaV.js} +10 -10
- package/dist/{ModelCreator-C2douDis.js → ModelCreator-CCdC7EXt.js} +1 -1
- package/dist/{ModelView-D7ENFV2U.js → ModelView-DUDA_PtZ.js} +3 -3
- package/dist/{PageLoading-C5WK5i5Y.js → PageLoading-BzniJM0C.js} +622 -619
- package/dist/{ResultComponent-1MQlbELb.js → ResultComponent-CAn_r3BL.js} +3 -3
- package/dist/{Results-DXIYS4iW.js → Results-BJtlwebY.js} +3 -3
- package/dist/{SymbolsPane-BM6dUN-h.js → SymbolsPane-KYSwN9Sv.js} +1 -1
- package/dist/{TutorialPage-HXbXCznh.js → TutorialPage-NRcee6Dx.js} +8 -8
- package/dist/{VegaLiteChart-CS4h_t-K.js → VegaLiteChart-D1slfOUY.js} +11227 -11195
- package/dist/{WelcomePage-CLKvAvk-.js → WelcomePage-D5mU2x1L.js} +1 -1
- package/dist/components/AssetAutoImporter.vue.d.ts.map +1 -1
- package/dist/components/TipsCTA.vue.d.ts +1 -1
- package/dist/components/VegaLiteChart.vue.d.ts.map +1 -1
- package/dist/components/chartHelpers.d.ts +9 -1
- package/dist/components/chartHelpers.d.ts.map +1 -1
- package/dist/components/chartRenderManager.d.ts +4 -1
- package/dist/components/chartRenderManager.d.ts.map +1 -1
- package/dist/components/community/AddStoreModal.vue.d.ts +1 -1
- package/dist/components/community/AddStoreModal.vue.d.ts.map +1 -1
- package/dist/components/community/CommunityModelCard.vue.d.ts.map +1 -1
- package/dist/components/editor/Editor.vue.d.ts.map +1 -1
- package/dist/components/jobs/JobsAddStoreModal.vue.d.ts.map +1 -1
- package/dist/components/llm/ChatCreatorModal.vue.d.ts +17 -1
- package/dist/components/llm/ChatCreatorModal.vue.d.ts.map +1 -1
- package/dist/components/llm/ChatModelSelect.vue.d.ts +13 -0
- package/dist/components/llm/ChatModelSelect.vue.d.ts.map +1 -0
- package/dist/components/llm/GlobalChatPanel.vue.d.ts.map +1 -1
- package/dist/components/sidebar/CommunityModelList.vue.d.ts +33 -50
- package/dist/components/sidebar/CommunityModelList.vue.d.ts.map +1 -1
- package/dist/components/sidebar/CommunityModelListItem.vue.d.ts.map +1 -1
- package/dist/components/sidebar/EditorList.vue.d.ts +32 -49
- package/dist/components/sidebar/EditorList.vue.d.ts.map +1 -1
- package/dist/components/sidebar/Sidebar.vue.d.ts +65 -99
- package/dist/components/sidebar/Sidebar.vue.d.ts.map +1 -1
- package/dist/dashboard.js +52 -52
- package/dist/{dashboardStore-BzmI4OCV.js → dashboardStore-ppM6nGks.js} +33 -33
- package/dist/dashboards/layerSpec.d.ts +38 -4
- package/dist/dashboards/layerSpec.d.ts.map +1 -1
- package/dist/dashboards/lineAreaSpec.d.ts +8 -80
- package/dist/dashboards/lineAreaSpec.d.ts.map +1 -1
- package/dist/dashboards/mapSpec.d.ts +1 -0
- package/dist/dashboards/mapSpec.d.ts.map +1 -1
- package/dist/dashboards/spec.d.ts.map +1 -1
- package/dist/data/tutorial/documentation.d.ts.map +1 -1
- package/dist/data/tutorial/startupScripts.d.ts +3 -0
- package/dist/data/tutorial/startupScripts.d.ts.map +1 -0
- package/dist/{demoSetup-vmiMeH4_.js → demoSetup-mwa-RDC6.js} +19 -18
- package/dist/{editorRefinementRuntime-hC3u8GMO.js → editorRefinementRuntime-DoP9P5J2.js} +892 -871
- package/dist/{editorRefinementToolExecutor-Bomrpmns.js → editorRefinementToolExecutor-CVY3FA7k.js} +392 -267
- package/dist/{editorStore-Bj3Qnatp.js → editorStore-BhhmXbi-.js} +230 -183
- package/dist/entry.dashboard.d.ts +1 -0
- package/dist/entry.dashboard.d.ts.map +1 -1
- package/dist/{globalChatRuntime-DXI2KyGn.js → globalChatRuntime-DWZxeS57.js} +13 -13
- package/dist/{helpers-CFp2QCvv.js → helpers-BLQAgX3C.js} +17 -16
- package/dist/llm/chatAgentPrompt.d.ts.map +1 -1
- package/dist/llm/data/constants.d.ts +1 -1
- package/dist/llm/data/constants.d.ts.map +1 -1
- package/dist/llm/data/trilogySyntax.generated.d.ts +7 -1
- package/dist/llm/data/trilogySyntax.generated.d.ts.map +1 -1
- package/dist/llm/docsIndex.d.ts.map +1 -1
- package/dist/llm/openai.d.ts +2 -1
- package/dist/llm/openai.d.ts.map +1 -1
- package/dist/{llm-BuEsqH9Q.js → llm-BBOVWb-F.js} +7 -5
- package/dist/llm.js +3 -3
- package/dist/{llmStore-x5lRrKgx.js → llmStore-DJM2JhzY.js} +2 -2
- package/dist/{localStorage-C-ZtI6YA.js → localStorage-D9uyOFEO.js} +14 -14
- package/dist/models/helpers.d.ts.map +1 -1
- package/dist/monaco.js +1 -1
- package/dist/{reconcile-E5LR5p_f.js → reconcile-Dyf4UZmj.js} +1 -1
- package/dist/{remoteStoreSync-MwuJpHHt.js → remoteStoreSync-Z-yjlYtI.js} +1 -1
- package/dist/remotes/displayHelpers.d.ts.map +1 -1
- package/dist/remotes/genericStoreMetadata.d.ts +1 -0
- package/dist/remotes/genericStoreMetadata.d.ts.map +1 -1
- package/dist/remotes/models.d.ts +37 -5
- package/dist/remotes/models.d.ts.map +1 -1
- package/dist/remotes/staticStorePresets.d.ts +22 -0
- package/dist/remotes/staticStorePresets.d.ts.map +1 -0
- package/dist/remotes/storeService.d.ts +25 -4
- package/dist/remotes/storeService.d.ts.map +1 -1
- package/dist/stores/communityApiStore.d.ts +32 -60
- package/dist/stores/communityApiStore.d.ts.map +1 -1
- package/dist/stores/connectionStore.d.ts.map +1 -1
- package/dist/stores/resolver.d.ts +5 -0
- package/dist/stores/resolver.d.ts.map +1 -1
- package/dist/stores/urlStore.d.ts +1 -0
- package/dist/stores/urlStore.d.ts.map +1 -1
- package/dist/{stores-D-gHjj-_.js → stores-BT9hTLny.js} +47 -47
- package/dist/stores.js +7 -7
- package/dist/trilogy-studio-components.css +1 -1
- package/dist/{urlStore-Dgmwx1YI.js → urlStore-52HG6lsX.js} +1 -0
- package/dist/{useChatWithTools-BlCJrJT7.js → useChatWithTools-B0HZ_DVh.js} +1 -1
- package/dist/{useDashboard-CVlbV17V.js → useDashboard-CdSnPe_6.js} +186 -186
- package/dist/{useGlobalChatPanel-CNARTFYu.js → useGlobalChatPanel-D_0luajQ.js} +2 -2
- package/dist/views/MobileIDE.vue.d.ts.map +1 -1
- package/dist/views/TutorialPage.vue.d.ts +32 -49
- package/dist/views/TutorialPage.vue.d.ts.map +1 -1
- package/dist/views.js +16 -16
- package/package.json +1 -1
- package/dist/AssetAutoImporter-BD1P91Hq.js +0 -403
- package/dist/ChatCreatorModal-BTqY06B6.js +0 -177
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { r as e } from "./results-DTgAKr5j.js";
|
|
2
|
-
import { $ as t,
|
|
3
|
-
import { s as
|
|
4
|
-
import { t as
|
|
5
|
-
import { defineStore as
|
|
2
|
+
import { $t as t, At as n, Gt as r, Jt as i, Kt as a, L as o, Mt as s, Nt as c, Ot as l, Q as u, Qt as d, R as f, U as p, V as m, Xt as h, Yt as g, Z as ee, Zt as te, a as _, at as ne, bt as re, c as ie, dt as ae, et as oe, ft as se, i as ce, it as le, jt as ue, k as v, l as de, lt as fe, mt as pe, n as me, nn as he, o as ge, ot as y, pt as _e, q as ve, qt as ye, r as b, s as be, st as xe, t as Se, tn as x, ut as Ce, xt as S, y as C, z as w } from "./editorRefinementToolExecutor-CVY3FA7k.js";
|
|
3
|
+
import { s as we } from "./base-C8IuZWqY.js";
|
|
4
|
+
import { t as Te } from "./connectionHistoryStorage-DKJHiKJY.js";
|
|
5
|
+
import { defineStore as Ee } from "pinia";
|
|
6
6
|
//#region editors/fileTypes.ts
|
|
7
|
-
var
|
|
7
|
+
var T = (e) => {
|
|
8
8
|
let t = e.lastIndexOf(".");
|
|
9
9
|
return t === -1 ? "" : e.slice(t).toLowerCase();
|
|
10
|
-
},
|
|
10
|
+
}, De = (e) => {
|
|
11
11
|
switch (e) {
|
|
12
12
|
case "sql": return ".sql";
|
|
13
13
|
case "python": return ".py";
|
|
@@ -15,11 +15,11 @@ var S = (e) => {
|
|
|
15
15
|
case "csv": return ".csv";
|
|
16
16
|
default: return ".preql";
|
|
17
17
|
}
|
|
18
|
-
},
|
|
18
|
+
}, E = (e, t) => {
|
|
19
19
|
let n = e.trim();
|
|
20
|
-
return !n || /\.(preql|sql|csv|py|md|markdown|txt)$/i.test(n) ? n : `${n}${
|
|
21
|
-
},
|
|
22
|
-
switch (
|
|
20
|
+
return !n || /\.(preql|sql|csv|py|md|markdown|txt)$/i.test(n) ? n : `${n}${De(t)}`;
|
|
21
|
+
}, D = (e) => {
|
|
22
|
+
switch (T(e)) {
|
|
23
23
|
case ".preql": return "preql";
|
|
24
24
|
case ".sql": return "sql";
|
|
25
25
|
case ".py": return "python";
|
|
@@ -29,7 +29,7 @@ var S = (e) => {
|
|
|
29
29
|
case ".csv": return "csv";
|
|
30
30
|
default: return null;
|
|
31
31
|
}
|
|
32
|
-
},
|
|
32
|
+
}, Oe = (e) => {
|
|
33
33
|
switch (e) {
|
|
34
34
|
case "sql":
|
|
35
35
|
case "csv": return "sql";
|
|
@@ -37,23 +37,23 @@ var S = (e) => {
|
|
|
37
37
|
case "markdown": return "markdown";
|
|
38
38
|
default: return "trilogy";
|
|
39
39
|
}
|
|
40
|
-
},
|
|
41
|
-
let t =
|
|
40
|
+
}, O = (e) => e === "trilogy" || e === "preql", ke = (e) => O(e), Ae = (e) => O(e), je = (e) => O(e), Me = (e) => O(e) || e === "sql", Ne = (e) => e !== "python" && e !== "markdown" && e !== "csv", Pe = (e) => {
|
|
41
|
+
let t = T(e);
|
|
42
42
|
return t !== ".py" && t !== ".md" && t !== ".csv";
|
|
43
|
-
},
|
|
43
|
+
}, k = /* @__PURE__ */ function(e) {
|
|
44
44
|
return e.SOURCE = "source", e.STARTUP_SCRIPT = "startup_script", e;
|
|
45
45
|
}({});
|
|
46
|
-
function
|
|
47
|
-
return e.storage === "remote" ?
|
|
46
|
+
function Fe(e) {
|
|
47
|
+
return e.storage === "remote" ? we({
|
|
48
48
|
name: e.connection,
|
|
49
49
|
storage: "remote",
|
|
50
50
|
remoteStoreId: e.remoteStoreId || null
|
|
51
|
-
}) :
|
|
51
|
+
}) : we({
|
|
52
52
|
name: e.connection,
|
|
53
53
|
storage: "local"
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
var
|
|
56
|
+
var Ie = class t {
|
|
57
57
|
id;
|
|
58
58
|
name;
|
|
59
59
|
type;
|
|
@@ -94,11 +94,11 @@ var Re = class t {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
constructor({ id: t, name: n, type: r, connection: i, storage: a, contents: o = null, tags: s = null, remoteStoreId: c = null, remotePath: l = null, remoteOriginalPath: u = null, remotePersisted: d = !1 }) {
|
|
97
|
-
this.id = t, this.name = a === "remote" ?
|
|
97
|
+
this.id = t, this.name = a === "remote" ? E(n, r) : n, this.type = D(this.name) ?? r, this.syntax = this.type === "python" ? "python" : "preql", this.connection = i, this.connectionId = Fe({
|
|
98
98
|
connection: i,
|
|
99
99
|
storage: a,
|
|
100
100
|
remoteStoreId: c
|
|
101
|
-
}), this.results = new e(/* @__PURE__ */ new Map(), []), this.contents = o || this.defaultContents(r), this.loading = !1, this.error = null, this.duration = null, this.status_code = 200, this.generated_sql = null, this.executed_contents = null, this.storage = a, this.tags = s || [], this.startTime = null, this.cancelCallback = null, this.changed = !0, this.deleted = !1, this.chartFromStatement = !1, this.chartStatementWarnings = [], this.chartLayers = null, this.completionSymbols = [], this.remoteStoreId = c, this.remotePath = a === "remote" ?
|
|
101
|
+
}), this.results = new e(/* @__PURE__ */ new Map(), []), this.contents = o || this.defaultContents(r), this.loading = !1, this.error = null, this.duration = null, this.status_code = 200, this.generated_sql = null, this.executed_contents = null, this.storage = a, this.tags = s || [], this.startTime = null, this.cancelCallback = null, this.changed = !0, this.deleted = !1, this.chartFromStatement = !1, this.chartStatementWarnings = [], this.chartLayers = null, this.completionSymbols = [], this.remoteStoreId = c, this.remotePath = a === "remote" ? E(l || this.name, r) : l, this.remoteOriginalPath = u, this.remotePersisted = d;
|
|
102
102
|
}
|
|
103
103
|
getAutocomplete(e) {
|
|
104
104
|
return this.completionSymbols.filter((t) => t.label.startsWith(e));
|
|
@@ -110,18 +110,18 @@ var Re = class t {
|
|
|
110
110
|
this.contents !== e && (this.contents = e, this.changed = !0);
|
|
111
111
|
}
|
|
112
112
|
setName(e) {
|
|
113
|
-
let t = this.storage === "remote" ?
|
|
113
|
+
let t = this.storage === "remote" ? E(e, this.type) : e;
|
|
114
114
|
if (this.name === t) return;
|
|
115
115
|
if (this.changed = !0, this.storage === "remote") {
|
|
116
116
|
let e = this.remotePath || this.name;
|
|
117
117
|
!this.remoteOriginalPath && this.remotePersisted && e !== t && (this.remoteOriginalPath = e), this.remotePath = t;
|
|
118
118
|
}
|
|
119
119
|
this.name = t;
|
|
120
|
-
let n =
|
|
120
|
+
let n = D(this.name);
|
|
121
121
|
n && this.type !== n && (this.type = n);
|
|
122
122
|
}
|
|
123
123
|
setConnection(e) {
|
|
124
|
-
this.connection !== e && (this.connection = e, this.connectionId =
|
|
124
|
+
this.connection !== e && (this.connection = e, this.connectionId = Fe({
|
|
125
125
|
connection: e,
|
|
126
126
|
storage: this.storage,
|
|
127
127
|
remoteStoreId: this.remoteStoreId
|
|
@@ -189,12 +189,12 @@ var Re = class t {
|
|
|
189
189
|
remoteOriginalPath: r.remoteOriginalPath || null,
|
|
190
190
|
remotePersisted: r.remotePersisted || !1
|
|
191
191
|
});
|
|
192
|
-
return i.syntax = r.syntax || "preql", i.results = r.results ? e.fromJSON(r.results) : new e(/* @__PURE__ */ new Map(), []), i.loading = r.loading || !1, i.error = r.error || null, i.status_code = r.status_code || 200, i.duration = r.duration || null, i.generated_sql = r.generated_sql || null, i.executed_contents = r.executed_contents || null, i.changed = !1, i.tags = r.tags ? r.tags.map((e) => Object.values(
|
|
192
|
+
return i.syntax = r.syntax || "preql", i.results = r.results ? e.fromJSON(r.results) : new e(/* @__PURE__ */ new Map(), []), i.loading = r.loading || !1, i.error = r.error || null, i.status_code = r.status_code || 200, i.duration = r.duration || null, i.generated_sql = r.generated_sql || null, i.executed_contents = r.executed_contents || null, i.changed = !1, i.tags = r.tags ? r.tags.map((e) => Object.values(k).includes(e) ? e : null).filter((e) => e !== null) : [], i.chartConfig = r.chartConfig || void 0, i.remoteStoreId = r.remoteStoreId || null, i.remotePath = r.remotePath || null, i.remoteOriginalPath = r.remoteOriginalPath || null, i.remotePersisted = r.remotePersisted || !1, i;
|
|
193
193
|
}
|
|
194
194
|
};
|
|
195
195
|
//#endregion
|
|
196
196
|
//#region editors/helpers.ts
|
|
197
|
-
function
|
|
197
|
+
function Le(e, t, n, r) {
|
|
198
198
|
let i = [], a = /* @__PURE__ */ new Set(), o = e.reduce((e, t) => (e[t.id] = t, e), {}), s = Object.values(t).sort((e, t) => {
|
|
199
199
|
let n = e.storage.localeCompare(t.storage);
|
|
200
200
|
if (n !== 0) return n;
|
|
@@ -302,17 +302,17 @@ function ze(e, t, n, r) {
|
|
|
302
302
|
}
|
|
303
303
|
//#endregion
|
|
304
304
|
//#region llm/overseerAgentPrompt.ts
|
|
305
|
-
var
|
|
306
|
-
function
|
|
307
|
-
let t = e.lastMessage ? ` — "${
|
|
305
|
+
var Re = ["architect", "analyst"], ze = "You are an ANALYST subchat. Your job is to answer the overseer's question — run queries, build charts, surface findings.\n\nWhen done, call return_to_user with a summary written for the person reading the analysis, not for another engineer. Unless your task gives contradicting instructions:\n- Lead with the story in the data: the key insights, what they mean, and any caveats that change how they should be read (data gaps, small samples, definitional quirks).\n- Keep technical details — queries run, tables used, methodology, tool mechanics — out of the top summary. Put anything worth keeping in a short \"Technical notes\" section at the bottom.\n- Focus on the data and the narrative, not the how.", Be = "You are the OVERSEER of a Trilogy Explorer workspace. Your job is to coordinate work — never to query data directly.\n\nORCHESTRATION RULES:\n- You delegate work to two kinds of subchat: 'architect' (sets up data models from raw files — registers tables, defines Trilogy concepts) and 'analyst' (runs queries, builds charts, produces dashboards).\n- You have NO direct query tools. Always delegate via spawn_subchat.\n- Subchats run asynchronously. spawn_subchat returns immediately with a subchat id and 'running' status. When the subchat finishes, its summary appears in your conversation as a system-injected message and you'll be re-invoked to react.\n- You may follow up with a running OR finished subchat via send_to_subchat — useful for clarifying questions or asking it to extend its work.\n- list_subchats gives you the current snapshot at any time (also visible in the prompt above).\n- When you've handled the user's request and have nothing more to delegate or report, call return_to_user with a concise summary.\n\nDELEGATION GUIDELINES:\n- Architect subchats: use for \"set up a model\", \"load these files\", \"define concepts for X\", schema work.\n- Prefer to keep subchats scoped to a specific goal; only reuse a subchat when the new request overlaps with the existing work.\n- Analyst subchats: use for \"show me revenue trend\", \"build a dashboard\", \"what does the data look like\", any question that produces results or visuals.\n- Give subchats a complete, self-contained task description — they don't see your conversation with the user. Include any relevant file names, connection names, or constraints.\n\nREPORTS (analytical memos):\n- When the user asks for a \"report\", \"memo\", \"writeup\", \"analysis\", or anything that should produce a source-backed narrative deliverable rather than a free-form interactive board, use the create_report tool. This creates a new report-mode dashboard in the active project and queues an initial prompt for the report's dedicated authoring agent.\n- After create_report returns, tell the user the report is open in the sidebar; the dashboard chat panel attached to that report does the authoring and renders the executive memo + claim sections live as the agent works.\n- Do NOT delegate report authoring to an analyst subchat — analysts produce dashboards, the report agent produces narrative memos with provenance and confidence.\n";
|
|
306
|
+
function Ve(e) {
|
|
307
|
+
let t = e.lastMessage ? ` — "${He(e.lastMessage, 120)}"` : "";
|
|
308
308
|
return ` - ${e.id} [${e.kind}] (${e.status})${t}`;
|
|
309
309
|
}
|
|
310
|
-
function
|
|
310
|
+
function He(e, t) {
|
|
311
311
|
return e.length <= t ? e : e.slice(0, t - 1) + "…";
|
|
312
312
|
}
|
|
313
|
-
function
|
|
314
|
-
let { projectName: t, projectDescription: n, subchats: r, availableConnections: i, availableEditors: a, instructionsOverride: o } = e, s = r.length > 0 ? `ACTIVE SUBCHATS:\n${r.map(
|
|
315
|
-
return `${o?.trim() ||
|
|
313
|
+
function Ue(e) {
|
|
314
|
+
let { projectName: t, projectDescription: n, subchats: r, availableConnections: i, availableEditors: a, instructionsOverride: o } = e, s = r.length > 0 ? `ACTIVE SUBCHATS:\n${r.map(Ve).join("\n")}` : "No subchats spawned yet.", c = a.length > 0 ? `Attached files: ${a.join(", ")}` : "No files attached yet.";
|
|
315
|
+
return `${o?.trim() || Be}
|
|
316
316
|
|
|
317
317
|
${`WORKSPACE: ${t || "unnamed"}${n ? `\n${n}` : ""}
|
|
318
318
|
|
|
@@ -322,7 +322,7 @@ DATA CONNECTIONS: ${i.length > 0 ? i.join(", ") : "none configured"}
|
|
|
322
322
|
${s}`}
|
|
323
323
|
`;
|
|
324
324
|
}
|
|
325
|
-
var
|
|
325
|
+
var We = [
|
|
326
326
|
{
|
|
327
327
|
name: "spawn_subchat",
|
|
328
328
|
description: "Delegate work to a new subchat. Returns immediately with the subchat's id; the subchat runs asynchronously and its summary is injected back into your conversation when it terminates. Use 'architect' for data model setup, 'analyst' for queries / analysis / charts.",
|
|
@@ -331,7 +331,7 @@ var Ge = [
|
|
|
331
331
|
properties: {
|
|
332
332
|
kind: {
|
|
333
333
|
type: "string",
|
|
334
|
-
enum:
|
|
334
|
+
enum: Re,
|
|
335
335
|
description: "'architect' for data model / schema work; 'analyst' for queries, analysis, and charts."
|
|
336
336
|
},
|
|
337
337
|
task: {
|
|
@@ -415,21 +415,21 @@ var Ge = [
|
|
|
415
415
|
required: ["subchat_id"]
|
|
416
416
|
}
|
|
417
417
|
},
|
|
418
|
-
|
|
418
|
+
y
|
|
419
419
|
];
|
|
420
420
|
//#endregion
|
|
421
421
|
//#region llm/dashboardAgentTools.ts
|
|
422
|
-
function
|
|
422
|
+
function A(e, t) {
|
|
423
423
|
return {
|
|
424
424
|
type: "string",
|
|
425
425
|
enum: e.map((e) => e.value),
|
|
426
426
|
description: `${t} ${e.map((e) => `"${e.value}": ${e.hint}`).join(" ")}`
|
|
427
427
|
};
|
|
428
428
|
}
|
|
429
|
-
var
|
|
429
|
+
var Ge = Object.fromEntries(d.map((e) => [e.key, {
|
|
430
430
|
type: "string",
|
|
431
431
|
description: `${e.label} color — ${e.hint} Accepts a hex string ("#101820"), rgb()/rgba()/hsl(), or a CSS color name. Pass an empty string to clear it back to the inherited theme color. Omit to leave it as-is.`
|
|
432
|
-
}])),
|
|
432
|
+
}])), j = [
|
|
433
433
|
{
|
|
434
434
|
name: "get_dashboard_state",
|
|
435
435
|
description: "Re-read the current state of the dashboard: title, description, active data connection, active data source, item list, and (in report mode) section structure. The dashboard state in your starting context is a snapshot from the beginning of the conversation and does NOT update as you work — call this after making changes, or whenever you are unsure whether your view is current. Pass item_id to read just one item instead of the whole dashboard.",
|
|
@@ -465,7 +465,7 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
|
|
|
465
465
|
},
|
|
466
466
|
{
|
|
467
467
|
name: "add_dashboard_item",
|
|
468
|
-
description: `Add a new item to the dashboard grid. Supports chart, table, markdown, filter, and freeform types. For charts and tables, provide a Trilogy query as content. For markdown items you can ALSO supply a 'query' to drive dynamic data — the query results are then available to the markdown via {field} template substitutions (see system prompt for full templating syntax). For freeform items, put the Trilogy query in 'content' and the widget markup in 'html' — the query and the rendering are authored separately, and the widget never issues queries of its own. ${
|
|
468
|
+
description: `Add a new item to the dashboard grid. Supports chart, table, markdown, filter, and freeform types. For charts and tables, provide a Trilogy query as content. For markdown items you can ALSO supply a 'query' to drive dynamic data — the query results are then available to the markdown via {field} template substitutions (see system prompt for full templating syntax). For freeform items, put the Trilogy query in 'content' and the widget markup in 'html' — the query and the rendering are authored separately, and the widget never issues queries of its own. ${ae}`,
|
|
469
469
|
input_schema: {
|
|
470
470
|
type: "object",
|
|
471
471
|
properties: {
|
|
@@ -497,7 +497,7 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
|
|
|
497
497
|
description: "Optional Trilogy query for MARKDOWN items only. When provided, the query is executed and its result rows are bound to the markdown template — you can reference fields with {field}, {data[N].field}, formats like {revenue:,.2f}, fallbacks like {field || \"N/A\"}, and loops {{#each data}}...{{/each}}. Ignored for non-markdown types."
|
|
498
498
|
},
|
|
499
499
|
chartConfig: {
|
|
500
|
-
...
|
|
500
|
+
...se,
|
|
501
501
|
description: "Chart configuration (only for chart type). If omitted, chart type is auto-detected from the data."
|
|
502
502
|
},
|
|
503
503
|
width: {
|
|
@@ -550,7 +550,7 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
|
|
|
550
550
|
description: "Change the item type"
|
|
551
551
|
},
|
|
552
552
|
chartConfig: {
|
|
553
|
-
...
|
|
553
|
+
...se,
|
|
554
554
|
description: "New chart configuration (for chart items)"
|
|
555
555
|
}
|
|
556
556
|
},
|
|
@@ -644,15 +644,15 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
|
|
|
644
644
|
input_schema: {
|
|
645
645
|
type: "object",
|
|
646
646
|
properties: {
|
|
647
|
-
preset:
|
|
648
|
-
corners:
|
|
649
|
-
density:
|
|
650
|
-
elevation:
|
|
647
|
+
preset: A(te, "Baseline look. Sets corners, density, elevation, and border treatment together; the individual fields below override whatever it chose."),
|
|
648
|
+
corners: A(a, "Card corner rounding."),
|
|
649
|
+
density: A(i, "Gutter width and padding. Denser fits more panels per screen; spacious suits fewer, larger panels."),
|
|
650
|
+
elevation: A(h, "Card drop shadow."),
|
|
651
651
|
mobileCards: {
|
|
652
652
|
type: "boolean",
|
|
653
653
|
description: "Keep the card treatment on narrow viewports. Default false, which flattens panels to the page background below 768px — the right choice for most dashboards, since cards on a phone waste horizontal space."
|
|
654
654
|
},
|
|
655
|
-
...
|
|
655
|
+
...Ge,
|
|
656
656
|
reset: {
|
|
657
657
|
type: "boolean",
|
|
658
658
|
description: "Clear the entire theme and go back to the app default, which follows the user's light/dark setting. Use this when the user says \"undo the styling\" or \"back to normal\". When true, every other field is ignored."
|
|
@@ -709,9 +709,9 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
|
|
|
709
709
|
required: []
|
|
710
710
|
}
|
|
711
711
|
},
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
],
|
|
712
|
+
_e,
|
|
713
|
+
y
|
|
714
|
+
], Ke = [
|
|
715
715
|
{
|
|
716
716
|
name: "set_executive_memo",
|
|
717
717
|
description: "Create or update the executive memo at the top of the report. Exactly one memo per report. The six fields ARE the memo — they map to the headline / verdict / magnitude / cause / action / confidence rubric. Pass `query` to bind live values into the prose via {field} templating. Calling this when a memo already exists updates it in place.",
|
|
@@ -842,35 +842,35 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
|
|
|
842
842
|
];
|
|
843
843
|
//#endregion
|
|
844
844
|
//#region llm/dashboardAgentPrompt.ts
|
|
845
|
-
function
|
|
846
|
-
let { dashboard: t, dataConnectionName:
|
|
847
|
-
if (
|
|
848
|
-
let e = t.gridItems[
|
|
845
|
+
function qe(e) {
|
|
846
|
+
let { dashboard: t, dataConnectionName: n, activeImports: r = [], isDataConnectionActive: i = !0, itemId: a } = e, o = Object.entries(t.gridItems);
|
|
847
|
+
if (a) {
|
|
848
|
+
let e = t.gridItems[a];
|
|
849
849
|
if (!e) {
|
|
850
|
-
let e =
|
|
851
|
-
return `Item "${
|
|
850
|
+
let e = o.map(([e]) => e).join(", ");
|
|
851
|
+
return `Item "${a}" not found on this dashboard.${e ? ` Known item IDs: ${e}` : " The dashboard is empty."}`;
|
|
852
852
|
}
|
|
853
|
-
return `CURRENT STATE OF ITEM [${
|
|
853
|
+
return `CURRENT STATE OF ITEM [${a}]:\n${Je(a, e)}`;
|
|
854
854
|
}
|
|
855
|
-
let
|
|
855
|
+
let s = n ? `ACTIVE DATA CONNECTION: ${n}${n && !i ? " (NOT CONNECTED - use connect_data_connection tool to connect before running queries)" : ""}` : "ACTIVE DATA CONNECTION: none selected. Ask the user which connection to use.", c = r.length > 0 ? `ACTIVE DATA SOURCE: ${r[0].name} (this is the import the dashboard itself uses — changing it via select_active_import will affect every existing item on the next refresh)` : "ACTIVE DATA SOURCE: none selected. Use select_active_import to select a data source, or list_available_imports to see what is available.", l = o.length > 0 ? `CURRENT DASHBOARD ITEMS (${o.length}):\n${o.map(([e, t]) => Je(e, t)).join("\n")}` : "The dashboard is currently empty.", u = t.layoutType === "report" ? `\n${Ye(t)}` : "";
|
|
856
856
|
return `DASHBOARD: "${t.name}"${t.description ? `\nDESCRIPTION: ${t.description}` : ""}
|
|
857
|
+
${s}
|
|
857
858
|
${c}
|
|
858
|
-
${
|
|
859
|
-
THEME: ${n(t.theme)}${d}
|
|
859
|
+
THEME: ${x(t.theme)}${u}
|
|
860
860
|
|
|
861
|
-
${
|
|
861
|
+
${l}`;
|
|
862
862
|
}
|
|
863
|
-
function
|
|
863
|
+
function Je(e, t) {
|
|
864
864
|
let n = typeof t.content == "string" ? t.content.substring(0, 80) : t.content?.markdown?.substring(0, 80) || "";
|
|
865
865
|
return `- [${e}] ${t.type}: "${t.name}" — ${n}${n.length >= 80 ? "..." : ""}`;
|
|
866
866
|
}
|
|
867
|
-
function
|
|
868
|
-
let t = Object.values(e.gridItems), n = t.filter((e) => e.type ===
|
|
867
|
+
function Ye(e) {
|
|
868
|
+
let t = Object.values(e.gridItems), n = t.filter((e) => e.type === l.MEMO).length, r = t.filter((e) => e.type === l.CLAIM).length, i = t.filter((e) => e.type === l.APPENDIX_HEADER).length;
|
|
869
869
|
return `CURRENT REPORT STRUCTURE: ${n} memo / ${r} claim section${r === 1 ? "" : "s"} / ${i} appendix divider${i === 1 ? "" : "s"}.`;
|
|
870
870
|
}
|
|
871
|
-
function
|
|
872
|
-
let { dashboard: t, availableConnections: n, availableConcepts: r, availableImportsForConnection: i = [] } = e, a = r && r.length > 0 ? `\n\nAVAILABLE FIELDS FOR QUERIES:\n${
|
|
873
|
-
return `${s ?
|
|
871
|
+
function Xe(e) {
|
|
872
|
+
let { dashboard: t, availableConnections: n, availableConcepts: r, availableImportsForConnection: i = [] } = e, a = r && r.length > 0 ? `\n\nAVAILABLE FIELDS FOR QUERIES:\n${pe(r)}` : "", o = i.length > 0 ? `\n\nAVAILABLE DATA SOURCES:\n${i.map((e) => `- ${e.name}`).join("\n")}` : "", s = t.layoutType === "report";
|
|
873
|
+
return `${s ? Qe() : ""}You are a dashboard assistant with full control over a live ${s ? "report (a single-column analytical memo)" : "dashboard"}. The user can see changes you make in real time.
|
|
874
874
|
|
|
875
875
|
${s ? "This is a REPORT, not a free-form dashboard. Reports render top-to-bottom as a single narrative column. Use the report tools (set_executive_memo, add_claim_section, add_appendix_header) to author structure; use add_dashboard_item only for evidence (charts, tables, markdown). The grid x/y/w/h still drives ordering — y determines vertical position; x and w are mostly cosmetic in report mode." : "You have tools to add, update, remove, and reposition items on the dashboard grid. The grid is 20 columns wide. Items can be charts, tables, markdown blocks, filters, or freeform custom-HTML widgets."}
|
|
876
876
|
|
|
@@ -880,7 +880,7 @@ The current dashboard title, items, active connection, and active data source ar
|
|
|
880
880
|
AVAILABLE DATA CONNECTIONS: ${n.length > 0 ? n.join(", ") : "None configured"}${o}
|
|
881
881
|
|
|
882
882
|
TRILOGY LANGUAGE REFERENCE:
|
|
883
|
-
${
|
|
883
|
+
${S}
|
|
884
884
|
${a}
|
|
885
885
|
|
|
886
886
|
IMPORTANT GUIDELINES:
|
|
@@ -946,7 +946,7 @@ Example loop over top products:
|
|
|
946
946
|
|
|
947
947
|
When the user asks for a "summary card", "KPI tile", "headline metric", or any narrative text that should reflect live data, prefer a markdown item with a query over hardcoded numbers.
|
|
948
948
|
|
|
949
|
-
${
|
|
949
|
+
${Ze()}
|
|
950
950
|
|
|
951
951
|
VALIDATION BEFORE HANDOFF:
|
|
952
952
|
- Once you believe the dashboard is complete, call capture_dashboard_screenshot to render it as a PNG and review the actual visual layout.
|
|
@@ -958,25 +958,25 @@ VALIDATION BEFORE HANDOFF:
|
|
|
958
958
|
COMPLETING YOUR RESPONSE:
|
|
959
959
|
- When you have finished addressing the user's request — and have validated the result via capture_dashboard_screenshot — call return_to_user with a brief summary.
|
|
960
960
|
- Never end a turn with plain text only — you must always call a tool. return_to_user is always your final tool call.
|
|
961
|
-
${s ?
|
|
961
|
+
${s ? $e() : ""}`;
|
|
962
962
|
}
|
|
963
|
-
function
|
|
963
|
+
function Ze() {
|
|
964
964
|
return "FREEFORM WIDGETS (custom HTML):\nWhen the built-in chart types genuinely cannot express what the user asked for — a bespoke layout, a custom interaction, a visual metaphor specific to their domain — add an item of type \"freeform\". Prefer a chart or table when one fits: they are themed, accessible, and maintained. Reach for a widget when they don't.\n\nAuthoring a widget is TWO separate artifacts, passed as two fields:\n- `content`: the Trilogy query. Written exactly like a chart query, and subject to the same rules (full field paths, LIMIT, no GROUP BY). Global filters and cross-filters apply to it automatically.\n- `html`: the markup that renders the query's rows. You never write queries inside the widget — the widget cannot issue them.\n\nThe widget runs inside a sandboxed iframe. Available to it:\n- `trilogy.subscribe(fn)` — fn is called immediately and on every change with `{ status, columns, rows, rowCount, truncated, filters, error }`. `status` is 'loading' | 'ready' | 'error'; `columns` is `[{ name, type, address }]`; `rows` is an array of plain objects keyed by column name.\n- `trilogy.ready()` — REQUIRED. Call it once the first render completes. A widget that never calls ready() is reported as broken after a few seconds.\n- `trilogy.filters.eq(field, value)` — cross-filter the rest of the dashboard, exactly like clicking a chart mark. Pass the column name as it appears in `rows` (e.g. `'product.name'`); the host resolves it to the underlying concept. A field the dashboard doesn't expose is ignored.\n- `trilogy.filters.set(map)` / `.append(map)` / `.clear()` — map is `{ field: { op: 'eq' | 'in' | 'range' | 'is_null', value } }`.\n- `trilogy.refresh()` — re-run the backing query.\n- `trilogy.resize(px)` — request a taller frame.\n- `trilogy.theme` — `{ mode, vars }` where mode is 'light' or 'dark'. See the theme contract below.\n\nTHEME CONTRACT (read this before writing any CSS):\nNEVER hardcode a color, background, or font in a widget. The dashboard runs in light OR dark mode, the user can switch at any time, and a widget with baked-in colors is illegible in the other mode. Every color you need is already injected as a CSS variable on `:root` inside the frame, and they are re-pushed when the user switches mode:\n- `--widget-bg` — page background. The frame is transparent over the dashboard card by default; only set a background if you need a distinct surface.\n- `--widget-surface` — a raised/inset surface (cards, table headers, wells).\n- `--widget-text` — primary text. `--widget-text-muted` — secondary/labels/axis text.\n- `--widget-border` — borders and dividers. `--widget-border-light` — subtler dividers.\n- `--widget-accent` — accent/primary. `--widget-accent-rgb` — the same color as \"r, g, b\" for `rgba(var(--widget-accent-rgb), 0.12)` tints.\n- `--widget-positive` / `--widget-negative` — good/bad, up/down, profit/loss.\n- `--widget-series-1` … `--widget-series-6` — categorical palette for multi-series charts. Use these in order; do not invent your own.\n- `--widget-font`, `--widget-font-size` — already applied to `body`; inherit rather than restating.\n\nRules:\n1. Use `var(--widget-*)` for EVERY color. No hex literals, no `rgb()` literals, no named colors, no `background: white`, no `color: #333`.\n2. For translucent fills use `rgba(var(--widget-accent-rgb), 0.12)` — never a hardcoded rgba.\n3. Do not set `background` on `body` or `html`. The frame is transparent so the widget sits on the dashboard card; painting your own background is what makes a widget look like a pasted-in rectangle.\n4. If you truly need a mode-specific value, branch on `trilogy.theme.mode` inside `subscribe` — do not assume dark.\n5. Test your reasoning against both modes before finishing: would every piece of text still be readable if the background flipped?\n\nSandbox limits — code that violates these silently fails:\n- No network. `fetch`, XHR, WebSockets, and remote images are blocked. All data comes from the query.\n- No storage. `localStorage`, `sessionStorage`, and cookies are unavailable.\n- No access to the host page, no popups, no forms, no top-level navigation.\n- Libraries may be loaded from cdn.jsdelivr.net, unpkg.com, esm.sh, or cdnjs.cloudflare.com. Pin exact versions. Prefer plain DOM — it always works and loads instantly.\n\nWidget authoring rules:\n1. Handle every status. Render something sensible while `status === 'loading'` and when `rows` is empty; never assume data has arrived on first call.\n2. Read column names from `columns`, or use the names your own query selected — do not hardcode names you did not select.\n3. Build DOM with `textContent`/`createElement` rather than string concatenation into `innerHTML`. Row values are data you do not control, and a value containing markup will otherwise break the layout.\n4. Keep it self-contained: one `<style>`, one `<script>`, no build step.\n5. Call `trilogy.ready()` at the end of the initial render, not inside the subscribe callback's data branch.\n6. Every color comes from the theme contract above. A widget that hardcodes colors is a defect, not a style preference.\n\nMinimal shape:\n content:\n SELECT product.name, product.revenue ORDER BY product.revenue DESC LIMIT 10;\n html:\n <style>\n .row { padding: 6px 8px; border-bottom: 1px solid var(--widget-border-light); cursor: pointer; }\n .row:hover { background: rgba(var(--widget-accent-rgb), 0.08); }\n .name { color: var(--widget-text); }\n .value { color: var(--widget-text-muted); font-variant-numeric: tabular-nums; }\n .empty { color: var(--widget-text-muted); padding: 12px; }\n </style>\n <div id=\"root\"></div>\n <script>\n var root = document.getElementById('root');\n trilogy.subscribe(function (state) {\n if (!state.rows.length) {\n root.innerHTML = '';\n var empty = document.createElement('div');\n empty.className = 'empty';\n empty.textContent = state.status === 'loading' ? 'Loading…' : 'No data';\n root.appendChild(empty);\n return;\n }\n root.innerHTML = '';\n state.rows.forEach(function (row) {\n var el = document.createElement('div');\n el.className = 'row';\n var name = document.createElement('span');\n name.className = 'name';\n name.textContent = row['product.name'];\n var value = document.createElement('span');\n value.className = 'value';\n value.textContent = ' — ' + row['product.revenue'];\n el.appendChild(name);\n el.appendChild(value);\n el.onclick = function () { trilogy.filters.eq('product.name', row['product.name']); };\n root.appendChild(el);\n });\n });\n trilogy.ready();\n <\/script>";
|
|
965
965
|
}
|
|
966
|
-
function
|
|
966
|
+
function Qe() {
|
|
967
967
|
return "# REPORT AUTHORING MODE\n\nYou are authoring a source-backed analytical memo, not an interactive dashboard. The reader is a busy decision-maker who wants the answer, the evidence, and the honest caveats — in that order.\n\nPRINCIPLES (non-negotiable):\n1. Make the claim. A great agent makes a claim, shows the evidence, exposes uncertainty, and lets the human interrogate it. Do NOT defer to the reader to make the call themselves by laying out raw charts.\n2. Executive memo first. One memo at the top: headline / verdict / magnitude / cause / action / confidence. Use set_executive_memo. Aim for one screen — the reader should not need to scroll to get the answer.\n3. Narrative sections, not dashboard tiles. Each claim is its own section. Do NOT title sections \"Revenue Chart\" or \"Conversion Analysis\" — those are table-of-contents entries, not insights. Title them with the claim itself.\n4. Charts serve the argument. Each claim has exactly the visual evidence needed to support it — no more. Add a chart RIGHT AFTER its claim section so the report renderer pairs them visually.\n5. Caveats are mandatory on every claim. State what the data does NOT prove. If you cannot articulate a caveat, the claim is probably overconfident.\n6. Drilldown comes after the answer, not before. Interactivity belongs below the verdict — never use it as the primary communication layer.\n7. Tables go in the appendix. Use add_appendix_header before adding detail tables; never put a giant table above a claim.\n8. Provenance is the price of authorship. Every claim must have a query, filters, and a confidence level. Use get_provenance to audit your own evidence chain before handing off. If you can't justify a number, do not state it.\n\n";
|
|
968
968
|
}
|
|
969
|
-
function
|
|
969
|
+
function $e() {
|
|
970
970
|
return "\n\nREPORT AUTHORING WORKFLOW:\n1. Start with set_executive_memo. All six fields are required. Pick a verdict (\"good\" / \"bad\" / \"mixed\" / \"inconclusive\") — pick \"inconclusive\" only when the data genuinely cannot decide. Pick a confidence (\"high\" / \"medium\" / \"low\") and explain the rationale. Bind a query if your magnitude/cause/action want live numbers.\n2. For each point you want to make, call add_claim_section with the claim sentence (and caveat, drilldown). Then immediately call add_dashboard_item with type='chart' (or 'table') for the evidence — the renderer pairs adjacent blocks. Keep one claim per section.\n3. When you have detail tables or methodology, call add_appendix_header once, then add_dashboard_item for each appendix block. Anything added after the divider renders as appendix content.\n4. Audit before handing off: call get_provenance on at least your memo and each claim section. Confirm queries, filters, and confidence are sane.\n5. Capture a screenshot to review visual rendering (capture_dashboard_screenshot).\n6. Call return_to_user with a one-paragraph summary highlighting the verdict and your confidence.\n\nANTI-PATTERNS (don't):\n- A wall of charts with no claims. That is the defensive design we are explicitly avoiding.\n- A claim without a chart, OR a chart without a claim. They come in pairs.\n- \"Tabs\" or interactivity-driven analysis. Reports are read top-to-bottom. Use drilldown text on claims when the reader needs to investigate further.\n- Memos with vague verdicts (\"worth investigating\", \"needs more analysis\"). Pick a side; lower the confidence if you are unsure.\n";
|
|
971
971
|
}
|
|
972
972
|
//#endregion
|
|
973
973
|
//#region dashboards/connectionResolution.ts
|
|
974
|
-
function
|
|
974
|
+
function M(e, t) {
|
|
975
975
|
if (!e) return "";
|
|
976
976
|
let { connection: n = "", connectionId: r = "" } = e;
|
|
977
977
|
return r && t.connections[r] ? r : (n ? t.connectionByName(n)?.id : void 0) || r || n;
|
|
978
978
|
}
|
|
979
|
-
function
|
|
979
|
+
function et(e, t) {
|
|
980
980
|
let n = 0;
|
|
981
981
|
for (let r of e) {
|
|
982
982
|
if (r.connectionId && t.connections[r.connectionId] || !r.connection) continue;
|
|
@@ -987,47 +987,47 @@ function nt(e, t) {
|
|
|
987
987
|
}
|
|
988
988
|
//#endregion
|
|
989
989
|
//#region dashboards/freeform/types.ts
|
|
990
|
-
var
|
|
990
|
+
var tt = 262144, nt = 2e4, rt = 8e3, it = 4e3, at = [
|
|
991
991
|
"https://cdn.jsdelivr.net",
|
|
992
992
|
"https://unpkg.com",
|
|
993
993
|
"https://esm.sh",
|
|
994
994
|
"https://cdnjs.cloudflare.com"
|
|
995
|
-
],
|
|
995
|
+
], ot = "trilogy-freeform-hello";
|
|
996
996
|
//#endregion
|
|
997
997
|
//#region dashboards/provenance.ts
|
|
998
|
-
function
|
|
999
|
-
let i =
|
|
1000
|
-
|
|
998
|
+
function st(e, t, r) {
|
|
999
|
+
let i = n(t), a = Array.isArray(r.imports) && r.imports.length > 0 ? r.imports[0] : void 0, o = [];
|
|
1000
|
+
r.filter && o.push({
|
|
1001
1001
|
source: "global",
|
|
1002
|
-
expression:
|
|
1002
|
+
expression: r.filter
|
|
1003
1003
|
});
|
|
1004
|
-
for (let e of t.filters || [])
|
|
1004
|
+
for (let e of t.filters || []) o.push({
|
|
1005
1005
|
source: e.source,
|
|
1006
1006
|
expression: e.value,
|
|
1007
1007
|
parameters: e.parameters
|
|
1008
1008
|
});
|
|
1009
|
-
let
|
|
1010
|
-
|
|
1011
|
-
let
|
|
1009
|
+
let u, d, f, p;
|
|
1010
|
+
c(t.content) ? (u = t.content.confidence, d = t.content.confidenceRationale) : ue(t.content) && (f = t.content.caveat, p = t.content.drilldown);
|
|
1011
|
+
let m = !i && !(t.type === l.CHART || t.type === l.TABLE) && !(s(t.content) && t.content.query);
|
|
1012
1012
|
return {
|
|
1013
1013
|
itemId: e,
|
|
1014
1014
|
type: t.type,
|
|
1015
1015
|
name: t.name,
|
|
1016
1016
|
query: i,
|
|
1017
|
-
dataSource:
|
|
1018
|
-
freshness:
|
|
1019
|
-
filters:
|
|
1017
|
+
dataSource: a?.name,
|
|
1018
|
+
freshness: r.updatedAt,
|
|
1019
|
+
filters: o,
|
|
1020
1020
|
parameters: t.parameters,
|
|
1021
|
-
confidence:
|
|
1022
|
-
confidenceRationale:
|
|
1023
|
-
caveat:
|
|
1024
|
-
drilldown:
|
|
1021
|
+
confidence: u,
|
|
1022
|
+
confidenceRationale: d,
|
|
1023
|
+
caveat: f,
|
|
1024
|
+
drilldown: p,
|
|
1025
1025
|
rowCount: t.results?.data?.length,
|
|
1026
1026
|
error: t.error ?? null,
|
|
1027
|
-
noQuery:
|
|
1027
|
+
noQuery: m
|
|
1028
1028
|
};
|
|
1029
1029
|
}
|
|
1030
|
-
function
|
|
1030
|
+
function ct(e) {
|
|
1031
1031
|
let t = [];
|
|
1032
1032
|
if (t.push(`# Provenance for "${e.name}" [${e.itemId}] (${e.type})`), t.push(""), e.dataSource && t.push(`- Data source: ${e.dataSource}`), t.push(`- Last updated: ${e.freshness.toISOString()}`), e.confidence && t.push(`- Confidence: ${e.confidence}${e.confidenceRationale ? ` — ${e.confidenceRationale}` : ""}`), e.rowCount !== void 0 && t.push(`- Rows returned: ${e.rowCount}`), e.error && t.push(`- Last error: ${e.error}`), e.caveat && t.push(`- Caveat: ${e.caveat}`), e.drilldown && t.push(`- Next drilldown: ${e.drilldown}`), e.filters.length > 0) {
|
|
1033
1033
|
t.push("- Filters:");
|
|
@@ -1042,14 +1042,14 @@ function ut(e) {
|
|
|
1042
1042
|
}
|
|
1043
1043
|
//#endregion
|
|
1044
1044
|
//#region llm/dashboardToolExecutor.ts
|
|
1045
|
-
function
|
|
1046
|
-
return !e || !e.trim() ? "Freeform items require `html` — the widget markup. Author the query in `content` and the markup in `html`." : e.length > 262144 ? `Widget html is ${e.length} characters; the limit is ${
|
|
1045
|
+
function lt(e) {
|
|
1046
|
+
return !e || !e.trim() ? "Freeform items require `html` — the widget markup. Author the query in `content` and the markup in `html`." : e.length > 262144 ? `Widget html is ${e.length} characters; the limit is ${tt}. Simplify the widget or move logic into fewer, denser helpers.` : null;
|
|
1047
1047
|
}
|
|
1048
|
-
var
|
|
1048
|
+
var ut = { heatmap: {
|
|
1049
1049
|
xField: 25,
|
|
1050
1050
|
yField: 25
|
|
1051
1051
|
} };
|
|
1052
|
-
function
|
|
1052
|
+
function dt(e) {
|
|
1053
1053
|
if (e == null) return null;
|
|
1054
1054
|
if (typeof e == "object") {
|
|
1055
1055
|
let t = e;
|
|
@@ -1057,15 +1057,15 @@ function pt(e) {
|
|
|
1057
1057
|
}
|
|
1058
1058
|
return String(e);
|
|
1059
1059
|
}
|
|
1060
|
-
function
|
|
1060
|
+
function ft(e, t) {
|
|
1061
1061
|
let n = /* @__PURE__ */ new Set();
|
|
1062
1062
|
for (let r of e) {
|
|
1063
|
-
let e =
|
|
1063
|
+
let e = dt(r[t]);
|
|
1064
1064
|
e !== null && n.add(e);
|
|
1065
1065
|
}
|
|
1066
1066
|
return n.size;
|
|
1067
1067
|
}
|
|
1068
|
-
var
|
|
1068
|
+
var pt = class {
|
|
1069
1069
|
deps;
|
|
1070
1070
|
constructor(e) {
|
|
1071
1071
|
this.deps = e;
|
|
@@ -1074,7 +1074,7 @@ var ht = class {
|
|
|
1074
1074
|
return this.deps.dashboardStore.dashboards[this.deps.dashboardId] || null;
|
|
1075
1075
|
}
|
|
1076
1076
|
get connectionName() {
|
|
1077
|
-
return
|
|
1077
|
+
return M(this.dashboard, this.deps.connectionStore);
|
|
1078
1078
|
}
|
|
1079
1079
|
async executeToolCall(e, t) {
|
|
1080
1080
|
switch (e) {
|
|
@@ -1119,7 +1119,7 @@ var ht = class {
|
|
|
1119
1119
|
let n = this.deps.connectionStore?.connections[this.connectionName];
|
|
1120
1120
|
return {
|
|
1121
1121
|
success: !0,
|
|
1122
|
-
message:
|
|
1122
|
+
message: qe({
|
|
1123
1123
|
dashboard: t,
|
|
1124
1124
|
dataConnectionName: t.connection,
|
|
1125
1125
|
activeImports: this.deps.getActiveImports(),
|
|
@@ -1140,8 +1140,8 @@ var ht = class {
|
|
|
1140
1140
|
message: "The dashboard is empty. No items to list."
|
|
1141
1141
|
};
|
|
1142
1142
|
let n = new Map(e.layout.map((e) => [e.i, e])), r = t.map(([e, t]) => {
|
|
1143
|
-
let r = n.get(e), i = typeof t.content == "object" && t.content !== null,
|
|
1144
|
-
return `- [${e}] ${t.type}: "${t.name}" ${
|
|
1143
|
+
let r = n.get(e), i = typeof t.content == "object" && t.content !== null, a = i ? t.content : null, o = t.type === l.FREEFORM && i ? t.content : null, s = o ? `${o.html.length} chars of widget html` : i ? a?.markdown?.substring(0, 120) || "" : t.content.substring(0, 120), c = r ? `(x=${r.x}, y=${r.y}, w=${r.w}, h=${r.h})` : "", u = o?.query || a?.query, d = u ? `\n Query: ${u.substring(0, 120)}${u.length >= 120 ? "..." : ""}` : "";
|
|
1144
|
+
return `- [${e}] ${t.type}: "${t.name}" ${c}\n Content: ${s}${s.length >= 120 ? "..." : ""}${d}`;
|
|
1145
1145
|
});
|
|
1146
1146
|
return {
|
|
1147
1147
|
success: !0,
|
|
@@ -1149,42 +1149,42 @@ var ht = class {
|
|
|
1149
1149
|
};
|
|
1150
1150
|
}
|
|
1151
1151
|
getDashboardItem(e) {
|
|
1152
|
-
let
|
|
1153
|
-
if (!
|
|
1152
|
+
let t = this.dashboard;
|
|
1153
|
+
if (!t) return {
|
|
1154
1154
|
success: !1,
|
|
1155
1155
|
error: "Dashboard not found."
|
|
1156
1156
|
};
|
|
1157
|
-
let
|
|
1158
|
-
if (!
|
|
1157
|
+
let n = t.gridItems[e];
|
|
1158
|
+
if (!n) return {
|
|
1159
1159
|
success: !1,
|
|
1160
1160
|
error: `Item "${e}" not found. Use list_dashboard_items to see available items.`
|
|
1161
1161
|
};
|
|
1162
|
-
let
|
|
1162
|
+
let r = t.layout.find((t) => t.i === e), i = typeof n.content == "object" && n.content !== null, a = i ? n.content : null, o = n.type === l.FREEFORM && i ? n.content : null, s = {
|
|
1163
1163
|
id: e,
|
|
1164
|
-
type:
|
|
1165
|
-
name:
|
|
1166
|
-
content:
|
|
1167
|
-
query:
|
|
1168
|
-
chartConfig:
|
|
1169
|
-
position:
|
|
1170
|
-
x:
|
|
1171
|
-
y:
|
|
1172
|
-
w:
|
|
1173
|
-
h:
|
|
1164
|
+
type: n.type,
|
|
1165
|
+
name: n.name,
|
|
1166
|
+
content: o ? o.query : i ? a?.markdown || "" : n.content,
|
|
1167
|
+
query: o?.query || a?.query || null,
|
|
1168
|
+
chartConfig: n.chartConfig || null,
|
|
1169
|
+
position: r ? {
|
|
1170
|
+
x: r.x,
|
|
1171
|
+
y: r.y,
|
|
1172
|
+
w: r.w,
|
|
1173
|
+
h: r.h
|
|
1174
1174
|
} : null,
|
|
1175
|
-
loading:
|
|
1176
|
-
error:
|
|
1175
|
+
loading: n.loading || !1,
|
|
1176
|
+
error: n.error || null
|
|
1177
1177
|
};
|
|
1178
|
-
if (
|
|
1179
|
-
let e = typeof
|
|
1180
|
-
|
|
1178
|
+
if (o && (s.html = o.html), n.results) {
|
|
1179
|
+
let e = typeof n.results.toJSON == "function" ? n.results.toJSON() : n.results, { head: t, totalRows: r, cutCount: i } = oe(e);
|
|
1180
|
+
i > 0 ? (s.resultPreview = {
|
|
1181
1181
|
...e,
|
|
1182
|
-
data:
|
|
1183
|
-
},
|
|
1182
|
+
data: t
|
|
1183
|
+
}, s.resultTruncated = `Showing first ${t.length} of ${r} rows`) : s.results = e;
|
|
1184
1184
|
}
|
|
1185
1185
|
return {
|
|
1186
1186
|
success: !0,
|
|
1187
|
-
message: JSON.stringify(
|
|
1187
|
+
message: JSON.stringify(s, null, 2)
|
|
1188
1188
|
};
|
|
1189
1189
|
}
|
|
1190
1190
|
async validateChartConfigAfterQuery(e, t, n) {
|
|
@@ -1193,10 +1193,10 @@ var ht = class {
|
|
|
1193
1193
|
try {
|
|
1194
1194
|
let i = await r.waitForQuery(t), a = i?.results ?? i ?? null;
|
|
1195
1195
|
if (!a || !a.headers) return null;
|
|
1196
|
-
let o =
|
|
1196
|
+
let o = v(a.data, a.headers, n);
|
|
1197
1197
|
if (o.valid) return null;
|
|
1198
1198
|
let s = o.suggestedConfig;
|
|
1199
|
-
return s && s.chartType && this.deps.dashboardStore.updateItemChartConfig(this.deps.dashboardId, e, s), `Auto-corrected to default configuration.\n${
|
|
1199
|
+
return s && s.chartType && this.deps.dashboardStore.updateItemChartConfig(this.deps.dashboardId, e, s), `Auto-corrected to default configuration.\n${C(o)}`;
|
|
1200
1200
|
} catch {
|
|
1201
1201
|
return null;
|
|
1202
1202
|
}
|
|
@@ -1223,34 +1223,34 @@ var ht = class {
|
|
|
1223
1223
|
success: !1,
|
|
1224
1224
|
error: "Content is required."
|
|
1225
1225
|
};
|
|
1226
|
-
let i = n,
|
|
1227
|
-
if (
|
|
1228
|
-
let e =
|
|
1226
|
+
let i = n, a = typeof e.query == "string" ? e.query : "", o = i === l.MARKDOWN && a.length > 0, s = i === l.FREEFORM, c = typeof e.html == "string" ? e.html : "";
|
|
1227
|
+
if (s) {
|
|
1228
|
+
let e = lt(c);
|
|
1229
1229
|
if (e) return {
|
|
1230
1230
|
success: !1,
|
|
1231
1231
|
error: e
|
|
1232
1232
|
};
|
|
1233
1233
|
}
|
|
1234
|
-
let u = e.width || (i ===
|
|
1235
|
-
if (
|
|
1234
|
+
let u = e.width || (i === l.MARKDOWN ? 20 : 10), d = e.height || (i === l.MARKDOWN ? o ? 6 : 4 : 8), f = e.name || n.charAt(0).toUpperCase() + n.slice(1), p = t.layout.reduce((e, t) => Math.max(e, t.y + t.h), 0), m = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, i, 0, p, u, d, r, f);
|
|
1235
|
+
if (o) {
|
|
1236
1236
|
let e = {
|
|
1237
1237
|
markdown: r,
|
|
1238
|
-
query:
|
|
1238
|
+
query: a
|
|
1239
1239
|
};
|
|
1240
1240
|
this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, m, { content: e });
|
|
1241
1241
|
}
|
|
1242
|
-
if (
|
|
1242
|
+
if (s) {
|
|
1243
1243
|
let e = {
|
|
1244
|
-
html:
|
|
1244
|
+
html: c,
|
|
1245
1245
|
query: r
|
|
1246
1246
|
};
|
|
1247
1247
|
this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, m, { content: e });
|
|
1248
1248
|
}
|
|
1249
|
-
e.chartConfig && i ===
|
|
1249
|
+
e.chartConfig && i === l.CHART && this.deps.dashboardStore.updateItemChartConfig(this.deps.dashboardId, m, e.chartConfig);
|
|
1250
1250
|
let h;
|
|
1251
|
-
(i ===
|
|
1252
|
-
let g = `Added ${n} item "${f}" (ID: ${m}) at position (0, ${p}, ${u}x${d}).${
|
|
1253
|
-
if (e.chartConfig && i ===
|
|
1251
|
+
(i === l.CHART || i === l.TABLE || s || o) && (h = this.deps.refreshItem(m));
|
|
1252
|
+
let g = `Added ${n} item "${f}" (ID: ${m}) at position (0, ${p}, ${u}x${d}).${o ? " Markdown is bound to query results for dynamic data." : ""}${s ? " The widget receives this query’s rows via window.trilogy." : ""}`;
|
|
1253
|
+
if (e.chartConfig && i === l.CHART && h) {
|
|
1254
1254
|
let t = await this.validateChartConfigAfterQuery(m, h, e.chartConfig);
|
|
1255
1255
|
if (t) return {
|
|
1256
1256
|
success: !1,
|
|
@@ -1273,9 +1273,9 @@ var ht = class {
|
|
|
1273
1273
|
success: !1,
|
|
1274
1274
|
error: `Item "${n}" not found. Use list_dashboard_items to see available items.`
|
|
1275
1275
|
};
|
|
1276
|
-
let r = [], i = t.gridItems[n],
|
|
1277
|
-
if (
|
|
1278
|
-
let t = i.content, a = typeof t == "string" ? "" : t?.html || "", o = typeof t == "string" ? t : t?.query || "", s = e.html === void 0 ? a : e.html, c = e.content === void 0 ? o : e.content, l =
|
|
1276
|
+
let r = [], i = t.gridItems[n], a = e.type || i.type, o = a === l.MARKDOWN;
|
|
1277
|
+
if (a === l.FREEFORM && (e.content !== void 0 || e.html !== void 0)) {
|
|
1278
|
+
let t = i.content, a = typeof t == "string" ? "" : t?.html || "", o = typeof t == "string" ? t : t?.query || "", s = e.html === void 0 ? a : e.html, c = e.content === void 0 ? o : e.content, l = lt(s);
|
|
1279
1279
|
if (l) return {
|
|
1280
1280
|
success: !1,
|
|
1281
1281
|
error: l
|
|
@@ -1284,7 +1284,7 @@ var ht = class {
|
|
|
1284
1284
|
html: s,
|
|
1285
1285
|
query: c
|
|
1286
1286
|
} }), e.content !== void 0 && r.push("content"), e.html !== void 0 && r.push("html");
|
|
1287
|
-
} else if (
|
|
1287
|
+
} else if (o && (e.content !== void 0 || e.query !== void 0)) {
|
|
1288
1288
|
let t = i.content, a = typeof t == "string" ? t : t?.markdown || "", o = typeof t == "string" ? "" : t?.query || "", s = e.content === void 0 ? a : e.content, c = e.query === void 0 ? o : e.query, l = c ? {
|
|
1289
1289
|
markdown: s,
|
|
1290
1290
|
query: c
|
|
@@ -1295,25 +1295,25 @@ var ht = class {
|
|
|
1295
1295
|
success: !0,
|
|
1296
1296
|
message: `No updates provided for item "${n}".`
|
|
1297
1297
|
};
|
|
1298
|
-
let
|
|
1299
|
-
(r.includes("content") || r.includes("type") || r.includes("query")) && u && (
|
|
1298
|
+
let s = t.gridItems[n], c, u = s.type === l.CHART || s.type === l.TABLE || s.type === l.FREEFORM || s.type === l.MARKDOWN && typeof s.content == "object" && !!s.content.query;
|
|
1299
|
+
(r.includes("content") || r.includes("type") || r.includes("query")) && u && (c = this.deps.refreshItem(n));
|
|
1300
1300
|
let d = `Updated item "${n}": ${r.join(", ")}.`;
|
|
1301
|
-
if (r.includes("chartConfig") &&
|
|
1302
|
-
if (
|
|
1303
|
-
let t = await this.validateChartConfigAfterQuery(n,
|
|
1301
|
+
if (r.includes("chartConfig") && s.type === l.CHART) {
|
|
1302
|
+
if (c) {
|
|
1303
|
+
let t = await this.validateChartConfigAfterQuery(n, c, e.chartConfig);
|
|
1304
1304
|
if (t) return {
|
|
1305
1305
|
success: !1,
|
|
1306
1306
|
error: `${d}\n\nHowever, the chart configuration is invalid:\n${t}`
|
|
1307
1307
|
};
|
|
1308
|
-
} else if (
|
|
1309
|
-
let t =
|
|
1308
|
+
} else if (s.results) {
|
|
1309
|
+
let t = s.results;
|
|
1310
1310
|
if (t.headers) {
|
|
1311
|
-
let r =
|
|
1311
|
+
let r = v(t.data, t.headers, e.chartConfig);
|
|
1312
1312
|
if (!r.valid) {
|
|
1313
1313
|
let e = r.suggestedConfig;
|
|
1314
1314
|
return e && e.chartType && this.deps.dashboardStore.updateItemChartConfig(this.deps.dashboardId, n, e), {
|
|
1315
1315
|
success: !1,
|
|
1316
|
-
error: `${d}\n\nAuto-corrected to default configuration.\n${
|
|
1316
|
+
error: `${d}\n\nAuto-corrected to default configuration.\n${C(r)}`
|
|
1317
1317
|
};
|
|
1318
1318
|
}
|
|
1319
1319
|
}
|
|
@@ -1373,7 +1373,7 @@ var ht = class {
|
|
|
1373
1373
|
itemCount: Object.keys(e.gridItems).length,
|
|
1374
1374
|
imports: e.imports,
|
|
1375
1375
|
filter: e.filter,
|
|
1376
|
-
theme:
|
|
1376
|
+
theme: x(e.theme),
|
|
1377
1377
|
createdAt: e.createdAt,
|
|
1378
1378
|
updatedAt: e.updatedAt
|
|
1379
1379
|
};
|
|
@@ -1397,8 +1397,8 @@ var ht = class {
|
|
|
1397
1397
|
};
|
|
1398
1398
|
}
|
|
1399
1399
|
setDashboardTheme(e) {
|
|
1400
|
-
let
|
|
1401
|
-
if (!
|
|
1400
|
+
let n = this.dashboard;
|
|
1401
|
+
if (!n) return {
|
|
1402
1402
|
success: !1,
|
|
1403
1403
|
error: "Dashboard not found."
|
|
1404
1404
|
};
|
|
@@ -1406,20 +1406,20 @@ var ht = class {
|
|
|
1406
1406
|
success: !0,
|
|
1407
1407
|
message: "Dashboard theme cleared. It now inherits the app light/dark styling."
|
|
1408
1408
|
};
|
|
1409
|
-
let
|
|
1410
|
-
["preset",
|
|
1411
|
-
["corners",
|
|
1412
|
-
["density",
|
|
1413
|
-
["elevation",
|
|
1409
|
+
let i = {}, a = [], o = [
|
|
1410
|
+
["preset", t],
|
|
1411
|
+
["corners", r],
|
|
1412
|
+
["density", ye],
|
|
1413
|
+
["elevation", g]
|
|
1414
1414
|
];
|
|
1415
|
-
for (let [t, n] of
|
|
1416
|
-
let
|
|
1417
|
-
if (
|
|
1418
|
-
if (!n.includes(
|
|
1415
|
+
for (let [t, n] of o) {
|
|
1416
|
+
let r = e[t];
|
|
1417
|
+
if (r != null) {
|
|
1418
|
+
if (!n.includes(r)) return {
|
|
1419
1419
|
success: !1,
|
|
1420
|
-
error: `Invalid ${t} "${
|
|
1420
|
+
error: `Invalid ${t} "${r}". Must be one of: ${n.join(", ")}.`
|
|
1421
1421
|
};
|
|
1422
|
-
|
|
1422
|
+
i[t] = r, a.push(`${t}=${r}`);
|
|
1423
1423
|
}
|
|
1424
1424
|
}
|
|
1425
1425
|
if (e.mobileCards !== void 0 && e.mobileCards !== null) {
|
|
@@ -1427,9 +1427,9 @@ var ht = class {
|
|
|
1427
1427
|
success: !1,
|
|
1428
1428
|
error: "mobileCards must be true or false."
|
|
1429
1429
|
};
|
|
1430
|
-
|
|
1430
|
+
i.mobileCards = e.mobileCards, a.push(`mobileCards=${e.mobileCards}`);
|
|
1431
1431
|
}
|
|
1432
|
-
for (let t of
|
|
1432
|
+
for (let t of d) {
|
|
1433
1433
|
let n = e[t.key];
|
|
1434
1434
|
if (n != null) {
|
|
1435
1435
|
if (typeof n != "string") return {
|
|
@@ -1437,22 +1437,22 @@ var ht = class {
|
|
|
1437
1437
|
error: `${t.key} must be a color string.`
|
|
1438
1438
|
};
|
|
1439
1439
|
if (n.trim() === "") {
|
|
1440
|
-
|
|
1440
|
+
i[t.key] = void 0, a.push(`${t.key} cleared`);
|
|
1441
1441
|
continue;
|
|
1442
1442
|
}
|
|
1443
|
-
if (!
|
|
1443
|
+
if (!he(n)) return {
|
|
1444
1444
|
success: !1,
|
|
1445
1445
|
error: `Invalid ${t.key} "${n}". Use a hex string ("#101820"), rgb()/rgba()/hsl(), or a CSS color name.`
|
|
1446
1446
|
};
|
|
1447
|
-
|
|
1447
|
+
i[t.key] = n.trim(), a.push(`${t.key}=${n.trim()}`);
|
|
1448
1448
|
}
|
|
1449
1449
|
}
|
|
1450
|
-
return
|
|
1450
|
+
return a.length === 0 ? {
|
|
1451
1451
|
success: !0,
|
|
1452
|
-
message: `No theme changes provided. Current theme: ${n
|
|
1453
|
-
} : (this.deps.dashboardStore.setDashboardTheme(this.deps.dashboardId,
|
|
1452
|
+
message: `No theme changes provided. Current theme: ${x(n.theme)}. Pass reset=true to clear it.`
|
|
1453
|
+
} : (this.deps.dashboardStore.setDashboardTheme(this.deps.dashboardId, i), {
|
|
1454
1454
|
success: !0,
|
|
1455
|
-
message: `Theme updated (${
|
|
1455
|
+
message: `Theme updated (${a.join(", ")}). Full theme is now: ${x(this.dashboard?.theme)}. Call capture_dashboard_screenshot to confirm it reads well — custom colors override the app palette, so check contrast rather than assuming it.`
|
|
1456
1456
|
});
|
|
1457
1457
|
}
|
|
1458
1458
|
getChartAxisThresholdWarnings() {
|
|
@@ -1460,17 +1460,17 @@ var ht = class {
|
|
|
1460
1460
|
if (!e) return [];
|
|
1461
1461
|
let t = [];
|
|
1462
1462
|
for (let [n, r] of Object.entries(e.gridItems)) {
|
|
1463
|
-
if (r.type !==
|
|
1464
|
-
let i =
|
|
1463
|
+
if (r.type !== l.CHART || !r.chartConfig || !r.results) continue;
|
|
1464
|
+
let i = ut[r.chartConfig.chartType];
|
|
1465
1465
|
if (!i) continue;
|
|
1466
|
-
let
|
|
1466
|
+
let a = r.results.data, o = e.layout.find((e) => e.i === n), s = r.name || "(untitled)";
|
|
1467
1467
|
for (let e of ["xField", "yField"]) {
|
|
1468
|
-
let
|
|
1469
|
-
if (!
|
|
1470
|
-
let u =
|
|
1471
|
-
if (u <=
|
|
1472
|
-
let d = e === "xField" ? "x-axis" : "y-axis", f =
|
|
1473
|
-
t.push(`- [${n}] ${r.chartConfig.chartType} "${
|
|
1468
|
+
let c = i[e], l = r.chartConfig[e];
|
|
1469
|
+
if (!c || !l) continue;
|
|
1470
|
+
let u = ft(a, l);
|
|
1471
|
+
if (u <= c) continue;
|
|
1472
|
+
let d = e === "xField" ? "x-axis" : "y-axis", f = o ? ` Current size: w=${o.w}, h=${o.h}.` : "";
|
|
1473
|
+
t.push(`- [${n}] ${r.chartConfig.chartType} "${s}": ${d} field "${l}" has ${u} distinct values, exceeding the recommended limit of ${c}.${f}`);
|
|
1474
1474
|
}
|
|
1475
1475
|
}
|
|
1476
1476
|
return t;
|
|
@@ -1511,23 +1511,23 @@ var ht = class {
|
|
|
1511
1511
|
};
|
|
1512
1512
|
}
|
|
1513
1513
|
}
|
|
1514
|
-
async runTrilogyQuery(e,
|
|
1515
|
-
let
|
|
1516
|
-
if (!
|
|
1514
|
+
async runTrilogyQuery(e, t) {
|
|
1515
|
+
let n = t || this.connectionName;
|
|
1516
|
+
if (!n) return {
|
|
1517
1517
|
success: !1,
|
|
1518
1518
|
error: "No connection name specified and no dashboard connection set."
|
|
1519
1519
|
};
|
|
1520
|
-
let
|
|
1521
|
-
if (!
|
|
1522
|
-
let { queryResult:
|
|
1523
|
-
return
|
|
1524
|
-
...
|
|
1525
|
-
data:
|
|
1526
|
-
}, null, 2),
|
|
1520
|
+
let r = this.deps.getActiveImports(), i = await _(this.deps.queryExecutionService, this.deps.connectionStore, this.deps.editorStore, n, e, r);
|
|
1521
|
+
if (!i.success) return i;
|
|
1522
|
+
let { queryResult: a } = i, o = typeof a.results.toJSON == "function" ? a.results.toJSON() : a.results, { head: s, totalRows: c, cutCount: l } = oe(o), u;
|
|
1523
|
+
return l > 0 ? (u = JSON.stringify({
|
|
1524
|
+
...o,
|
|
1525
|
+
data: s
|
|
1526
|
+
}, null, 2), u += `\n\n(Showing first ${s.length} of ${c} rows)`) : u = JSON.stringify(o, null, 2), {
|
|
1527
1527
|
success: !0,
|
|
1528
|
-
message: `Query executed successfully (${
|
|
1528
|
+
message: `Query executed successfully (${a.executionTime}ms, ${c} rows):\n${u}`,
|
|
1529
1529
|
query: e,
|
|
1530
|
-
generatedSql:
|
|
1530
|
+
generatedSql: a.generatedSql
|
|
1531
1531
|
};
|
|
1532
1532
|
}
|
|
1533
1533
|
get importAccessor() {
|
|
@@ -1538,22 +1538,22 @@ var ht = class {
|
|
|
1538
1538
|
};
|
|
1539
1539
|
}
|
|
1540
1540
|
async selectActiveImport(e) {
|
|
1541
|
-
return
|
|
1541
|
+
return de(e, this.importAccessor, {
|
|
1542
1542
|
connectionStore: this.deps.connectionStore,
|
|
1543
1543
|
editorStore: this.deps.editorStore,
|
|
1544
1544
|
queryExecutionService: this.deps.queryExecutionService
|
|
1545
1545
|
}, this.deps.editorStore, this.deps.connectionStore);
|
|
1546
1546
|
}
|
|
1547
1547
|
listAvailableImports() {
|
|
1548
|
-
return
|
|
1548
|
+
return ie(this.importAccessor, this.deps.editorStore, this.deps.connectionStore);
|
|
1549
1549
|
}
|
|
1550
1550
|
async connectDataConnection(e) {
|
|
1551
|
-
return
|
|
1551
|
+
return ce(this.deps.connectionStore, e);
|
|
1552
1552
|
}
|
|
1553
1553
|
findMemoItemId() {
|
|
1554
1554
|
let e = this.dashboard;
|
|
1555
1555
|
if (!e) return null;
|
|
1556
|
-
for (let [t, n] of Object.entries(e.gridItems)) if (n.type ===
|
|
1556
|
+
for (let [t, n] of Object.entries(e.gridItems)) if (n.type === l.MEMO) return t;
|
|
1557
1557
|
return null;
|
|
1558
1558
|
}
|
|
1559
1559
|
async setExecutiveMemo(e) {
|
|
@@ -1600,13 +1600,13 @@ var ht = class {
|
|
|
1600
1600
|
confidence: n,
|
|
1601
1601
|
confidenceRationale: typeof e.confidence_rationale == "string" ? e.confidence_rationale : void 0,
|
|
1602
1602
|
query: typeof e.query == "string" && e.query.trim() ? e.query : void 0
|
|
1603
|
-
}, i = this.findMemoItemId(),
|
|
1603
|
+
}, i = this.findMemoItemId(), a;
|
|
1604
1604
|
return i ? (this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, i, {
|
|
1605
1605
|
content: r,
|
|
1606
1606
|
name: r.headline.slice(0, 80) || "Executive Memo"
|
|
1607
|
-
}),
|
|
1607
|
+
}), a = i) : (a = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, l.MEMO, 0, 0, 20, 8, "", r.headline.slice(0, 80) || "Executive Memo"), this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, a, { content: r })), r.query && this.deps.refreshItem(a), {
|
|
1608
1608
|
success: !0,
|
|
1609
|
-
message: i ? `Updated executive memo (id ${
|
|
1609
|
+
message: i ? `Updated executive memo (id ${a}). Verdict: ${t} · confidence: ${n}.` : `Added executive memo (id ${a}). Verdict: ${t} · confidence: ${n}.`
|
|
1610
1610
|
};
|
|
1611
1611
|
}
|
|
1612
1612
|
async addClaimSection(e) {
|
|
@@ -1625,7 +1625,7 @@ var ht = class {
|
|
|
1625
1625
|
caveat: typeof e.caveat == "string" ? e.caveat : void 0,
|
|
1626
1626
|
drilldown: typeof e.drilldown == "string" ? e.drilldown : void 0,
|
|
1627
1627
|
query: typeof e.query == "string" && e.query.trim() ? e.query : void 0
|
|
1628
|
-
}, r = n.heading || t.slice(0, 80), i = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId,
|
|
1628
|
+
}, r = n.heading || t.slice(0, 80), i = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, l.CLAIM, 0, 0, 20, 4, "", r);
|
|
1629
1629
|
return this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, i, { content: n }), n.query && this.deps.refreshItem(i), {
|
|
1630
1630
|
success: !0,
|
|
1631
1631
|
message: `Added claim section (id ${i}). Pair this claim with an evidence chart by calling add_dashboard_item next; the report renderer keeps adjacent blocks visually together.`
|
|
@@ -1637,14 +1637,14 @@ var ht = class {
|
|
|
1637
1637
|
success: !1,
|
|
1638
1638
|
error: "Dashboard not found."
|
|
1639
1639
|
};
|
|
1640
|
-
for (let [e, n] of Object.entries(t.gridItems)) if (n.type ===
|
|
1640
|
+
for (let [e, n] of Object.entries(t.gridItems)) if (n.type === l.APPENDIX_HEADER) return {
|
|
1641
1641
|
success: !1,
|
|
1642
1642
|
error: `Appendix header already exists (id ${e}). A report should have at most one.`
|
|
1643
1643
|
};
|
|
1644
1644
|
let n = typeof e.label == "string" && e.label.trim() ? e.label.trim() : "Appendix";
|
|
1645
1645
|
return {
|
|
1646
1646
|
success: !0,
|
|
1647
|
-
message: `Added appendix divider (id ${this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId,
|
|
1647
|
+
message: `Added appendix divider (id ${this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, l.APPENDIX_HEADER, 0, 0, 20, 1, "", n)}, label "${n}"). Subsequent items added to the report are rendered as appendix content.`
|
|
1648
1648
|
};
|
|
1649
1649
|
}
|
|
1650
1650
|
setReportLayout(e) {
|
|
@@ -1668,7 +1668,7 @@ var ht = class {
|
|
|
1668
1668
|
let n = t.gridItems[e];
|
|
1669
1669
|
return n ? {
|
|
1670
1670
|
success: !0,
|
|
1671
|
-
message:
|
|
1671
|
+
message: ct(st(e, n, t))
|
|
1672
1672
|
} : {
|
|
1673
1673
|
success: !1,
|
|
1674
1674
|
error: `Item "${e}" not found. Use list_dashboard_items to see available items.`
|
|
@@ -1677,9 +1677,9 @@ var ht = class {
|
|
|
1677
1677
|
};
|
|
1678
1678
|
//#endregion
|
|
1679
1679
|
//#region dashboards/itemData.ts
|
|
1680
|
-
function
|
|
1680
|
+
function N(e) {
|
|
1681
1681
|
return {
|
|
1682
|
-
type:
|
|
1682
|
+
type: l.CHART,
|
|
1683
1683
|
content: "",
|
|
1684
1684
|
structured_content: {
|
|
1685
1685
|
markdown: "",
|
|
@@ -1695,28 +1695,28 @@ function A(e) {
|
|
|
1695
1695
|
hasDrilldown: !1
|
|
1696
1696
|
};
|
|
1697
1697
|
}
|
|
1698
|
-
function
|
|
1698
|
+
function mt(e, t) {
|
|
1699
1699
|
return e.imports.map((e) => ({
|
|
1700
1700
|
alias: e.name,
|
|
1701
1701
|
contents: t.editors[e.id]?.contents || t.editors[e.name]?.contents || ""
|
|
1702
1702
|
}));
|
|
1703
1703
|
}
|
|
1704
|
-
function
|
|
1704
|
+
function P(e) {
|
|
1705
1705
|
return e && typeof e == "object" && typeof e.markdown == "string" && typeof e.query == "string";
|
|
1706
1706
|
}
|
|
1707
|
-
function
|
|
1707
|
+
function ht(e) {
|
|
1708
1708
|
return e && typeof e == "object" && "headline" in e && "verdict" in e;
|
|
1709
1709
|
}
|
|
1710
|
-
function
|
|
1710
|
+
function gt(e) {
|
|
1711
1711
|
return e && typeof e == "object" && "claim" in e && !("headline" in e);
|
|
1712
1712
|
}
|
|
1713
|
-
function
|
|
1713
|
+
function _t(e) {
|
|
1714
1714
|
return e && typeof e == "object" && typeof e.html == "string";
|
|
1715
1715
|
}
|
|
1716
|
-
function
|
|
1716
|
+
function vt(e, t, n) {
|
|
1717
1717
|
let r = e.gridItems[t];
|
|
1718
1718
|
if (!r) return {
|
|
1719
|
-
...
|
|
1719
|
+
...N(t),
|
|
1720
1720
|
imports: e.imports,
|
|
1721
1721
|
connectionName: e.connection || ""
|
|
1722
1722
|
};
|
|
@@ -1725,27 +1725,27 @@ function bt(e, t, n) {
|
|
|
1725
1725
|
value: e.filter,
|
|
1726
1726
|
source: "global"
|
|
1727
1727
|
}, ...i]));
|
|
1728
|
-
let o = !1, s =
|
|
1728
|
+
let o = !1, s = P(r.content) ? r.content : ht(r.content) || gt(r.content) || _t(r.content) ? {
|
|
1729
1729
|
markdown: "",
|
|
1730
1730
|
query: r.content.query || ""
|
|
1731
1731
|
} : {
|
|
1732
1732
|
markdown: r.type === "markdown" ? r.content : "",
|
|
1733
1733
|
query: r.type === "markdown" ? "" : r.content
|
|
1734
1734
|
};
|
|
1735
|
-
r.drilldown && (o = !0, s =
|
|
1735
|
+
r.drilldown && (o = !0, s = P(r.drilldown) ? r.drilldown : {
|
|
1736
1736
|
markdown: r.type === "markdown" ? r.drilldown : "",
|
|
1737
1737
|
query: r.type === "markdown" ? "" : r.drilldown
|
|
1738
1738
|
});
|
|
1739
1739
|
let c = r.chartConfig;
|
|
1740
1740
|
o && (c = r.drilldownChartConfig || void 0);
|
|
1741
1741
|
let l = "";
|
|
1742
|
-
return typeof r.content == "string" ? l = r.content :
|
|
1742
|
+
return typeof r.content == "string" ? l = r.content : P(r.content) && (l = r.content.markdown), {
|
|
1743
1743
|
type: r.type,
|
|
1744
1744
|
content: l,
|
|
1745
1745
|
structured_content: s,
|
|
1746
|
-
memoData:
|
|
1747
|
-
claimData:
|
|
1748
|
-
freeformData:
|
|
1746
|
+
memoData: ht(r.content) ? r.content : void 0,
|
|
1747
|
+
claimData: gt(r.content) ? r.content : void 0,
|
|
1748
|
+
freeformData: _t(r.content) ? r.content : void 0,
|
|
1749
1749
|
name: r.name,
|
|
1750
1750
|
allowCrossFilter: r.allowCrossFilter !== !1,
|
|
1751
1751
|
width: r.width || 0,
|
|
@@ -1766,7 +1766,7 @@ function bt(e, t, n) {
|
|
|
1766
1766
|
hasDrilldown: o
|
|
1767
1767
|
};
|
|
1768
1768
|
}
|
|
1769
|
-
function
|
|
1769
|
+
function yt(e, t, n, r) {
|
|
1770
1770
|
let i = {};
|
|
1771
1771
|
r.name && (i.name = r.name), r.chartConfig && (i.chartConfig = r.chartConfig), r.content && (i.content = r.content), r.dimensions && (i.layoutDimensions = {
|
|
1772
1772
|
w: r.dimensions.width,
|
|
@@ -1775,13 +1775,13 @@ function xt(e, t, n, r) {
|
|
|
1775
1775
|
}
|
|
1776
1776
|
//#endregion
|
|
1777
1777
|
//#region llm/dashboardAgentRuntime.ts
|
|
1778
|
-
function
|
|
1778
|
+
function bt(e, t, n) {
|
|
1779
1779
|
let r = e.chatId;
|
|
1780
1780
|
if (r) {
|
|
1781
1781
|
let e = t.chats[r];
|
|
1782
1782
|
if (e && !e.deleted) return r;
|
|
1783
1783
|
}
|
|
1784
|
-
let i = new
|
|
1784
|
+
let i = new o({
|
|
1785
1785
|
name: `Dashboard: ${e.name}`,
|
|
1786
1786
|
llmConnectionName: n.activeConnection || "",
|
|
1787
1787
|
dataConnectionName: e.connection || "",
|
|
@@ -1790,49 +1790,49 @@ function St(e, t, n) {
|
|
|
1790
1790
|
});
|
|
1791
1791
|
return t.addChat(i), e.setChatId(i.id), i.id;
|
|
1792
1792
|
}
|
|
1793
|
-
function
|
|
1794
|
-
return e.layoutType === "report" ? [...
|
|
1793
|
+
function xt(e) {
|
|
1794
|
+
return e.layoutType === "report" ? [...j, ...Ke] : j;
|
|
1795
1795
|
}
|
|
1796
|
-
function
|
|
1796
|
+
function F(e) {
|
|
1797
1797
|
return (e.imports || []).map((e) => ({
|
|
1798
1798
|
id: e.id,
|
|
1799
1799
|
name: e.name,
|
|
1800
1800
|
alias: e.alias || ""
|
|
1801
1801
|
}));
|
|
1802
1802
|
}
|
|
1803
|
-
function
|
|
1803
|
+
function St(e, t, n) {
|
|
1804
1804
|
let r = t ? Object.values(t.connections).map((e) => e.name) : [], i = e.connectionId && t?.connections[e.connectionId] || (e.connection ? t?.connectionByName(e.connection) : void 0);
|
|
1805
|
-
return
|
|
1805
|
+
return Xe({
|
|
1806
1806
|
dashboard: e,
|
|
1807
1807
|
availableConnections: r,
|
|
1808
|
-
availableImportsForConnection: n && i ? Object.values(n.editors).filter((e) => !e.deleted &&
|
|
1808
|
+
availableImportsForConnection: n && i ? Object.values(n.editors).filter((e) => !e.deleted && O(e.type) && e.connectionId === i.id).sort((e, t) => e.name.localeCompare(t.name)).map((e) => ({
|
|
1809
1809
|
id: e.id,
|
|
1810
1810
|
name: e.name.replace(/\//g, "."),
|
|
1811
1811
|
alias: ""
|
|
1812
1812
|
})) : []
|
|
1813
1813
|
});
|
|
1814
1814
|
}
|
|
1815
|
-
function
|
|
1815
|
+
function Ct(e, t) {
|
|
1816
1816
|
let n = e.connectionId && t?.connections[e.connectionId] || (e.connection ? t?.connectionByName(e.connection) : void 0);
|
|
1817
|
-
return
|
|
1817
|
+
return qe({
|
|
1818
1818
|
dashboard: e,
|
|
1819
1819
|
dataConnectionName: e.connection,
|
|
1820
|
-
activeImports:
|
|
1820
|
+
activeImports: F(e),
|
|
1821
1821
|
isDataConnectionActive: n?.connected ?? !1
|
|
1822
1822
|
});
|
|
1823
1823
|
}
|
|
1824
|
-
function
|
|
1824
|
+
function wt(e, t, n) {
|
|
1825
1825
|
let r = null;
|
|
1826
1826
|
return () => {
|
|
1827
1827
|
let i = e();
|
|
1828
|
-
return i ? (r === null && (r =
|
|
1828
|
+
return i ? (r === null && (r = Ct(i, t)), `${St(i, t, n)}
|
|
1829
1829
|
|
|
1830
1830
|
DASHBOARD STATE AT THE START OF THIS CONVERSATION (snapshot — call get_dashboard_state for current state):
|
|
1831
1831
|
${r}`) : "";
|
|
1832
1832
|
};
|
|
1833
1833
|
}
|
|
1834
|
-
function
|
|
1835
|
-
let { dashboardStore: n, connectionStore: r, editorStore: i, queryExecutionService: a } = t, o = n.dashboards[e], s =
|
|
1834
|
+
function Tt(e, t) {
|
|
1835
|
+
let { dashboardStore: n, connectionStore: r, editorStore: i, queryExecutionService: a } = t, o = n.dashboards[e], s = M(o, r);
|
|
1836
1836
|
return n.getOrCreateQueryExecutor(e, {
|
|
1837
1837
|
queryExecutionService: a,
|
|
1838
1838
|
connectionName: s,
|
|
@@ -1840,14 +1840,14 @@ function Dt(e, t) {
|
|
|
1840
1840
|
getDashboardData: (e) => n.dashboards[e],
|
|
1841
1841
|
getItemData: (e, t) => {
|
|
1842
1842
|
let r = n.dashboards[t];
|
|
1843
|
-
return r ?
|
|
1843
|
+
return r ? vt(r, e, { rootContent: mt(r, i) }) : N(e);
|
|
1844
1844
|
},
|
|
1845
|
-
setItemData: (e, t, r) =>
|
|
1845
|
+
setItemData: (e, t, r) => yt(n, t, e, r)
|
|
1846
1846
|
});
|
|
1847
1847
|
}
|
|
1848
|
-
function
|
|
1848
|
+
function Et(e, t) {
|
|
1849
1849
|
let { dashboardStore: n, connectionStore: r, editorStore: i, queryExecutionService: a } = t;
|
|
1850
|
-
return new
|
|
1850
|
+
return new pt({
|
|
1851
1851
|
dashboardStore: n,
|
|
1852
1852
|
connectionStore: r,
|
|
1853
1853
|
editorStore: i,
|
|
@@ -1855,7 +1855,7 @@ function Ot(e, t) {
|
|
|
1855
1855
|
dashboardId: e,
|
|
1856
1856
|
getActiveImports: () => {
|
|
1857
1857
|
let t = n.dashboards[e];
|
|
1858
|
-
return t ?
|
|
1858
|
+
return t ? F(t) : [];
|
|
1859
1859
|
},
|
|
1860
1860
|
setActiveImports: (t) => {
|
|
1861
1861
|
n.updateDashboardImports(e, t.map((e) => ({
|
|
@@ -1864,11 +1864,11 @@ function Ot(e, t) {
|
|
|
1864
1864
|
alias: e.alias
|
|
1865
1865
|
})));
|
|
1866
1866
|
},
|
|
1867
|
-
getDashboardQueryExecutor: () =>
|
|
1867
|
+
getDashboardQueryExecutor: () => Tt(e, t),
|
|
1868
1868
|
refreshItem: () => void 0
|
|
1869
1869
|
});
|
|
1870
1870
|
}
|
|
1871
|
-
var
|
|
1871
|
+
var Dt = /* @__PURE__ */ new Set([
|
|
1872
1872
|
"add_dashboard_item",
|
|
1873
1873
|
"update_dashboard_item",
|
|
1874
1874
|
"remove_dashboard_item",
|
|
@@ -1879,23 +1879,23 @@ var kt = /* @__PURE__ */ new Set([
|
|
|
1879
1879
|
"add_claim_section",
|
|
1880
1880
|
"add_appendix_header",
|
|
1881
1881
|
"set_report_layout"
|
|
1882
|
-
]),
|
|
1883
|
-
async function
|
|
1882
|
+
]), I = /* @__PURE__ */ new Set();
|
|
1883
|
+
async function Ot(e) {
|
|
1884
1884
|
let { dashboard: t, chatId: n, chatStore: r, dashboardStore: i, setActiveDashboard: a } = e;
|
|
1885
1885
|
if (t.parentDashboardId) return t.id;
|
|
1886
1886
|
let o = `${n}:${t.id}`;
|
|
1887
|
-
if (
|
|
1887
|
+
if (I.has(o)) return t.id;
|
|
1888
1888
|
let s = r.chats[n];
|
|
1889
|
-
if (s?.sourceRefId === t.id && s?.messages.some((e) => e.executedToolCalls?.some((e) =>
|
|
1889
|
+
if (s?.sourceRefId === t.id && s?.messages.some((e) => e.executedToolCalls?.some((e) => Dt.has(e.name) && e.result?.success)) || Object.keys(t.gridItems).length === 0) return I.add(o), t.id;
|
|
1890
1890
|
let c = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19).replace(/:/g, "");
|
|
1891
1891
|
try {
|
|
1892
1892
|
let e = i.forkDashboard(t.id, `chat-${c}`);
|
|
1893
|
-
return s && s.sourceRefId === t.id && (t.setChatId(null), e.setChatId(n), s.sourceRefId = e.id, s.changed = !0),
|
|
1893
|
+
return s && s.sourceRefId === t.id && (t.setChatId(null), e.setChatId(n), s.sourceRefId = e.id, s.changed = !0), I.add(`${n}:${e.id}`), a?.(e.id), e.id;
|
|
1894
1894
|
} catch (e) {
|
|
1895
1895
|
return console.error("Failed to auto-fork dashboard for chat mutation:", e), t.id;
|
|
1896
1896
|
}
|
|
1897
1897
|
}
|
|
1898
|
-
async function
|
|
1898
|
+
async function kt(e) {
|
|
1899
1899
|
let { chatStore: t, chatId: n, toolExecutor: r, imports: i } = e, a = t.chats[n];
|
|
1900
1900
|
if (!a || a.messages.length > 0 || i.length === 0) return;
|
|
1901
1901
|
let o = i[0], s;
|
|
@@ -1921,7 +1921,7 @@ async function jt(e) {
|
|
|
1921
1921
|
}, d = {
|
|
1922
1922
|
toolCallId: c,
|
|
1923
1923
|
toolName: "select_active_import",
|
|
1924
|
-
result:
|
|
1924
|
+
result: ee(s)
|
|
1925
1925
|
};
|
|
1926
1926
|
t.addMessageToChat(n, {
|
|
1927
1927
|
role: "assistant",
|
|
@@ -1935,26 +1935,26 @@ async function jt(e) {
|
|
|
1935
1935
|
hidden: !0
|
|
1936
1936
|
});
|
|
1937
1937
|
}
|
|
1938
|
-
async function
|
|
1938
|
+
async function At(e) {
|
|
1939
1939
|
let { dashboardId: t, prompt: n, stores: r, deps: i } = e, a = r.dashboardStore.dashboards[t];
|
|
1940
1940
|
if (!a) throw Error(`Dashboard ${t} not found`);
|
|
1941
|
-
let o =
|
|
1942
|
-
return await
|
|
1941
|
+
let o = bt(a, r.chatStore, r.llmConnectionStore), s = Et(t, r);
|
|
1942
|
+
return await kt({
|
|
1943
1943
|
chatStore: r.chatStore,
|
|
1944
1944
|
chatId: o,
|
|
1945
1945
|
toolExecutor: s,
|
|
1946
|
-
imports:
|
|
1946
|
+
imports: F(a)
|
|
1947
1947
|
}), r.chatStore.executeMessage(o, n, i, { overrides: {
|
|
1948
|
-
tools:
|
|
1948
|
+
tools: xt(a),
|
|
1949
1949
|
executeToolCall: (e, t) => s.executeToolCall(e, t),
|
|
1950
|
-
buildSystemPrompt:
|
|
1950
|
+
buildSystemPrompt: wt(() => r.dashboardStore.dashboards[t], r.connectionStore, r.editorStore)
|
|
1951
1951
|
} }).catch((e) => {
|
|
1952
1952
|
console.error(`Headless dashboard agent run for ${t} failed`, e);
|
|
1953
1953
|
}), o;
|
|
1954
1954
|
}
|
|
1955
1955
|
//#endregion
|
|
1956
1956
|
//#region llm/overseerToolExecutor.ts
|
|
1957
|
-
var
|
|
1957
|
+
var jt = class {
|
|
1958
1958
|
chatStore;
|
|
1959
1959
|
projectStore;
|
|
1960
1960
|
overseerChatId;
|
|
@@ -1986,9 +1986,9 @@ var Nt = class {
|
|
|
1986
1986
|
}
|
|
1987
1987
|
}
|
|
1988
1988
|
spawnSubchat(e, t, n) {
|
|
1989
|
-
if (!
|
|
1989
|
+
if (!Re.includes(e)) return {
|
|
1990
1990
|
success: !1,
|
|
1991
|
-
error: `Invalid kind '${e}'. Must be one of: ${
|
|
1991
|
+
error: `Invalid kind '${e}'. Must be one of: ${Re.join(", ")}`
|
|
1992
1992
|
};
|
|
1993
1993
|
if (!t || typeof t != "string") return {
|
|
1994
1994
|
success: !1,
|
|
@@ -2004,7 +2004,7 @@ var Nt = class {
|
|
|
2004
2004
|
success: !1,
|
|
2005
2005
|
error: "Overseer chat not found"
|
|
2006
2006
|
};
|
|
2007
|
-
let a = this.projectStore.projects[r], o = a?.dataConnectionId || i.dataConnectionId, s = i.dataConnectionName, c = this.chatStore.newChat(i.llmConnectionName, s, n ||
|
|
2007
|
+
let a = this.projectStore.projects[r], o = a?.dataConnectionId || i.dataConnectionId, s = i.dataConnectionName, c = this.chatStore.newChat(i.llmConnectionName, s, n || Mt(e, t), o);
|
|
2008
2008
|
return c.kind = e, c.parentChatId = this.overseerChatId, c.parentProjectId = r, c.changed = !0, this.projectStore.addSubchatToProject(r, c.id), this.chatStore.executeMessage(c.id, t, this.deps).catch((e) => {
|
|
2009
2009
|
console.error(`Subchat ${c.id} failed`, e);
|
|
2010
2010
|
}), {
|
|
@@ -2097,7 +2097,7 @@ var Nt = class {
|
|
|
2097
2097
|
};
|
|
2098
2098
|
let i = this.chatStore.isChatExecuting(e) ? "still running" : "idle / finished";
|
|
2099
2099
|
try {
|
|
2100
|
-
let n = await
|
|
2100
|
+
let n = await le(r, t, "peek");
|
|
2101
2101
|
return {
|
|
2102
2102
|
success: !0,
|
|
2103
2103
|
message: `Peek of ${e} (${t.kind}, ${i}):\n\n${n}`
|
|
@@ -2149,7 +2149,7 @@ var Nt = class {
|
|
|
2149
2149
|
}
|
|
2150
2150
|
this.projectStore.addDashboardToProject(i, o.id);
|
|
2151
2151
|
try {
|
|
2152
|
-
|
|
2152
|
+
At({
|
|
2153
2153
|
dashboardId: o.id,
|
|
2154
2154
|
prompt: r,
|
|
2155
2155
|
stores: {
|
|
@@ -2195,7 +2195,7 @@ var Nt = class {
|
|
|
2195
2195
|
for (let e of t.subchatIds) {
|
|
2196
2196
|
let t = this.chatStore.chats[e];
|
|
2197
2197
|
if (!t) continue;
|
|
2198
|
-
let n = this.chatStore.isChatExecuting(e) ? "running" : "idle", i = t.messages[t.messages.length - 1], a = i ?
|
|
2198
|
+
let n = this.chatStore.isChatExecuting(e) ? "running" : "idle", i = t.messages[t.messages.length - 1], a = i ? Nt(i.content || "", 80) : "";
|
|
2199
2199
|
r.push(` - ${e} [${t.kind}] (${n}) "${t.name}" — last: ${a}`);
|
|
2200
2200
|
}
|
|
2201
2201
|
return {
|
|
@@ -2204,15 +2204,15 @@ var Nt = class {
|
|
|
2204
2204
|
};
|
|
2205
2205
|
}
|
|
2206
2206
|
};
|
|
2207
|
-
function
|
|
2208
|
-
return `${e}: ${
|
|
2207
|
+
function Mt(e, t) {
|
|
2208
|
+
return `${e}: ${Nt(t.split(/\s+/).slice(0, 6).join(" "), 50)}`;
|
|
2209
2209
|
}
|
|
2210
|
-
function
|
|
2210
|
+
function Nt(e, t) {
|
|
2211
2211
|
return e.length <= t ? e : e.slice(0, t - 1) + "…";
|
|
2212
2212
|
}
|
|
2213
2213
|
//#endregion
|
|
2214
2214
|
//#region llm/architectToolExecutor.ts
|
|
2215
|
-
var
|
|
2215
|
+
var Pt = class {
|
|
2216
2216
|
chatStore;
|
|
2217
2217
|
projectStore;
|
|
2218
2218
|
editorStore;
|
|
@@ -2286,7 +2286,7 @@ var It = class {
|
|
|
2286
2286
|
success: !1,
|
|
2287
2287
|
error: `File "${e}" not found in project.`
|
|
2288
2288
|
};
|
|
2289
|
-
let n =
|
|
2289
|
+
let n = Rt(t.contents);
|
|
2290
2290
|
return {
|
|
2291
2291
|
success: !0,
|
|
2292
2292
|
message: `Contents of ${e} (${t.type}, ${t.contents.length} bytes):\n\n${n}`
|
|
@@ -2301,7 +2301,7 @@ var It = class {
|
|
|
2301
2301
|
success: !1,
|
|
2302
2302
|
error: "content is required"
|
|
2303
2303
|
};
|
|
2304
|
-
let r =
|
|
2304
|
+
let r = It(t);
|
|
2305
2305
|
if (!r) return {
|
|
2306
2306
|
success: !1,
|
|
2307
2307
|
error: `type must be 'trilogy' or 'sql' (got ${JSON.stringify(t)}).`
|
|
@@ -2311,7 +2311,7 @@ var It = class {
|
|
|
2311
2311
|
success: !1,
|
|
2312
2312
|
error: "No project context."
|
|
2313
2313
|
};
|
|
2314
|
-
let a =
|
|
2314
|
+
let a = Lt(e, r);
|
|
2315
2315
|
if (this.findEditorByName(a)) return {
|
|
2316
2316
|
success: !1,
|
|
2317
2317
|
error: `File "${a}" already exists. Use update_file to modify.`
|
|
@@ -2337,14 +2337,14 @@ var It = class {
|
|
|
2337
2337
|
success: !1,
|
|
2338
2338
|
error: `File "${e}" not found. Use create_file to add it.`
|
|
2339
2339
|
};
|
|
2340
|
-
let r =
|
|
2340
|
+
let r = D(e) || n.type;
|
|
2341
2341
|
if (r !== "preql" && r !== "trilogy" && r !== "sql") return {
|
|
2342
2342
|
success: !1,
|
|
2343
2343
|
error: `update_file handles .preql and .sql files; ${e} is ${r}.`
|
|
2344
2344
|
};
|
|
2345
2345
|
n.contents = t, n.changed = !0;
|
|
2346
2346
|
let i = r === "preql" || r === "trilogy", a = i ? `\n${await this.validateContent(e, t)}` : "", o = "";
|
|
2347
|
-
if (n.tags.includes(
|
|
2347
|
+
if (n.tags.includes(k.STARTUP_SCRIPT)) {
|
|
2348
2348
|
let e = this.connection;
|
|
2349
2349
|
if (e) try {
|
|
2350
2350
|
await this.connectionStore.connectConnection(e.id), o = `\nRe-ran startup against ${e.name}.`;
|
|
@@ -2373,11 +2373,11 @@ var It = class {
|
|
|
2373
2373
|
success: !1,
|
|
2374
2374
|
error: `File "${e}" not found in project.`
|
|
2375
2375
|
};
|
|
2376
|
-
if (n === "none") return r.removeTag(
|
|
2376
|
+
if (n === "none") return r.removeTag(k.STARTUP_SCRIPT), {
|
|
2377
2377
|
success: !0,
|
|
2378
2378
|
message: `Cleared purpose tags on ${e}.`
|
|
2379
2379
|
};
|
|
2380
|
-
r.addTag(
|
|
2380
|
+
r.addTag(k.STARTUP_SCRIPT);
|
|
2381
2381
|
let i = this.connection;
|
|
2382
2382
|
if (!i) return {
|
|
2383
2383
|
success: !0,
|
|
@@ -2480,7 +2480,7 @@ var It = class {
|
|
|
2480
2480
|
let r = n.queryResult;
|
|
2481
2481
|
return {
|
|
2482
2482
|
success: !0,
|
|
2483
|
-
message:
|
|
2483
|
+
message: zt(r),
|
|
2484
2484
|
executionTime: r.executionTime,
|
|
2485
2485
|
generatedSql: r.generatedSql,
|
|
2486
2486
|
query: e
|
|
@@ -2493,7 +2493,7 @@ var It = class {
|
|
|
2493
2493
|
text: "select 1;",
|
|
2494
2494
|
editorType: "trilogy",
|
|
2495
2495
|
imports: [],
|
|
2496
|
-
extraContent: [...
|
|
2496
|
+
extraContent: [...b(this.connectionStore, this.editorStore, n.id), {
|
|
2497
2497
|
alias: e.replace(/\.preql$/, ""),
|
|
2498
2498
|
contents: t
|
|
2499
2499
|
}],
|
|
@@ -2517,8 +2517,8 @@ var It = class {
|
|
|
2517
2517
|
return `Validation threw: ${e instanceof Error ? e.message : String(e)}`;
|
|
2518
2518
|
}
|
|
2519
2519
|
}
|
|
2520
|
-
},
|
|
2521
|
-
function
|
|
2520
|
+
}, L = 4e3, Ft = L * 2;
|
|
2521
|
+
function It(e) {
|
|
2522
2522
|
switch (typeof e == "string" ? e.toLowerCase() : "") {
|
|
2523
2523
|
case "trilogy":
|
|
2524
2524
|
case "preql": return "preql";
|
|
@@ -2526,15 +2526,15 @@ function Rt(e) {
|
|
|
2526
2526
|
default: return null;
|
|
2527
2527
|
}
|
|
2528
2528
|
}
|
|
2529
|
-
function
|
|
2529
|
+
function Lt(e, t) {
|
|
2530
2530
|
return t === "preql" ? e.endsWith(".preql") || e.endsWith(".trilogy") ? e : `${e}.preql` : e.endsWith(".sql") ? e : `${e}.sql`;
|
|
2531
2531
|
}
|
|
2532
|
-
function
|
|
2533
|
-
if (e.length <=
|
|
2534
|
-
let t = e.slice(0,
|
|
2535
|
-
return `${t}\n<redacted ${e.length -
|
|
2532
|
+
function Rt(e) {
|
|
2533
|
+
if (e.length <= Ft) return e;
|
|
2534
|
+
let t = e.slice(0, L), n = e.slice(-4e3);
|
|
2535
|
+
return `${t}\n<redacted ${e.length - L * 2} chars>\n${n}`;
|
|
2536
2536
|
}
|
|
2537
|
-
function
|
|
2537
|
+
function zt(e) {
|
|
2538
2538
|
let t = e.generatedSql ? `\nGenerated SQL:\n${e.generatedSql}\n` : "", n = e.results?.data || [], r = e.results?.headers ? Array.from(e.results.headers.keys()) : [], i = n.slice(0, 10);
|
|
2539
2539
|
return [
|
|
2540
2540
|
`Query OK — ${n.length} rows in ${e.executionTime}ms.`,
|
|
@@ -2545,7 +2545,7 @@ function Vt(e) {
|
|
|
2545
2545
|
}
|
|
2546
2546
|
//#endregion
|
|
2547
2547
|
//#region llm/registry/toolRegistry.ts
|
|
2548
|
-
var
|
|
2548
|
+
var Bt = {
|
|
2549
2549
|
chat: [
|
|
2550
2550
|
"data",
|
|
2551
2551
|
"artifacts",
|
|
@@ -2580,7 +2580,7 @@ var Ht = {
|
|
|
2580
2580
|
"docs",
|
|
2581
2581
|
"base"
|
|
2582
2582
|
]
|
|
2583
|
-
},
|
|
2583
|
+
}, Vt = class {
|
|
2584
2584
|
tools = /* @__PURE__ */ new Map();
|
|
2585
2585
|
toolsetCache = /* @__PURE__ */ new Map();
|
|
2586
2586
|
register(e) {
|
|
@@ -2591,7 +2591,7 @@ var Ht = {
|
|
|
2591
2591
|
e.forEach((e) => this.register(e));
|
|
2592
2592
|
}
|
|
2593
2593
|
toolsForContext(e) {
|
|
2594
|
-
let t =
|
|
2594
|
+
let t = Bt[e];
|
|
2595
2595
|
if (!t) throw Error(`Unknown toolset context '${e}'`);
|
|
2596
2596
|
let n = new Set(t);
|
|
2597
2597
|
return [...this.tools.values()].filter((e) => n.has(e.pack));
|
|
@@ -2604,7 +2604,7 @@ var Ht = {
|
|
|
2604
2604
|
return this.toolsForContext(e).map((e) => e.definition.name);
|
|
2605
2605
|
}
|
|
2606
2606
|
createExecutor(e, t, n) {
|
|
2607
|
-
let r = new Set(
|
|
2607
|
+
let r = new Set(Bt[e]), i = {
|
|
2608
2608
|
runtime: t,
|
|
2609
2609
|
session: n
|
|
2610
2610
|
};
|
|
@@ -2631,7 +2631,7 @@ var Ht = {
|
|
|
2631
2631
|
}
|
|
2632
2632
|
} };
|
|
2633
2633
|
}
|
|
2634
|
-
},
|
|
2634
|
+
}, Ht = class {
|
|
2635
2635
|
queryExecutionService;
|
|
2636
2636
|
connectionStore;
|
|
2637
2637
|
chatStore;
|
|
@@ -2678,11 +2678,11 @@ var Ht = {
|
|
|
2678
2678
|
if (r && n === "chart") {
|
|
2679
2679
|
let e = o.results;
|
|
2680
2680
|
if (e && e.headers) {
|
|
2681
|
-
let t =
|
|
2682
|
-
t.valid || (s = t.suggestedConfig, c = `Auto-corrected to default configuration.\n${
|
|
2681
|
+
let t = v(e.data, e.headers, r);
|
|
2682
|
+
t.valid || (s = t.suggestedConfig, c = `Auto-corrected to default configuration.\n${C(t)}`);
|
|
2683
2683
|
}
|
|
2684
2684
|
}
|
|
2685
|
-
let l =
|
|
2685
|
+
let l = w(), u = {
|
|
2686
2686
|
id: l,
|
|
2687
2687
|
type: n,
|
|
2688
2688
|
data: o.results,
|
|
@@ -2726,7 +2726,7 @@ var Ht = {
|
|
|
2726
2726
|
let r = this.resolvedChat?.imports || [], i = r.map((e) => ({
|
|
2727
2727
|
name: e.name,
|
|
2728
2728
|
alias: e.alias || ""
|
|
2729
|
-
})), a =
|
|
2729
|
+
})), a = b(this.connectionStore, this.editorStore, t, r), o = {
|
|
2730
2730
|
text: e.trim(),
|
|
2731
2731
|
editorType: "trilogy",
|
|
2732
2732
|
imports: i,
|
|
@@ -2769,20 +2769,20 @@ var Ht = {
|
|
|
2769
2769
|
return !this.resolvedChatId || !this.editorStore ? {
|
|
2770
2770
|
success: !1,
|
|
2771
2771
|
error: "No active chat or editor store not available"
|
|
2772
|
-
} :
|
|
2772
|
+
} : de(e, this.importAccessor, {
|
|
2773
2773
|
connectionStore: this.connectionStore,
|
|
2774
2774
|
editorStore: this.editorStore,
|
|
2775
2775
|
queryExecutionService: this.queryExecutionService
|
|
2776
2776
|
}, this.editorStore, this.connectionStore);
|
|
2777
2777
|
}
|
|
2778
2778
|
listAvailableImports() {
|
|
2779
|
-
return
|
|
2779
|
+
return ie(this.importAccessor, this.editorStore, this.connectionStore);
|
|
2780
2780
|
}
|
|
2781
2781
|
getAvailableImports(e) {
|
|
2782
|
-
return
|
|
2782
|
+
return be(this.editorStore, e, this.connectionStore);
|
|
2783
2783
|
}
|
|
2784
2784
|
async connectDataConnection(e) {
|
|
2785
|
-
return
|
|
2785
|
+
return ce(this.connectionStore, e, { onConnected: (e) => {
|
|
2786
2786
|
if (this.resolvedChatId && this.chatStore) {
|
|
2787
2787
|
let t = this.connectionStore.connectionByName(e);
|
|
2788
2788
|
this.chatStore.updateChatDataConnection(this.resolvedChatId, e, t?.id || "");
|
|
@@ -2794,7 +2794,7 @@ var Ht = {
|
|
|
2794
2794
|
success: !1,
|
|
2795
2795
|
error: "Markdown content is required and must be a non-empty string"
|
|
2796
2796
|
};
|
|
2797
|
-
let i =
|
|
2797
|
+
let i = w(), a = null;
|
|
2798
2798
|
if (n && n.trim()) {
|
|
2799
2799
|
if (r ||= this.resolvedChat?.dataConnectionName || "", !r) return {
|
|
2800
2800
|
success: !1,
|
|
@@ -2848,31 +2848,31 @@ var Ht = {
|
|
|
2848
2848
|
};
|
|
2849
2849
|
}
|
|
2850
2850
|
getArtifact(e) {
|
|
2851
|
-
let
|
|
2852
|
-
if (!
|
|
2851
|
+
let t = this.resolvedChat;
|
|
2852
|
+
if (!t) return {
|
|
2853
2853
|
success: !1,
|
|
2854
2854
|
error: "No active chat session."
|
|
2855
2855
|
};
|
|
2856
|
-
let
|
|
2857
|
-
if (!
|
|
2856
|
+
let n = t.getArtifactById(e);
|
|
2857
|
+
if (!n) return {
|
|
2858
2858
|
success: !1,
|
|
2859
2859
|
error: `Artifact "${e}" not found. Use list_artifacts to see available artifacts.`
|
|
2860
2860
|
};
|
|
2861
|
-
let
|
|
2862
|
-
id:
|
|
2863
|
-
type:
|
|
2861
|
+
let r = {
|
|
2862
|
+
id: n.id,
|
|
2863
|
+
type: n.type
|
|
2864
2864
|
};
|
|
2865
|
-
if (
|
|
2866
|
-
else if (
|
|
2867
|
-
let e = typeof
|
|
2868
|
-
|
|
2865
|
+
if (n.config && (r.config = { ...n.config }), n.type === "markdown") r.markdown = n.data?.markdown || "", r.query = n.data?.query || "", r.hasQueryResults = !!n.data?.queryResults;
|
|
2866
|
+
else if (n.data) {
|
|
2867
|
+
let e = typeof n.data.toJSON == "function" ? n.data.toJSON() : n.data, { head: t, tail: i, totalRows: a, cutCount: o } = oe(e);
|
|
2868
|
+
o > 0 ? (r.data = {
|
|
2869
2869
|
...e,
|
|
2870
|
-
data:
|
|
2871
|
-
},
|
|
2870
|
+
data: t
|
|
2871
|
+
}, r._truncated = `${o} of ${a} rows cut off (showing first 50 and last 50). Use get_artifact_rows to fetch any range.`, r._tail = i) : r.data = e;
|
|
2872
2872
|
}
|
|
2873
2873
|
return {
|
|
2874
2874
|
success: !0,
|
|
2875
|
-
message: `Artifact "${e}" (${
|
|
2875
|
+
message: `Artifact "${e}" (${n.type}):\n${JSON.stringify(r, null, 2)}`
|
|
2876
2876
|
};
|
|
2877
2877
|
}
|
|
2878
2878
|
getArtifactRows(e, t, n) {
|
|
@@ -2885,7 +2885,7 @@ var Ht = {
|
|
|
2885
2885
|
return i ? i.type !== "results" && i.type !== "chart" ? {
|
|
2886
2886
|
success: !1,
|
|
2887
2887
|
error: `Artifact "${e}" does not contain tabular data (type: ${i.type}). Only results and chart artifacts support row fetching.`
|
|
2888
|
-
} :
|
|
2888
|
+
} : ge(e, i.data, t, n) : {
|
|
2889
2889
|
success: !1,
|
|
2890
2890
|
error: `Artifact "${e}" not found. Use list_artifacts to see available artifacts.`
|
|
2891
2891
|
};
|
|
@@ -2911,7 +2911,7 @@ var Ht = {
|
|
|
2911
2911
|
}, o.push("title")), r !== void 0 && (a.type === "chart" || a.type === "results")) {
|
|
2912
2912
|
let t = a.data;
|
|
2913
2913
|
if (t && t.headers) {
|
|
2914
|
-
let n =
|
|
2914
|
+
let n = v(t.data, t.headers, r);
|
|
2915
2915
|
if (!n.valid) {
|
|
2916
2916
|
let t = n.suggestedConfig;
|
|
2917
2917
|
return a.config = {
|
|
@@ -2919,7 +2919,7 @@ var Ht = {
|
|
|
2919
2919
|
chartConfig: t
|
|
2920
2920
|
}, i.updatedAt = /* @__PURE__ */ new Date(), i.changed = !0, {
|
|
2921
2921
|
success: !1,
|
|
2922
|
-
error: `Auto-corrected artifact "${e}" chart config to default configuration.\n` +
|
|
2922
|
+
error: `Auto-corrected artifact "${e}" chart config to default configuration.\n` + C(n)
|
|
2923
2923
|
};
|
|
2924
2924
|
}
|
|
2925
2925
|
}
|
|
@@ -2983,13 +2983,13 @@ var Ht = {
|
|
|
2983
2983
|
message: r.length > 0 ? `Hidden ${i.length} artifact(s). Not found: ${r.join(", ")}. Hidden artifacts remain accessible to you via list_artifacts and can be restored by the user.` : `Hidden ${i.length} artifact(s). They remain accessible via list_artifacts and can be restored by the user.`
|
|
2984
2984
|
};
|
|
2985
2985
|
}
|
|
2986
|
-
},
|
|
2986
|
+
}, Ut = [
|
|
2987
2987
|
"run_trilogy_query",
|
|
2988
2988
|
"chart_trilogy_query",
|
|
2989
2989
|
"select_active_import",
|
|
2990
2990
|
"list_available_imports",
|
|
2991
2991
|
"connect_data_connection"
|
|
2992
|
-
],
|
|
2992
|
+
], Wt = [
|
|
2993
2993
|
"create_markdown",
|
|
2994
2994
|
"list_artifacts",
|
|
2995
2995
|
"get_artifact",
|
|
@@ -2997,35 +2997,35 @@ var Ht = {
|
|
|
2997
2997
|
"update_artifact",
|
|
2998
2998
|
"hide_artifact",
|
|
2999
2999
|
"reorder_artifacts"
|
|
3000
|
-
],
|
|
3001
|
-
function
|
|
3002
|
-
let t =
|
|
3000
|
+
], Gt = ["return_to_user"];
|
|
3001
|
+
function Kt(e) {
|
|
3002
|
+
let t = ne.find((t) => t.name === e);
|
|
3003
3003
|
if (!t) throw Error(`CHAT_TOOLS is missing expected tool '${e}'`);
|
|
3004
3004
|
return t;
|
|
3005
3005
|
}
|
|
3006
|
-
function
|
|
3006
|
+
function qt(e) {
|
|
3007
3007
|
let t = "chatToolExecutor", n = e.session.cache.get(t);
|
|
3008
|
-
return n || (n = new
|
|
3008
|
+
return n || (n = new Ht(e.runtime.queryExecutionService, e.runtime.connectionStore, e.runtime.chatStore, e.runtime.editorStore, e.session.chatId), e.session.cache.set(t, n)), n;
|
|
3009
3009
|
}
|
|
3010
|
-
function
|
|
3010
|
+
function R(e, t) {
|
|
3011
3011
|
return e.map((e) => ({
|
|
3012
|
-
definition:
|
|
3012
|
+
definition: Kt(e),
|
|
3013
3013
|
pack: t,
|
|
3014
|
-
execute: (t, n) =>
|
|
3014
|
+
execute: (t, n) => qt(n).executeToolCall(e, t)
|
|
3015
3015
|
}));
|
|
3016
3016
|
}
|
|
3017
|
-
function
|
|
3018
|
-
return
|
|
3017
|
+
function Jt() {
|
|
3018
|
+
return R(Ut, "data");
|
|
3019
3019
|
}
|
|
3020
|
-
function
|
|
3021
|
-
return
|
|
3020
|
+
function Yt() {
|
|
3021
|
+
return R(Wt, "artifacts");
|
|
3022
3022
|
}
|
|
3023
|
-
function
|
|
3024
|
-
return
|
|
3023
|
+
function Xt() {
|
|
3024
|
+
return R(Gt, "base");
|
|
3025
3025
|
}
|
|
3026
3026
|
//#endregion
|
|
3027
3027
|
//#region llm/registry/packs/navigationPack.ts
|
|
3028
|
-
var
|
|
3028
|
+
var z = [
|
|
3029
3029
|
"editors",
|
|
3030
3030
|
"connections",
|
|
3031
3031
|
"dashboard",
|
|
@@ -3036,25 +3036,25 @@ var I = [
|
|
|
3036
3036
|
"welcome",
|
|
3037
3037
|
"settings"
|
|
3038
3038
|
];
|
|
3039
|
-
function
|
|
3039
|
+
function B(e) {
|
|
3040
3040
|
return e.runtime.navigation ? { available: !0 } : {
|
|
3041
3041
|
available: !1,
|
|
3042
3042
|
hint: "App navigation is not available in this embedding context."
|
|
3043
3043
|
};
|
|
3044
3044
|
}
|
|
3045
|
-
function
|
|
3045
|
+
function Zt(e, t) {
|
|
3046
3046
|
let n = e.runtime.dashboardStore;
|
|
3047
3047
|
if (!n) return null;
|
|
3048
3048
|
let r = Object.prototype.hasOwnProperty.call(n.dashboards, t) ? n.dashboards[t] : void 0;
|
|
3049
3049
|
return r && !r.deleted ? r : Object.values(n.dashboards).find((e) => e.name === t && !e.deleted) || null;
|
|
3050
3050
|
}
|
|
3051
|
-
function
|
|
3051
|
+
function Qt(e, t) {
|
|
3052
3052
|
let n = e.runtime.editorStore, r = Object.prototype.hasOwnProperty.call(n.editors, t) ? n.editors[t] : void 0;
|
|
3053
3053
|
if (r && !r.deleted) return r;
|
|
3054
3054
|
let i = n.getEditorByName(t);
|
|
3055
3055
|
return i && !i.deleted ? i : null;
|
|
3056
3056
|
}
|
|
3057
|
-
async function
|
|
3057
|
+
async function $t(e) {
|
|
3058
3058
|
let t = e.runtime.navigation, n = [];
|
|
3059
3059
|
n.push(`Active screen: ${t.activeScreen.value || "welcome"}`);
|
|
3060
3060
|
let r = t.tabs.value;
|
|
@@ -3079,11 +3079,11 @@ async function tn(e) {
|
|
|
3079
3079
|
message: n.join("\n")
|
|
3080
3080
|
};
|
|
3081
3081
|
}
|
|
3082
|
-
function
|
|
3082
|
+
function en() {
|
|
3083
3083
|
return [
|
|
3084
3084
|
{
|
|
3085
3085
|
pack: "navigation",
|
|
3086
|
-
availability:
|
|
3086
|
+
availability: B,
|
|
3087
3087
|
definition: {
|
|
3088
3088
|
name: "get_app_state",
|
|
3089
3089
|
description: "Get a snapshot of the current app state: which screen the user is viewing, open tabs, the active dashboard/editor, data connections and their status, and available dashboards. Call this to orient yourself before navigating or when context may have changed.",
|
|
@@ -3092,11 +3092,11 @@ function nn() {
|
|
|
3092
3092
|
properties: {}
|
|
3093
3093
|
}
|
|
3094
3094
|
},
|
|
3095
|
-
execute: (e, t) =>
|
|
3095
|
+
execute: (e, t) => $t(t)
|
|
3096
3096
|
},
|
|
3097
3097
|
{
|
|
3098
3098
|
pack: "navigation",
|
|
3099
|
-
availability:
|
|
3099
|
+
availability: B,
|
|
3100
3100
|
definition: {
|
|
3101
3101
|
name: "navigate_to_screen",
|
|
3102
3102
|
description: "Navigate the app to a different screen. Use open_dashboard/open_editor instead when targeting a specific dashboard or editor.",
|
|
@@ -3104,7 +3104,7 @@ function nn() {
|
|
|
3104
3104
|
type: "object",
|
|
3105
3105
|
properties: { screen: {
|
|
3106
3106
|
type: "string",
|
|
3107
|
-
enum: [...
|
|
3107
|
+
enum: [...z],
|
|
3108
3108
|
description: "The screen to navigate to"
|
|
3109
3109
|
} },
|
|
3110
3110
|
required: ["screen"]
|
|
@@ -3112,9 +3112,9 @@ function nn() {
|
|
|
3112
3112
|
},
|
|
3113
3113
|
execute: async (e, t) => {
|
|
3114
3114
|
let n = e.screen;
|
|
3115
|
-
if (!
|
|
3115
|
+
if (!z.includes(n)) return {
|
|
3116
3116
|
success: !1,
|
|
3117
|
-
error: `Unknown screen "${e.screen}". Valid screens: ${
|
|
3117
|
+
error: `Unknown screen "${e.screen}". Valid screens: ${z.join(", ")}`
|
|
3118
3118
|
};
|
|
3119
3119
|
let r = t.runtime.navigation;
|
|
3120
3120
|
return r.setActiveScreen(n), r.setActiveSidebarScreen(n), {
|
|
@@ -3125,7 +3125,7 @@ function nn() {
|
|
|
3125
3125
|
},
|
|
3126
3126
|
{
|
|
3127
3127
|
pack: "navigation",
|
|
3128
|
-
availability:
|
|
3128
|
+
availability: B,
|
|
3129
3129
|
definition: {
|
|
3130
3130
|
name: "open_dashboard",
|
|
3131
3131
|
description: "Open a dashboard in the main pane by id or name. The user will see the dashboard; subsequent dashboard tools default to it.",
|
|
@@ -3143,7 +3143,7 @@ function nn() {
|
|
|
3143
3143
|
success: !1,
|
|
3144
3144
|
error: "Dashboard store is not available in this context."
|
|
3145
3145
|
};
|
|
3146
|
-
let n =
|
|
3146
|
+
let n = Zt(t, String(e.dashboard_ref ?? ""));
|
|
3147
3147
|
if (!n) {
|
|
3148
3148
|
let n = Object.values(t.runtime.dashboardStore.dashboards).filter((e) => !e.deleted).map((e) => `"${e.name}"`).join(", ");
|
|
3149
3149
|
return {
|
|
@@ -3159,7 +3159,7 @@ function nn() {
|
|
|
3159
3159
|
},
|
|
3160
3160
|
{
|
|
3161
3161
|
pack: "navigation",
|
|
3162
|
-
availability:
|
|
3162
|
+
availability: B,
|
|
3163
3163
|
definition: {
|
|
3164
3164
|
name: "open_editor",
|
|
3165
3165
|
description: "Open an editor (query/model file) in the main pane by id or name, so the user can see it.",
|
|
@@ -3173,7 +3173,7 @@ function nn() {
|
|
|
3173
3173
|
}
|
|
3174
3174
|
},
|
|
3175
3175
|
execute: async (e, t) => {
|
|
3176
|
-
let n =
|
|
3176
|
+
let n = Qt(t, String(e.editor_ref ?? ""));
|
|
3177
3177
|
return n ? (t.runtime.navigation.setActiveEditor(n.id), {
|
|
3178
3178
|
success: !0,
|
|
3179
3179
|
message: `Opened editor "${n.name}" (id ${n.id}, type ${n.type}).`
|
|
@@ -3187,29 +3187,29 @@ function nn() {
|
|
|
3187
3187
|
}
|
|
3188
3188
|
//#endregion
|
|
3189
3189
|
//#region llm/registry/packs/editorPack.ts
|
|
3190
|
-
var
|
|
3191
|
-
function
|
|
3190
|
+
var tn = 4e4;
|
|
3191
|
+
function V(e, t) {
|
|
3192
3192
|
let n = e.runtime.editorStore, r = Object.prototype.hasOwnProperty.call(n.editors, t) ? n.editors[t] : void 0;
|
|
3193
3193
|
if (r && !r.deleted) return r;
|
|
3194
3194
|
let i = n.getEditorByName(t);
|
|
3195
3195
|
return i && !i.deleted ? i : null;
|
|
3196
3196
|
}
|
|
3197
|
-
function
|
|
3197
|
+
function nn(e) {
|
|
3198
3198
|
let t = (e.contents || "").split("\n").length;
|
|
3199
3199
|
return `- "${e.name}" (id ${e.id}, type ${e.type}, connection ${e.connection}, ${t} lines${e.tags.length ? `, tags: ${e.tags.join(",")}` : ""})`;
|
|
3200
3200
|
}
|
|
3201
|
-
function
|
|
3202
|
-
return e.storage === "remote" ?
|
|
3201
|
+
function rn(e, t) {
|
|
3202
|
+
return e.storage === "remote" ? E(t, e.type) : t;
|
|
3203
3203
|
}
|
|
3204
|
-
function
|
|
3204
|
+
function an(e, t, n) {
|
|
3205
3205
|
return Object.values(e.runtime.editorStore.editors).find((e) => !e.deleted && e.id !== t.id && e.connectionId === t.connectionId && e.name === n) || null;
|
|
3206
3206
|
}
|
|
3207
|
-
function
|
|
3207
|
+
function on(e, t, n, r) {
|
|
3208
3208
|
let i = 0;
|
|
3209
3209
|
for (let a of Object.values(e.runtime.modelStore?.models || {})) a.sources.some((e) => e.editor === t) && (n === "rename" ? a.updateModelSourceName(t, r || "") : a.removeModelSourceSimple(t), i += 1);
|
|
3210
3210
|
return i;
|
|
3211
3211
|
}
|
|
3212
|
-
async function
|
|
3212
|
+
async function H(e, t = !1) {
|
|
3213
3213
|
let n = [e.runtime.saveEditors, t ? e.runtime.saveModels : void 0].filter((e) => !!e), r = !e.runtime.saveEditors || t && !e.runtime.saveModels;
|
|
3214
3214
|
try {
|
|
3215
3215
|
await Promise.all(n.map((e) => Promise.resolve(e())));
|
|
@@ -3224,10 +3224,10 @@ async function B(e, t = !1) {
|
|
|
3224
3224
|
suffix: r ? " The change is marked for autosave." : " The change was saved."
|
|
3225
3225
|
};
|
|
3226
3226
|
}
|
|
3227
|
-
function
|
|
3227
|
+
function sn(e, t) {
|
|
3228
3228
|
return e > 0 ? ` The editor source was ${t} in ${e} model${e === 1 ? "" : "s"}.` : "";
|
|
3229
3229
|
}
|
|
3230
|
-
async function
|
|
3230
|
+
async function cn(e, t) {
|
|
3231
3231
|
let n = e.runtime.connectionStore, r = n.connections[t.connectionId] || n.connectionByName(t.connection);
|
|
3232
3232
|
if (!r) return {
|
|
3233
3233
|
success: !1,
|
|
@@ -3246,7 +3246,7 @@ async function ln(e, t) {
|
|
|
3246
3246
|
editorType: t.type === "sql" ? "sql" : "trilogy",
|
|
3247
3247
|
imports: [],
|
|
3248
3248
|
currentFilename: t.name,
|
|
3249
|
-
extraContent:
|
|
3249
|
+
extraContent: b(n, e.runtime.editorStore, r.id)
|
|
3250
3250
|
};
|
|
3251
3251
|
try {
|
|
3252
3252
|
let n = await (await e.runtime.queryExecutionService.executeQuery(r.id, i)).resultPromise;
|
|
@@ -3262,7 +3262,7 @@ async function ln(e, t) {
|
|
|
3262
3262
|
};
|
|
3263
3263
|
t.chartConfig && t.results?.headers && t.results.headers.size > 0 && JSON.stringify(Array.from(t.results.headers.keys())) !== JSON.stringify(Array.from(n.results.headers.keys())) && t.setChartConfig(null), e.runtime.editorStore.setEditorResults(t.id, n.results), t.executed_contents = t.contents, n.generatedSql && (t.generated_sql = n.generatedSql), typeof n.executionTime == "number" && (t.duration = n.executionTime);
|
|
3264
3264
|
let a = {
|
|
3265
|
-
id:
|
|
3265
|
+
id: w(),
|
|
3266
3266
|
type: "results",
|
|
3267
3267
|
data: n.results,
|
|
3268
3268
|
config: {
|
|
@@ -3289,7 +3289,7 @@ async function ln(e, t) {
|
|
|
3289
3289
|
};
|
|
3290
3290
|
}
|
|
3291
3291
|
}
|
|
3292
|
-
function
|
|
3292
|
+
function ln() {
|
|
3293
3293
|
return [
|
|
3294
3294
|
{
|
|
3295
3295
|
pack: "editor",
|
|
@@ -3315,7 +3315,7 @@ function un() {
|
|
|
3315
3315
|
message: e.connection ? `No editors found for connection "${e.connection}".` : "No editors exist yet. Use create_editor to make one."
|
|
3316
3316
|
} : {
|
|
3317
3317
|
success: !0,
|
|
3318
|
-
message: `${n.length} editor(s):\n${n.sort((e, t) => e.name.localeCompare(t.name)).map(
|
|
3318
|
+
message: `${n.length} editor(s):\n${n.sort((e, t) => e.name.localeCompare(t.name)).map(nn).join("\n")}`
|
|
3319
3319
|
};
|
|
3320
3320
|
}
|
|
3321
3321
|
},
|
|
@@ -3334,12 +3334,12 @@ function un() {
|
|
|
3334
3334
|
}
|
|
3335
3335
|
},
|
|
3336
3336
|
execute: async (e, t) => {
|
|
3337
|
-
let n =
|
|
3337
|
+
let n = V(t, String(e.editor_ref ?? ""));
|
|
3338
3338
|
if (!n) return {
|
|
3339
3339
|
success: !1,
|
|
3340
3340
|
error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
|
|
3341
3341
|
};
|
|
3342
|
-
let r = n.contents || "", i = r.split("\n").length, a = r.length >
|
|
3342
|
+
let r = n.contents || "", i = r.split("\n").length, a = r.length > tn ? `${r.slice(0, tn)}\n...(truncated at ${tn} of ${r.length} characters)` : r;
|
|
3343
3343
|
return {
|
|
3344
3344
|
success: !0,
|
|
3345
3345
|
message: `Editor "${n.name}" (id ${n.id}, type ${n.type}, ${i} lines):\n\n${a}`
|
|
@@ -3397,7 +3397,7 @@ function un() {
|
|
|
3397
3397
|
};
|
|
3398
3398
|
let a = e.type === "sql" ? "sql" : "preql", o = t.runtime.editorStore.newEditor(n, a, r.name, e.contents || "");
|
|
3399
3399
|
t.runtime.navigation?.setActiveEditor(o.id);
|
|
3400
|
-
let s = await
|
|
3400
|
+
let s = await H(t);
|
|
3401
3401
|
return s.success ? {
|
|
3402
3402
|
success: !0,
|
|
3403
3403
|
message: `Created editor "${o.name}" (id ${o.id}, type ${o.type}) on connection "${r.name}".${s.suffix}`
|
|
@@ -3425,7 +3425,7 @@ function un() {
|
|
|
3425
3425
|
}
|
|
3426
3426
|
},
|
|
3427
3427
|
execute: async (e, t) => {
|
|
3428
|
-
let n =
|
|
3428
|
+
let n = V(t, String(e.editor_ref ?? ""));
|
|
3429
3429
|
if (!n) return {
|
|
3430
3430
|
success: !1,
|
|
3431
3431
|
error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
|
|
@@ -3435,19 +3435,19 @@ function un() {
|
|
|
3435
3435
|
success: !1,
|
|
3436
3436
|
error: "New editor name cannot be empty."
|
|
3437
3437
|
};
|
|
3438
|
-
let i =
|
|
3438
|
+
let i = rn(n, r), a = an(t, n, i);
|
|
3439
3439
|
if (a) return {
|
|
3440
3440
|
success: !1,
|
|
3441
3441
|
error: `An editor named "${i}" already exists on connection "${n.connection}" (id ${a.id}).`
|
|
3442
3442
|
};
|
|
3443
3443
|
let o = n.name;
|
|
3444
3444
|
t.runtime.editorStore.updateEditorName(n.id, r);
|
|
3445
|
-
let s =
|
|
3445
|
+
let s = on(t, n.id, "rename", n.name);
|
|
3446
3446
|
t.runtime.navigation?.updateTabName("editors", null, n.id);
|
|
3447
|
-
let c = await
|
|
3447
|
+
let c = await H(t, s > 0);
|
|
3448
3448
|
return c.success ? {
|
|
3449
3449
|
success: !0,
|
|
3450
|
-
message: `Renamed editor "${o}" to "${n.name}" (id ${n.id}).${
|
|
3450
|
+
message: `Renamed editor "${o}" to "${n.name}" (id ${n.id}).${sn(s, "renamed")}${c.suffix}`
|
|
3451
3451
|
} : c;
|
|
3452
3452
|
}
|
|
3453
3453
|
},
|
|
@@ -3472,7 +3472,7 @@ function un() {
|
|
|
3472
3472
|
}
|
|
3473
3473
|
},
|
|
3474
3474
|
execute: async (e, t) => {
|
|
3475
|
-
let n =
|
|
3475
|
+
let n = V(t, String(e.editor_ref ?? ""));
|
|
3476
3476
|
if (!n) return {
|
|
3477
3477
|
success: !1,
|
|
3478
3478
|
error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
|
|
@@ -3483,7 +3483,7 @@ function un() {
|
|
|
3483
3483
|
};
|
|
3484
3484
|
let r = (n.contents || "").split("\n").length;
|
|
3485
3485
|
n.setContent(e.contents);
|
|
3486
|
-
let i = await
|
|
3486
|
+
let i = await H(t);
|
|
3487
3487
|
return i.success ? {
|
|
3488
3488
|
success: !0,
|
|
3489
3489
|
message: `Updated editor "${n.name}": ${r} -> ${e.contents.split("\n").length} lines.${i.suffix}`
|
|
@@ -3511,7 +3511,7 @@ function un() {
|
|
|
3511
3511
|
}
|
|
3512
3512
|
},
|
|
3513
3513
|
execute: async (e, t) => {
|
|
3514
|
-
let n =
|
|
3514
|
+
let n = V(t, String(e.editor_ref ?? ""));
|
|
3515
3515
|
if (!n) return {
|
|
3516
3516
|
success: !1,
|
|
3517
3517
|
error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
|
|
@@ -3525,12 +3525,12 @@ function un() {
|
|
|
3525
3525
|
error: `Editor "${n.name}" is currently running. Wait for it to finish or cancel it before deleting.`
|
|
3526
3526
|
};
|
|
3527
3527
|
n.delete();
|
|
3528
|
-
let r =
|
|
3528
|
+
let r = on(t, n.id, "delete");
|
|
3529
3529
|
t.runtime.navigation?.closeTab(null, n.id);
|
|
3530
|
-
let i = await
|
|
3530
|
+
let i = await H(t, r > 0);
|
|
3531
3531
|
return i.success ? {
|
|
3532
3532
|
success: !0,
|
|
3533
|
-
message: `Deleted editor "${n.name}" (id ${n.id}).${
|
|
3533
|
+
message: `Deleted editor "${n.name}" (id ${n.id}).${sn(r, "removed")}${i.suffix}`
|
|
3534
3534
|
} : i;
|
|
3535
3535
|
}
|
|
3536
3536
|
},
|
|
@@ -3549,8 +3549,8 @@ function un() {
|
|
|
3549
3549
|
}
|
|
3550
3550
|
},
|
|
3551
3551
|
execute: async (e, t) => {
|
|
3552
|
-
let n =
|
|
3553
|
-
return n ?
|
|
3552
|
+
let n = V(t, String(e.editor_ref ?? ""));
|
|
3553
|
+
return n ? cn(t, n) : {
|
|
3554
3554
|
success: !1,
|
|
3555
3555
|
error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
|
|
3556
3556
|
};
|
|
@@ -3576,13 +3576,13 @@ function un() {
|
|
|
3576
3576
|
required: ["query", "connection"]
|
|
3577
3577
|
}
|
|
3578
3578
|
},
|
|
3579
|
-
execute: (e, t) =>
|
|
3579
|
+
execute: (e, t) => qt(t).validateQuery(String(e.query ?? ""), String(e.connection ?? ""))
|
|
3580
3580
|
}
|
|
3581
3581
|
];
|
|
3582
3582
|
}
|
|
3583
3583
|
//#endregion
|
|
3584
3584
|
//#region llm/registry/packs/dashboardPack.ts
|
|
3585
|
-
var
|
|
3585
|
+
var un = [
|
|
3586
3586
|
"get_dashboard_state",
|
|
3587
3587
|
"list_dashboard_items",
|
|
3588
3588
|
"get_dashboard_item",
|
|
@@ -3596,7 +3596,7 @@ var dn = [
|
|
|
3596
3596
|
"set_dashboard_theme",
|
|
3597
3597
|
"capture_dashboard_screenshot"
|
|
3598
3598
|
];
|
|
3599
|
-
function
|
|
3599
|
+
function dn(e) {
|
|
3600
3600
|
return {
|
|
3601
3601
|
...e,
|
|
3602
3602
|
input_schema: {
|
|
@@ -3611,10 +3611,10 @@ function fn(e) {
|
|
|
3611
3611
|
}
|
|
3612
3612
|
};
|
|
3613
3613
|
}
|
|
3614
|
-
function
|
|
3614
|
+
function fn(e, t) {
|
|
3615
3615
|
return t || e.runtime.screenBridge?.dashboard?.dashboardId || e.runtime.navigation?.activeDashboard.value || "";
|
|
3616
3616
|
}
|
|
3617
|
-
function
|
|
3617
|
+
function pn(e, t) {
|
|
3618
3618
|
let n = `dashboardExecutor:${t}`, r = e.session.cache.get(n);
|
|
3619
3619
|
if (r) return r;
|
|
3620
3620
|
let { dashboardStore: i, connectionStore: a, editorStore: o, chatStore: s, queryExecutionService: c } = e.runtime;
|
|
@@ -3630,7 +3630,7 @@ function mn(e, t) {
|
|
|
3630
3630
|
queryExecutionService: c,
|
|
3631
3631
|
llmConnectionStore: e.runtime.llmConnectionStore
|
|
3632
3632
|
};
|
|
3633
|
-
return r = new
|
|
3633
|
+
return r = new pt({
|
|
3634
3634
|
dashboardStore: i,
|
|
3635
3635
|
connectionStore: a,
|
|
3636
3636
|
editorStore: o,
|
|
@@ -3638,7 +3638,7 @@ function mn(e, t) {
|
|
|
3638
3638
|
dashboardId: t,
|
|
3639
3639
|
getActiveImports: () => {
|
|
3640
3640
|
let e = i.dashboards[t];
|
|
3641
|
-
return e ?
|
|
3641
|
+
return e ? F(e) : [];
|
|
3642
3642
|
},
|
|
3643
3643
|
setActiveImports: (e) => {
|
|
3644
3644
|
i.updateDashboardImports(t, e.map((e) => ({
|
|
@@ -3649,7 +3649,7 @@ function mn(e, t) {
|
|
|
3649
3649
|
},
|
|
3650
3650
|
getDashboardQueryExecutor: () => {
|
|
3651
3651
|
let e = l();
|
|
3652
|
-
return e ? e.getDashboardQueryExecutor() ?? null :
|
|
3652
|
+
return e ? e.getDashboardQueryExecutor() ?? null : Tt(t, u);
|
|
3653
3653
|
},
|
|
3654
3654
|
refreshItem: (e) => l()?.refreshItem(e) ?? void 0,
|
|
3655
3655
|
captureDashboardImage: async () => {
|
|
@@ -3659,13 +3659,13 @@ function mn(e, t) {
|
|
|
3659
3659
|
}
|
|
3660
3660
|
}), e.session.cache.set(n, r), r;
|
|
3661
3661
|
}
|
|
3662
|
-
async function
|
|
3662
|
+
async function mn(e, t, n) {
|
|
3663
3663
|
let { dashboard_id: r, ...i } = t;
|
|
3664
3664
|
if (!n.runtime.dashboardStore) return {
|
|
3665
3665
|
success: !1,
|
|
3666
3666
|
error: "Dashboard store is not available in this context."
|
|
3667
3667
|
};
|
|
3668
|
-
let a =
|
|
3668
|
+
let a = fn(n, r ? String(r) : void 0);
|
|
3669
3669
|
if (!a) return {
|
|
3670
3670
|
success: !1,
|
|
3671
3671
|
error: "No dashboard is currently in view. Pass dashboard_id, or use open_dashboard / list_dashboards first."
|
|
@@ -3674,23 +3674,23 @@ async function hn(e, t, n) {
|
|
|
3674
3674
|
return !o || o.deleted ? {
|
|
3675
3675
|
success: !1,
|
|
3676
3676
|
error: `Dashboard "${a}" not found.`
|
|
3677
|
-
} : (
|
|
3677
|
+
} : (Dt.has(e) && (a = await Ot({
|
|
3678
3678
|
dashboard: o,
|
|
3679
3679
|
chatId: n.session.chatId,
|
|
3680
3680
|
chatStore: n.runtime.chatStore,
|
|
3681
3681
|
dashboardStore: n.runtime.dashboardStore,
|
|
3682
3682
|
setActiveDashboard: n.runtime.navigation ? (e) => n.runtime.navigation.setActiveDashboard(e) : void 0
|
|
3683
|
-
})),
|
|
3683
|
+
})), pn(n, a).executeToolCall(e, i));
|
|
3684
3684
|
}
|
|
3685
|
-
function
|
|
3685
|
+
function hn() {
|
|
3686
3686
|
return [
|
|
3687
|
-
...
|
|
3688
|
-
let t =
|
|
3687
|
+
...un.map((e) => {
|
|
3688
|
+
let t = j.find((t) => t.name === e);
|
|
3689
3689
|
if (!t) throw Error(`DASHBOARD_TOOLS is missing expected tool '${e}'`);
|
|
3690
3690
|
return {
|
|
3691
|
-
definition:
|
|
3691
|
+
definition: dn(t),
|
|
3692
3692
|
pack: "dashboard",
|
|
3693
|
-
execute: (t, n) =>
|
|
3693
|
+
execute: (t, n) => mn(e, t, n)
|
|
3694
3694
|
};
|
|
3695
3695
|
}),
|
|
3696
3696
|
{
|
|
@@ -3790,7 +3790,7 @@ function gn() {
|
|
|
3790
3790
|
hint: "No dashboard is mounted on screen. Use open_dashboard first, then retry."
|
|
3791
3791
|
},
|
|
3792
3792
|
execute: async (e, t) => {
|
|
3793
|
-
let n = t.runtime.screenBridge?.dashboard, r =
|
|
3793
|
+
let n = t.runtime.screenBridge?.dashboard, r = fn(t, e.dashboard_id ? String(e.dashboard_id) : void 0);
|
|
3794
3794
|
if (!n || n.dashboardId !== r) return {
|
|
3795
3795
|
success: !1,
|
|
3796
3796
|
error: `Dashboard "${r}" is not the one on screen. Use open_dashboard first.`
|
|
@@ -3806,22 +3806,22 @@ function gn() {
|
|
|
3806
3806
|
}
|
|
3807
3807
|
//#endregion
|
|
3808
3808
|
//#region llm/registry/packs/jobsPack.ts
|
|
3809
|
-
var
|
|
3810
|
-
function
|
|
3809
|
+
var gn = 2e3, _n = 120, vn = 300;
|
|
3810
|
+
function U(e) {
|
|
3811
3811
|
return e.runtime.jobsStore ? { available: !0 } : {
|
|
3812
3812
|
available: !1,
|
|
3813
3813
|
hint: "Job stores are not available in this context."
|
|
3814
3814
|
};
|
|
3815
3815
|
}
|
|
3816
|
-
function
|
|
3816
|
+
function W(e) {
|
|
3817
3817
|
return `job ${e.job_id}: ${e.operation} "${e.target}" — status ${e.status}${e.error ? `, error: ${e.error}` : ""}${e.pollingState && e.pollingState !== "ok" ? ` (polling ${e.pollingState})` : ""}`;
|
|
3818
3818
|
}
|
|
3819
|
-
var
|
|
3820
|
-
function
|
|
3819
|
+
var yn = null, bn = () => (yn ||= new Te(), yn);
|
|
3820
|
+
function xn() {
|
|
3821
3821
|
return [
|
|
3822
3822
|
{
|
|
3823
3823
|
pack: "jobs",
|
|
3824
|
-
availability:
|
|
3824
|
+
availability: U,
|
|
3825
3825
|
definition: {
|
|
3826
3826
|
name: "list_job_stores",
|
|
3827
3827
|
description: "List remote model stores that support jobs (run/refresh of hosted targets), with their status.",
|
|
@@ -3843,7 +3843,7 @@ function Sn() {
|
|
|
3843
3843
|
},
|
|
3844
3844
|
{
|
|
3845
3845
|
pack: "jobs",
|
|
3846
|
-
availability:
|
|
3846
|
+
availability: U,
|
|
3847
3847
|
definition: {
|
|
3848
3848
|
name: "list_store_files",
|
|
3849
3849
|
description: "List files/targets available in a job-capable store.",
|
|
@@ -3878,7 +3878,7 @@ function Sn() {
|
|
|
3878
3878
|
},
|
|
3879
3879
|
{
|
|
3880
3880
|
pack: "jobs",
|
|
3881
|
-
availability:
|
|
3881
|
+
availability: U,
|
|
3882
3882
|
definition: {
|
|
3883
3883
|
name: "submit_store_job",
|
|
3884
3884
|
description: "Submit a job to a store: run or refresh a target (e.g. refresh a materialized parquet aggregate). Returns the job id; use await_job to wait for completion.",
|
|
@@ -3911,7 +3911,7 @@ function Sn() {
|
|
|
3911
3911
|
try {
|
|
3912
3912
|
return {
|
|
3913
3913
|
success: !0,
|
|
3914
|
-
message: `Submitted ${
|
|
3914
|
+
message: `Submitted ${W(await n.submitJob(String(e.store_id), String(e.target), e.operation === "refresh" ? "refresh" : "run"))}. Use await_job to wait for completion.`
|
|
3915
3915
|
};
|
|
3916
3916
|
} catch (e) {
|
|
3917
3917
|
return {
|
|
@@ -3923,7 +3923,7 @@ function Sn() {
|
|
|
3923
3923
|
},
|
|
3924
3924
|
{
|
|
3925
3925
|
pack: "jobs",
|
|
3926
|
-
availability:
|
|
3926
|
+
availability: U,
|
|
3927
3927
|
definition: {
|
|
3928
3928
|
name: "get_job_status",
|
|
3929
3929
|
description: "Get the current status of a job by id.",
|
|
@@ -3946,7 +3946,7 @@ function Sn() {
|
|
|
3946
3946
|
let n = t.runtime.jobsStore.getStoreJobs(String(e.store_id)).find((t) => t.job_id === String(e.job_id));
|
|
3947
3947
|
return n ? {
|
|
3948
3948
|
success: !0,
|
|
3949
|
-
message:
|
|
3949
|
+
message: W(n)
|
|
3950
3950
|
} : {
|
|
3951
3951
|
success: !1,
|
|
3952
3952
|
error: `Job ${e.job_id} not found in store ${e.store_id}.`
|
|
@@ -3955,7 +3955,7 @@ function Sn() {
|
|
|
3955
3955
|
},
|
|
3956
3956
|
{
|
|
3957
3957
|
pack: "jobs",
|
|
3958
|
-
availability:
|
|
3958
|
+
availability: U,
|
|
3959
3959
|
definition: {
|
|
3960
3960
|
name: "await_job",
|
|
3961
3961
|
description: "Wait for a job to finish (or time out). Prefer this over polling get_job_status in a loop — one call, returns the final status.",
|
|
@@ -3972,14 +3972,14 @@ function Sn() {
|
|
|
3972
3972
|
},
|
|
3973
3973
|
timeout_seconds: {
|
|
3974
3974
|
type: "number",
|
|
3975
|
-
description: `Max seconds to wait (default ${
|
|
3975
|
+
description: `Max seconds to wait (default ${_n}, max ${vn})`
|
|
3976
3976
|
}
|
|
3977
3977
|
},
|
|
3978
3978
|
required: ["store_id", "job_id"]
|
|
3979
3979
|
}
|
|
3980
3980
|
},
|
|
3981
3981
|
execute: async (e, t) => {
|
|
3982
|
-
let n = t.runtime.jobsStore, r = String(e.store_id), i = String(e.job_id), a = Math.min(
|
|
3982
|
+
let n = t.runtime.jobsStore, r = String(e.store_id), i = String(e.job_id), a = Math.min(vn, Math.max(1, Number(e.timeout_seconds) || _n)), o = Date.now() + a * 1e3, s = () => n.getStoreJobs(r).find((e) => e.job_id === i), c = s();
|
|
3983
3983
|
if (!c) return {
|
|
3984
3984
|
success: !1,
|
|
3985
3985
|
error: `Job ${i} not found in store ${r}.`
|
|
@@ -3987,19 +3987,19 @@ function Sn() {
|
|
|
3987
3987
|
for (; c && c.status === "running" && Date.now() < o;) {
|
|
3988
3988
|
if ((c.pollingState ?? "ok") !== "ok") return {
|
|
3989
3989
|
success: !1,
|
|
3990
|
-
error: `Stopped waiting: ${
|
|
3990
|
+
error: `Stopped waiting: ${W(c)}. ${c.pollingError || ""}`.trim()
|
|
3991
3991
|
};
|
|
3992
|
-
await new Promise((e) => setTimeout(e,
|
|
3992
|
+
await new Promise((e) => setTimeout(e, gn)), c = s();
|
|
3993
3993
|
}
|
|
3994
3994
|
return c ? c.status === "running" ? {
|
|
3995
3995
|
success: !1,
|
|
3996
|
-
error: `Timed out after ${a}s: ${
|
|
3996
|
+
error: `Timed out after ${a}s: ${W(c)}. It continues in the background — call await_job or get_job_status again.`
|
|
3997
3997
|
} : c.status === "success" ? {
|
|
3998
3998
|
success: !0,
|
|
3999
|
-
message: `Job finished: ${
|
|
3999
|
+
message: `Job finished: ${W(c)}`
|
|
4000
4000
|
} : {
|
|
4001
4001
|
success: !1,
|
|
4002
|
-
error: `Job finished unsuccessfully: ${
|
|
4002
|
+
error: `Job finished unsuccessfully: ${W(c)}`
|
|
4003
4003
|
} : {
|
|
4004
4004
|
success: !1,
|
|
4005
4005
|
error: `Job ${i} disappeared while waiting.`
|
|
@@ -4008,7 +4008,7 @@ function Sn() {
|
|
|
4008
4008
|
},
|
|
4009
4009
|
{
|
|
4010
4010
|
pack: "jobs",
|
|
4011
|
-
availability:
|
|
4011
|
+
availability: U,
|
|
4012
4012
|
definition: {
|
|
4013
4013
|
name: "get_store_state",
|
|
4014
4014
|
description: "Get the freshness/state snapshot for a target in a job-capable store (e.g. when its data was last computed).",
|
|
@@ -4058,7 +4058,7 @@ function Sn() {
|
|
|
4058
4058
|
},
|
|
4059
4059
|
{
|
|
4060
4060
|
pack: "jobs",
|
|
4061
|
-
availability:
|
|
4061
|
+
availability: U,
|
|
4062
4062
|
definition: {
|
|
4063
4063
|
name: "list_jobs",
|
|
4064
4064
|
description: "List recent jobs for a store, newest first.",
|
|
@@ -4084,7 +4084,7 @@ function Sn() {
|
|
|
4084
4084
|
message: `No jobs recorded for store ${e.store_id}.`
|
|
4085
4085
|
} : {
|
|
4086
4086
|
success: !0,
|
|
4087
|
-
message: i.map(
|
|
4087
|
+
message: i.map(W).join("\n")
|
|
4088
4088
|
};
|
|
4089
4089
|
}
|
|
4090
4090
|
},
|
|
@@ -4111,7 +4111,7 @@ function Sn() {
|
|
|
4111
4111
|
execute: async (e, t) => {
|
|
4112
4112
|
let n = Math.max(1, Math.floor(Number(e.limit) || 10));
|
|
4113
4113
|
try {
|
|
4114
|
-
let t = await
|
|
4114
|
+
let t = await bn().getQueriesForConnection(String(e.connection));
|
|
4115
4115
|
return t.length ? {
|
|
4116
4116
|
success: !0,
|
|
4117
4117
|
message: t.slice(0, n).map((e) => {
|
|
@@ -4134,7 +4134,7 @@ function Sn() {
|
|
|
4134
4134
|
}
|
|
4135
4135
|
//#endregion
|
|
4136
4136
|
//#region data/tutorial/docTypes.ts
|
|
4137
|
-
var
|
|
4137
|
+
var G = class {
|
|
4138
4138
|
title;
|
|
4139
4139
|
content;
|
|
4140
4140
|
type;
|
|
@@ -4142,43 +4142,43 @@ var U = class {
|
|
|
4142
4142
|
constructor(e, t, n = null, r = null) {
|
|
4143
4143
|
this.title = e, this.content = t, this.type = n, this.data = r || {};
|
|
4144
4144
|
}
|
|
4145
|
-
},
|
|
4145
|
+
}, K = class {
|
|
4146
4146
|
title;
|
|
4147
4147
|
paragraphs;
|
|
4148
4148
|
displayName;
|
|
4149
4149
|
constructor(e, t, n = void 0) {
|
|
4150
4150
|
this.title = e, this.paragraphs = t, this.displayName = n;
|
|
4151
4151
|
}
|
|
4152
|
-
},
|
|
4152
|
+
}, q = class {
|
|
4153
4153
|
title;
|
|
4154
4154
|
articles;
|
|
4155
4155
|
constructor(e, t) {
|
|
4156
4156
|
this.title = e, this.articles = t;
|
|
4157
4157
|
}
|
|
4158
|
-
},
|
|
4159
|
-
new
|
|
4160
|
-
new
|
|
4161
|
-
new
|
|
4162
|
-
new
|
|
4163
|
-
new
|
|
4164
|
-
new
|
|
4165
|
-
new
|
|
4166
|
-
new
|
|
4167
|
-
new
|
|
4168
|
-
new
|
|
4169
|
-
new
|
|
4170
|
-
new
|
|
4171
|
-
new
|
|
4172
|
-
new
|
|
4173
|
-
new
|
|
4174
|
-
], "Dashboard Tutorial"),
|
|
4175
|
-
new
|
|
4176
|
-
new
|
|
4177
|
-
new
|
|
4178
|
-
new
|
|
4179
|
-
new
|
|
4180
|
-
new
|
|
4181
|
-
new
|
|
4158
|
+
}, Sn = new K("Dashboards", [
|
|
4159
|
+
new G("Purpose", "Trilogy is designed as a data consumption language, and one of the fastest ways to expore data is through visualization. The dashboarding system in Trilogy Studio is designed to be streamlined but powerful. The semantic model means that default selections can be tailored and rich."),
|
|
4160
|
+
new G("Purpose", "It aspires to be good out of the box, with progressive disclosure of capability. It is designed to be extensible and feature requests are welcome."),
|
|
4161
|
+
new G("Purpose", "We`ll use the demo model you set up in the first tutorial. If you haven`t done that yet, go back and run through the imports. Make sure you have a model and connection read to go below."),
|
|
4162
|
+
new G("ModelList", "", "model-validator"),
|
|
4163
|
+
new G("ConnectionList", "", "connection-validator"),
|
|
4164
|
+
new G("Purpose", "Now that you have a connection and model set up, lets get charting! Dashboards are accessed through the dashboard icon the left nav, but we'll drop one in right below. The demo model comes with a default example. "),
|
|
4165
|
+
new G("Purpose", "Dashboards are SQL centric - so mostly, you'll just be writing queries. The one special thing on a dashboard is the global where clause. The filter bar at the top defines this; it will be concatenated to the where clause of every query on the dashboard. This is a great way to filter all of your charts at once.\n You can also cross-filter by clicking on any element in the chart. This will be automatically enabled; you can toggle off cross-filtering behavior on a per-chart basis on the header. (Make it only filter otherse, or not filterable at all.)"),
|
|
4166
|
+
new G("Purpose", "In the below dashboard, try the following: <br>-Click on a manufacturer to filter other views. <br>-Go to edit mode and update the markdown on the top. <br>-Add a new chart. <br>-Change the horizontal bar chart to a vertical."),
|
|
4167
|
+
new G("Purpose", "", "dashboard"),
|
|
4168
|
+
new G("Purpose", "When starting a new dashboard, you can either create a blank one or use a template. The templates are designed to be a good starting point for common use cases. You can also import dashboards from other users. Some of the public models will come with default dashboards."),
|
|
4169
|
+
new G("Purpose", "Each chart can have their own query, so it's easy to define metrics in place. For dimensions, it's best to define them in the model so that automatic cross-filtering can be applied; if they are local to a chart, they cannot be applied to other views."),
|
|
4170
|
+
new G("Purpose", "If you have an active LLM configured, the top search bar can be used for natural language filtering with the LLM - use ctrl+shift+enter."),
|
|
4171
|
+
new G("Purpose", "You can only crossfilter if fields exist on all charts. It's best to define anything you want to filter on in the base dashboard file, and avoid aliasing base fields.", "tip"),
|
|
4172
|
+
new G("Purpose", "To fix the formatting of fields, ensure they have the appropriate types and traits in the model. For example, marking a year as a year type will make line charts eligible. See the model tutorial for more details. ", "tip"),
|
|
4173
|
+
new G("Purpose", "For missing features [such as chart types] or other issues, please open an issue on the <a href=\"https://github.com/trilogy-data/trilogy-studio-core/issues\" target=\"_blank\">studio GitHub repo</a>.")
|
|
4174
|
+
], "Dashboard Tutorial"), Cn = new K("Model Tutorial", [
|
|
4175
|
+
new G("Purpose", "The prior two tutorials cover how to query data and create dashboards. Both of those rely on the foundational concept of Trilogy - the model."),
|
|
4176
|
+
new G("Purpose", "A model is simply a collection of editors that have been marked as sources. Any source can be imported by name into another editor, or multiple times under different aliases. Composition of models is how more complex models are built."),
|
|
4177
|
+
new G("Purpose", "The core aspects of models are concepts and datasources. Concepts represent the abstract logical model of the data, while datasources represent physical instantiations of it."),
|
|
4178
|
+
new G("Purpose", "These are loosely coupled, meaning that you can evolve or change your underlying database schema and simply update the datasources to point to the new tables and column names. Critically, queries operate on concepts so do not need to change."),
|
|
4179
|
+
new G("Purpose", "Let's set one up. If you haven't already done the first tutorial, go back and create `my-first-editor` using that. We'll use that as a base."),
|
|
4180
|
+
new G("Purpose", "", "editor-validator"),
|
|
4181
|
+
new G("Tutorial Queries", "bsb", "tutorial-prompts", {
|
|
4182
4182
|
context: "model-tutorial",
|
|
4183
4183
|
editorId: "sql-basic-editor",
|
|
4184
4184
|
prompts: [
|
|
@@ -4212,40 +4212,40 @@ var U = class {
|
|
|
4212
4212
|
}
|
|
4213
4213
|
]
|
|
4214
4214
|
}),
|
|
4215
|
-
new
|
|
4216
|
-
new
|
|
4217
|
-
new
|
|
4218
|
-
new
|
|
4219
|
-
new
|
|
4220
|
-
new
|
|
4221
|
-
new
|
|
4222
|
-
new
|
|
4223
|
-
new
|
|
4224
|
-
new
|
|
4225
|
-
new
|
|
4226
|
-
new
|
|
4227
|
-
new
|
|
4228
|
-
new
|
|
4229
|
-
], "Model Tutorial"),
|
|
4230
|
-
new
|
|
4231
|
-
new
|
|
4232
|
-
new
|
|
4233
|
-
new
|
|
4234
|
-
new
|
|
4235
|
-
new
|
|
4236
|
-
new
|
|
4237
|
-
new
|
|
4238
|
-
new
|
|
4239
|
-
new
|
|
4240
|
-
new
|
|
4241
|
-
new
|
|
4242
|
-
new
|
|
4243
|
-
new
|
|
4244
|
-
new
|
|
4245
|
-
new
|
|
4246
|
-
new
|
|
4247
|
-
new
|
|
4248
|
-
new
|
|
4215
|
+
new G("Purpose", "Review the standard library doc for always available traits.", "tip"),
|
|
4216
|
+
new G("Purpose", "We've defined all the key parts of a model - a key, some properties, and a datasource to fetch. In a real database, you'll usually be working off an actual table. Let's try that out. Follow these steps:\n <ul>\n <li>1. Create a new duckdb connection called 'iris-data' using the connection list below.</li>\n <li>2. Copy the SQL command to create the table.</li>\n <li>3. Click the black doc icon to create a new SQL editor. This will take you to the editor!</li>\n <li>4. Paste in the code and run it.</li>\n <li>5. Click 'set editor as startup script' in the top right.</li>\n <li>6. Come back into this tutorial tab.</li>\n </ul>\n "),
|
|
4217
|
+
new G("Purpose", "It's important to not miss step 5 - that way every time you connect, the table will be automatically populated!.", "tip"),
|
|
4218
|
+
new G("Purpose", "DuckDB WASM CSV import of this file don't appear to work properly on Firefox or Safari as of early 2025. On those browsers, consider download the csv directly and importing it into duckdb using the drag box rather than using the read_csv SQL below.", "warning"),
|
|
4219
|
+
new G("Purpose", "CREATE OR REPLACE TABLE iris_data AS select *, row_number() over () as pk FROM read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv');", "code"),
|
|
4220
|
+
new G("Purpose", "", "connections"),
|
|
4221
|
+
new G("Purpose", "Now we're ready to create a new model. The steps below will again take you out of this tutorial, but you can always come back here by clicking the 'documentation' icon on the left.\n <ul>\n <li>1. Click on the connection you have, and expand the memory database.</li>\n <li>2. Hit refresh if needed. On the row with the iris table, click the database icon to generate a new raw datasource file.</li>\n <li>3. In the open file, change pk to be a \"key\" instead of \"property\" and replace the placeholder tags with pk. (example: property pk.species string;)</li>\n <li>4. In the open file, add a grain clause below the field mapping - grain(pk)</li>\n <li>5. Click the 'set as source' in top right to make this importable by other files.</li>\n <li>6. Rename the file to 'iris'.</li>\n </ul>\n "),
|
|
4222
|
+
new G("Purpose", "The grain clause defines the primary key of your datasource. In this case, we've added a surrogate key called pk. Grains are used to understand when information needs to be grouped, and by what. The property tag is used similarly. A datasource can have no grain, in which case it is a heap and most operations on it will require an extra group to deduplicate.\n ", "tip"),
|
|
4223
|
+
new G("Purpose", "\nkey pk int; # surrogate primary key for the dataset\nproperty pk.sepal_length float;\nproperty pk.sepal_width float;\nproperty pk.petal_length float;\nproperty pk.petal_width float;\nproperty pk.species string;\n\ndatasource iris_data (\n sepal_length:sepal_length,\n sepal_width:sepal_width,\n petal_length:petal_length,\n petal_width:petal_width,\n species:species,\n pk:pk,\n)\ngrain (pk)\naddress iris_data;\n ", "code"),
|
|
4224
|
+
new G("Purpose", "Make sure your file looks like the above. Now create a new trilogy editor by clicking the orange document icon by your connection. Copy the below code and run it! \n "),
|
|
4225
|
+
new G("Purpose", "import iris;\nselect\n species,\n avg(petal_length) as avg_petal_length,\n avg(petal_width) as avg_petal_width\n;\n ", "code"),
|
|
4226
|
+
new G("Purpose", "If you get any errors, double check names and that you have set the iris file as a source. If you get a \"no such table\" error, make sure you have run the startup script to create the table. \n ", "tip"),
|
|
4227
|
+
new G("Purpose", "Make sure your file looks like the above. Now create a new trilogy editor by clicking the orange document icon by your connection. Copy the below code and run it! \n "),
|
|
4228
|
+
new G("Further Reading", "Go to the model page under the trilogy reference section.\n ")
|
|
4229
|
+
], "Model Tutorial"), wn = new K("Welcome", [
|
|
4230
|
+
new G("Purpose", "Trilogy is a semantic-powered SQL - familiar SQL syntax that operates on a typed semantic layer, instead of tables. It should feel like SQL, but faster and more expressive. You can read more about the language in the Trilogy reference section."),
|
|
4231
|
+
new G("Purpose", "Trilogy Studio is intended as an accessible demonstration of core language features, which would otherwise have a high barrier to experimentation. It is open source and feature requests and contributions are welcome."),
|
|
4232
|
+
new G("Purpose", "In the rest of this tutorial, we will walk you through a demo-model - based on the TPC-H dataset - to get started. This will give you a feel for how Trilogy works and how you can use it to query data."),
|
|
4233
|
+
new G("Purpose", "A \"model\" is a collection of associated Trilogy files that define a specific semantic layer. Models can be imported and exported, and are the primary way to share and reuse Trilogy code. In Trilogy Studio, a model will always be associated with a connection, representing a database backend. You can assign the same model to multiple connections for reuse. ", "tip"),
|
|
4234
|
+
new G("Purpose", "Trilogy Studio has a built in model \"store\", containing a set of public models hosted on github. The demo model is one of those options. Let's get started! Search below for the demo-model, and click the import button to add it. You want to import it to a duckdb connection. Select \"New DuckDB\" and keep the default connection name."),
|
|
4235
|
+
new G("Purpose", "<PLACEHOLDER>", "community-models"),
|
|
4236
|
+
new G("ModelList", "", "model-validator"),
|
|
4237
|
+
new G("Purpose", "Now that you have the model imported and associated with a connection, we just need to click the plug icon to connect to it."),
|
|
4238
|
+
new G("Purpose", "If you had set up the connection first, you could click the connection name to toggle details, and click the model dropdown and select the \"demo-model\".", "tip"),
|
|
4239
|
+
new G("Purpose", "You can also run normal SQL against a connection, using a 'sql' typed editor. This is a good way to debug or test queries that you are not sure how to write in Trilogy, or to manage databases.", "tip"),
|
|
4240
|
+
new G("ConnectionList", "", "connections"),
|
|
4241
|
+
new G("ConnectionList", "", "connection-validator"),
|
|
4242
|
+
new G("Purpose", "When the above indicator is green, you are good to move on! Note that normally, you will manage connections through the connection panel available on the sidebar - this component is just embedded here as well for the tutorial.", "tip"),
|
|
4243
|
+
new G("Purpose", "Now that you have a connection and model set up, you can start querying in an editor. Editors are grouped by their storage, then by connection. `Local` represents local storage in your browser. [Other storage, like Github, will be available in the future] Your imported model will have populated some default editors, but let's start with a new one. In the sidebar, click the \"...\" menu next to the demo-model-connection to expand its options, then create a new Trilogy editor named \"my-first-editor\"."),
|
|
4244
|
+
new G("Purpose", "", "editors"),
|
|
4245
|
+
new G("Purpose", "", "editor-validator"),
|
|
4246
|
+
new G("Purpose", "When the above indicator is green, you are good to move on! Note again that normally editors are managed through the sidebar. You will also see that some editors are marked as sources - these are importable by other editors on the model. As these often are metadata only, you can use the toggle at the top to hide these if desired. Any editor can be made a source by toggling a button in the editor.", "tip"),
|
|
4247
|
+
new G("Purpose", "Editors are your interactive SQL experience. They let you write Trilogy, validate it, run it, and look at the results. Running (ctrl-enter) will either execute the full editor or the highlighted chunk of code (with any imports). In the below tutorial editor, we'll run through a few queries and validate your results."),
|
|
4248
|
+
new G("Tutorial Queries", "bsb", "tutorial-prompts", {
|
|
4249
4249
|
context: "sql-tutorial",
|
|
4250
4250
|
editorId: "sql-basic-editor",
|
|
4251
4251
|
prompts: [
|
|
@@ -4272,20 +4272,20 @@ var U = class {
|
|
|
4272
4272
|
}
|
|
4273
4273
|
]
|
|
4274
4274
|
}),
|
|
4275
|
-
new
|
|
4276
|
-
new
|
|
4277
|
-
new
|
|
4278
|
-
], "Welcome and Query Tutorial"),
|
|
4279
|
-
new
|
|
4280
|
-
new
|
|
4281
|
-
new
|
|
4282
|
-
new
|
|
4283
|
-
new
|
|
4284
|
-
new
|
|
4285
|
-
new
|
|
4286
|
-
new
|
|
4287
|
-
new
|
|
4288
|
-
new
|
|
4275
|
+
new G("Purpose", "Thats the basics! You know how to run queries in an editor, how to import a model, and how to connect to a datasource.", "tip"),
|
|
4276
|
+
new G("Purpose", "For the last query, could you figure out how to visualize the data? Editors support inline visualization for rapid introspection; to create more complicated visuals, check out the dashboarding tutorial up next.", "tip"),
|
|
4277
|
+
new G("Purpose", "Feel free to explore from here. A good place to start is clicking on the editors icon in the left nav and exploring the pre-populated demo editors, which show more functionality. Alternatively, poke more around the docs!")
|
|
4278
|
+
], "Welcome and Query Tutorial"), Tn = new K("LLM Tutorial", [
|
|
4279
|
+
new G("Connections", "LLMs can optionally be used to enhance the studio experience. The LLM screen is accessible on the left-hand nav and provides some basic validation that your LLM will work. LLMs is not required, but we aim to make the best use of them when available."),
|
|
4280
|
+
new G("Connections", "Gemini has had some of the best results, but any lightweight fast model works well to build scaffolding quickly. You will need to QA answers.", "tip"),
|
|
4281
|
+
new G("Connections", "Core LLM use cases are -query generation from natural language, dashboard generation from natural language, dashboard filtering with natural language, Feature requests welcome! To add an LLM, you will need an appropriate API key. Currently, we support OpenAI, Anthropic, and Google, but adding more is straightforward."),
|
|
4282
|
+
new G("Connections", "To improve LLM performance, the first place to start is your model, as concept definitions are provided as primary context. Adding more specific concepts and - especially - descriptions to fields will help the model understand better. For complex calculations, consider pre-defining them so they can be easily selected by the model."),
|
|
4283
|
+
new G("Connections", "Make sure to update the model to your favorite! Do this by selecting one from the dropdown and explicitly clicking update model - after first setting up an LLM. We don't know what models are available until you first connect.", "tip"),
|
|
4284
|
+
new G("Managing Connections", "You can view current LLM connections below. Only one LLM connection can be the default at a time. You can set a default by clicking the star icon. The default LLM will be used for all LLM functionality. Try setting one up, confirm you can connect, select an appropriate model, and save. Then head to an editor or a dashboard to try it out!"),
|
|
4285
|
+
new G("LLMList", "", "llm-connections"),
|
|
4286
|
+
new G("LLMList", "The LLM experience is not tested on mobile.", "warning"),
|
|
4287
|
+
new G("Purpose", "", "editor-validator"),
|
|
4288
|
+
new G("Tutorial Queries", "There should be an edito here! Make sure you've completed the first tutorial!", "tutorial-prompts", {
|
|
4289
4289
|
context: "sql-tutorial",
|
|
4290
4290
|
editorId: "sql-basic-editor",
|
|
4291
4291
|
prompts: [{
|
|
@@ -4302,12 +4302,12 @@ var U = class {
|
|
|
4302
4302
|
validationFn: (e) => e.data?.some((e) => e.order_customer_nation_region_headquarters === "HQ4" && Math.round(e.total_revenue) === 451328736) ?? !1
|
|
4303
4303
|
}]
|
|
4304
4304
|
}),
|
|
4305
|
-
new
|
|
4306
|
-
]),
|
|
4307
|
-
new
|
|
4308
|
-
new
|
|
4309
|
-
new
|
|
4310
|
-
new
|
|
4305
|
+
new G("Connections", "LLMs operate on the imported concept context. Make sure you have a datasource selected on a dashboard, or add imports to an editor before typing your comment. Ctrl-shift-enter is the default LLM activation shortcut; typically you will highlight text and then run this.", "tip")
|
|
4306
|
+
]), J = new K("Functions", [
|
|
4307
|
+
new G("Trilogy Built-in Functions", "Trilogy provides a comprehensive set of built-in functions that closely map to standard SQL functions. These functions enable data transformation, aggregation, and manipulation within your queries. They are categorized into aggregate, date/time, string, numeric, collection, and utility functions."),
|
|
4308
|
+
new G("Function Usage & Concepts", "Each function definition includes its input requirements and output characteristics. Key information includes:\n <ul>\n <li><b>Input Types:</b> The allowed data types for each argument.</li>\n <li><b>Output Type:</b> The data type of the value returned by the function.</li>\n <li><b>Output Purpose:</b> Describes the conceptual role of the output.\n <ul>\n <li><b>PROPERTY:</b> Represents an attribute derived from existing data, typically associated with the grain of the input(s). Example: <code>UPPER(name)</code>.</li>\n <li><b>METRIC:</b> Represents a calculated value that summarizes data across rows, respecting the query's grouping. Example: <code>SUM(revenue)</code>.</li>\n <li><b>KEY:</b> Represents an identifier or a dimension used for joining or grouping, often changing the grain of the data. Example: <code>UNNEST(tags)</code>.</li>\n <li><b>CONSTANT:</b> Represents a fixed value, independent of the input data rows. Example: <code>CURRENT_DATE()</code>.</li>\n </ul>\n </li>\n </ul>"),
|
|
4309
|
+
new G("Aggregate Functions", "Aggregate functions perform calculations across a set of input rows and return a single summary value for each group."),
|
|
4310
|
+
new G("SUM", "Computes the sum of non-null numeric values within each group. Returns NULL if the group is empty or contains only NULL values.", "function", { function: {
|
|
4311
4311
|
inputTypes: [
|
|
4312
4312
|
"INTEGER",
|
|
4313
4313
|
"FLOAT",
|
|
@@ -4318,19 +4318,19 @@ var U = class {
|
|
|
4318
4318
|
outputPurpose: "METRIC",
|
|
4319
4319
|
example: "SELECT SUM(order_total) AS total_revenue BY customer_id;"
|
|
4320
4320
|
} }),
|
|
4321
|
-
new
|
|
4321
|
+
new G("COUNT", "Counts the number of non-null input rows within each group. Takes a single argument of any type.", "function", { function: {
|
|
4322
4322
|
inputTypes: ["Any"],
|
|
4323
4323
|
outputType: "INTEGER",
|
|
4324
4324
|
outputPurpose: "METRIC",
|
|
4325
4325
|
example: "SELECT COUNT(order_id) AS num_orders BY product_category;"
|
|
4326
4326
|
} }),
|
|
4327
|
-
new
|
|
4327
|
+
new G("COUNT_DISTINCT", "Counts the number of distinct non-null input values within each group. Takes a single argument of any type.", "function", { function: {
|
|
4328
4328
|
inputTypes: ["Any"],
|
|
4329
4329
|
outputType: "INTEGER",
|
|
4330
4330
|
outputPurpose: "METRIC",
|
|
4331
4331
|
example: "SELECT COUNT_DISTINCT(customer_id) AS unique_customers BY month;"
|
|
4332
4332
|
} }),
|
|
4333
|
-
new
|
|
4333
|
+
new G("MAX", "Returns the maximum non-null value across all input rows within each group. Comparison follows standard data type rules.", "function", { function: {
|
|
4334
4334
|
inputTypes: [
|
|
4335
4335
|
"INTEGER",
|
|
4336
4336
|
"FLOAT",
|
|
@@ -4345,7 +4345,7 @@ var U = class {
|
|
|
4345
4345
|
outputPurpose: "METRIC",
|
|
4346
4346
|
example: "SELECT MAX(order_date) AS latest_order_date BY customer_id;"
|
|
4347
4347
|
} }),
|
|
4348
|
-
new
|
|
4348
|
+
new G("MIN", "Returns the minimum non-null value across all input rows within each group. Comparison follows standard data type rules.", "function", { function: {
|
|
4349
4349
|
inputTypes: [
|
|
4350
4350
|
"INTEGER",
|
|
4351
4351
|
"FLOAT",
|
|
@@ -4359,7 +4359,7 @@ var U = class {
|
|
|
4359
4359
|
outputPurpose: "METRIC",
|
|
4360
4360
|
example: "SELECT MIN(order_date) AS first_order_date BY customer_id;"
|
|
4361
4361
|
} }),
|
|
4362
|
-
new
|
|
4362
|
+
new G("AVG", "Computes the average (arithmetic mean) of non-null numeric values within each group. Returns NULL if the group is empty or contains only NULL values. The result is typically a FLOAT or NUMBER.", "function", { function: {
|
|
4363
4363
|
inputTypes: [
|
|
4364
4364
|
"INTEGER",
|
|
4365
4365
|
"FLOAT",
|
|
@@ -4370,20 +4370,20 @@ var U = class {
|
|
|
4370
4370
|
outputPurpose: "METRIC",
|
|
4371
4371
|
example: "SELECT AVG(product_price) AS avg_price BY category;"
|
|
4372
4372
|
} }),
|
|
4373
|
-
new
|
|
4374
|
-
new
|
|
4373
|
+
new G("Date/Time Functions", "Functions for manipulating and extracting information from date and time values."),
|
|
4374
|
+
new G("CURRENT_DATE", "Returns the current date when the query begins execution. This value remains constant throughout the query.", "function", { function: {
|
|
4375
4375
|
inputTypes: [],
|
|
4376
4376
|
outputType: "DATE",
|
|
4377
4377
|
outputPurpose: "CONSTANT",
|
|
4378
4378
|
example: "SELECT order_id WHERE order_date = CURRENT_DATE();"
|
|
4379
4379
|
} }),
|
|
4380
|
-
new
|
|
4380
|
+
new G("CURRENT_DATETIME", "Returns the current date and time (timestamp) when the query begins execution. This value remains constant throughout the query.", "function", { function: {
|
|
4381
4381
|
inputTypes: [],
|
|
4382
4382
|
outputType: "DATETIME",
|
|
4383
4383
|
outputPurpose: "CONSTANT",
|
|
4384
4384
|
example: "SELECT event_id, CURRENT_DATETIME() AS processing_time;"
|
|
4385
4385
|
} }),
|
|
4386
|
-
new
|
|
4386
|
+
new G("DATE", "Extracts the date part from a timestamp, datetime, or string, or casts a value to the DATE type. If the input is already a DATE, it is returned unchanged.", "function", { function: {
|
|
4387
4387
|
inputTypes: [
|
|
4388
4388
|
"DATE",
|
|
4389
4389
|
"TIMESTAMP",
|
|
@@ -4394,7 +4394,7 @@ var U = class {
|
|
|
4394
4394
|
outputPurpose: "PROPERTY",
|
|
4395
4395
|
example: "SELECT DATE(user_signup_timestamp) AS signup_date;"
|
|
4396
4396
|
} }),
|
|
4397
|
-
new
|
|
4397
|
+
new G("DATETIME", "Casts the input value (date, timestamp, or string) to the DATETIME type.", "function", { function: {
|
|
4398
4398
|
inputTypes: [
|
|
4399
4399
|
"DATE",
|
|
4400
4400
|
"TIMESTAMP",
|
|
@@ -4405,7 +4405,7 @@ var U = class {
|
|
|
4405
4405
|
outputPurpose: "PROPERTY",
|
|
4406
4406
|
example: "SELECT DATETIME(event_date) AS event_datetime;"
|
|
4407
4407
|
} }),
|
|
4408
|
-
new
|
|
4408
|
+
new G("TIMESTAMP", "Casts the input value (date, datetime, or string) to the TIMESTAMP type.", "function", { function: {
|
|
4409
4409
|
inputTypes: [
|
|
4410
4410
|
"DATE",
|
|
4411
4411
|
"TIMESTAMP",
|
|
@@ -4416,19 +4416,19 @@ var U = class {
|
|
|
4416
4416
|
outputPurpose: "PROPERTY",
|
|
4417
4417
|
example: "SELECT TIMESTAMP(log_entry_string) AS log_timestamp;"
|
|
4418
4418
|
} }),
|
|
4419
|
-
new
|
|
4419
|
+
new G("DATE_TRUNCATE", "Truncates a date/time value to the specified precision ('year', 'month', 'day', etc.). Returns a value of the same type as the input (or DATE if truncating to day or higher from a timestamp/datetime).", "function", { function: {
|
|
4420
4420
|
inputTypes: ["DATE/TIMESTAMP/DATETIME/STRING", "DATE_PART"],
|
|
4421
4421
|
outputType: "Same as input type (usually DATE or DATETIME/TIMESTAMP)",
|
|
4422
4422
|
outputPurpose: "PROPERTY",
|
|
4423
4423
|
example: "SELECT DATE_TRUNCATE(created_at, month) AS creation_month;"
|
|
4424
4424
|
} }),
|
|
4425
|
-
new
|
|
4425
|
+
new G("DATE_PART", "Extracts a specific subfield (e.g., 'year', 'month', 'day', 'hour', 'dow' for day of week) from a date/time value as an integer.", "function", { function: {
|
|
4426
4426
|
inputTypes: ["DATE/TIMESTAMP/DATETIME/STRING", "DATE_PART"],
|
|
4427
4427
|
outputType: "INTEGER (with potential trait)",
|
|
4428
4428
|
outputPurpose: "PROPERTY",
|
|
4429
4429
|
example: "SELECT DATE_PART(order_timestamp, hour) AS hour_of_order;"
|
|
4430
4430
|
} }),
|
|
4431
|
-
new
|
|
4431
|
+
new G("DATE_ADD", "Adds a specified integer interval to a date/time value. The unit of the interval is specified by the date part argument (e.g., day, month, year).", "function", { function: {
|
|
4432
4432
|
inputTypes: [
|
|
4433
4433
|
"DATE/TIMESTAMP/DATETIME/STRING",
|
|
4434
4434
|
"DATE_PART",
|
|
@@ -4438,7 +4438,7 @@ var U = class {
|
|
|
4438
4438
|
outputPurpose: "PROPERTY",
|
|
4439
4439
|
example: "SELECT DATE_ADD(start_date, day, 14) AS end_date;"
|
|
4440
4440
|
} }),
|
|
4441
|
-
new
|
|
4441
|
+
new G("DATE_SUB", "Subtracts a specified integer interval from a date/time value. The unit of the interval is specified by the date part argument.", "function", { function: {
|
|
4442
4442
|
inputTypes: [
|
|
4443
4443
|
"DATE/TIMESTAMP/DATETIME/STRING",
|
|
4444
4444
|
"DATE_PART",
|
|
@@ -4448,7 +4448,7 @@ var U = class {
|
|
|
4448
4448
|
outputPurpose: "PROPERTY",
|
|
4449
4449
|
example: "SELECT DATE_SUB(event_timestamp, hour, 3) AS reminder_time;"
|
|
4450
4450
|
} }),
|
|
4451
|
-
new
|
|
4451
|
+
new G("DATE_DIFF", "Calculates the integer difference between two date/time values, expressed in the units specified by the date part argument.", "function", { function: {
|
|
4452
4452
|
inputTypes: [
|
|
4453
4453
|
"DATE/TIMESTAMP/DATETIME/STRING",
|
|
4454
4454
|
"DATE/TIMESTAMP/DATETIME/STRING",
|
|
@@ -4458,13 +4458,13 @@ var U = class {
|
|
|
4458
4458
|
outputPurpose: "PROPERTY",
|
|
4459
4459
|
example: "SELECT DATE_DIFF(end_date, start_date, day) AS duration_days;"
|
|
4460
4460
|
} }),
|
|
4461
|
-
new
|
|
4461
|
+
new G("UNIX_TO_TIMESTAMP", "Converts an integer Unix epoch timestamp (seconds since 1970-01-01 00:00:00 UTC) to a TIMESTAMP value.", "function", { function: {
|
|
4462
4462
|
inputTypes: ["INTEGER"],
|
|
4463
4463
|
outputType: "TIMESTAMP",
|
|
4464
4464
|
outputPurpose: "PROPERTY",
|
|
4465
4465
|
example: "SELECT UNIX_TO_TIMESTAMP(log_time_seconds) AS event_time;"
|
|
4466
4466
|
} }),
|
|
4467
|
-
new
|
|
4467
|
+
new G("SECOND", "Extracts the second component (0-59) from a date/time value as an integer.", "function", { function: {
|
|
4468
4468
|
inputTypes: [
|
|
4469
4469
|
"DATE",
|
|
4470
4470
|
"TIMESTAMP",
|
|
@@ -4475,7 +4475,7 @@ var U = class {
|
|
|
4475
4475
|
outputPurpose: "PROPERTY",
|
|
4476
4476
|
example: "SELECT SECOND(event_time) AS event_second;"
|
|
4477
4477
|
} }),
|
|
4478
|
-
new
|
|
4478
|
+
new G("MINUTE", "Extracts the minute component (0-59) from a date/time value as an integer.", "function", { function: {
|
|
4479
4479
|
inputTypes: [
|
|
4480
4480
|
"DATE",
|
|
4481
4481
|
"TIMESTAMP",
|
|
@@ -4486,7 +4486,7 @@ var U = class {
|
|
|
4486
4486
|
outputPurpose: "PROPERTY",
|
|
4487
4487
|
example: "SELECT MINUTE(event_time) AS event_minute;"
|
|
4488
4488
|
} }),
|
|
4489
|
-
new
|
|
4489
|
+
new G("HOUR", "Extracts the hour component (0-23) from a date/time value as an integer.", "function", { function: {
|
|
4490
4490
|
inputTypes: [
|
|
4491
4491
|
"DATE",
|
|
4492
4492
|
"TIMESTAMP",
|
|
@@ -4497,7 +4497,7 @@ var U = class {
|
|
|
4497
4497
|
outputPurpose: "PROPERTY",
|
|
4498
4498
|
example: "SELECT HOUR(event_time) AS event_hour;"
|
|
4499
4499
|
} }),
|
|
4500
|
-
new
|
|
4500
|
+
new G("DAY", "Extracts the day of the month component (1-31) from a date/time value as an integer.", "function", { function: {
|
|
4501
4501
|
inputTypes: [
|
|
4502
4502
|
"DATE",
|
|
4503
4503
|
"TIMESTAMP",
|
|
@@ -4508,7 +4508,7 @@ var U = class {
|
|
|
4508
4508
|
outputPurpose: "PROPERTY",
|
|
4509
4509
|
example: "SELECT DAY(event_date) AS event_day_of_month;"
|
|
4510
4510
|
} }),
|
|
4511
|
-
new
|
|
4511
|
+
new G("WEEK", "Extracts the ISO week number (1-53) from a date/time value as an integer.", "function", { function: {
|
|
4512
4512
|
inputTypes: [
|
|
4513
4513
|
"DATE",
|
|
4514
4514
|
"TIMESTAMP",
|
|
@@ -4519,7 +4519,7 @@ var U = class {
|
|
|
4519
4519
|
outputPurpose: "PROPERTY",
|
|
4520
4520
|
example: "SELECT WEEK(order_date) AS order_week_iso;"
|
|
4521
4521
|
} }),
|
|
4522
|
-
new
|
|
4522
|
+
new G("MONTH", "Extracts the month component (1-12) from a date/time value as an integer.", "function", { function: {
|
|
4523
4523
|
inputTypes: [
|
|
4524
4524
|
"DATE",
|
|
4525
4525
|
"TIMESTAMP",
|
|
@@ -4530,7 +4530,7 @@ var U = class {
|
|
|
4530
4530
|
outputPurpose: "PROPERTY",
|
|
4531
4531
|
example: "SELECT MONTH(signup_date) AS signup_month;"
|
|
4532
4532
|
} }),
|
|
4533
|
-
new
|
|
4533
|
+
new G("QUARTER", "Extracts the quarter component (1-4) from a date/time value as an integer.", "function", { function: {
|
|
4534
4534
|
inputTypes: [
|
|
4535
4535
|
"DATE",
|
|
4536
4536
|
"TIMESTAMP",
|
|
@@ -4541,7 +4541,7 @@ var U = class {
|
|
|
4541
4541
|
outputPurpose: "PROPERTY",
|
|
4542
4542
|
example: "SELECT QUARTER(fiscal_date) AS fiscal_quarter;"
|
|
4543
4543
|
} }),
|
|
4544
|
-
new
|
|
4544
|
+
new G("YEAR", "Extracts the year component from a date/time value as an integer.", "function", { function: {
|
|
4545
4545
|
inputTypes: [
|
|
4546
4546
|
"DATE",
|
|
4547
4547
|
"TIMESTAMP",
|
|
@@ -4552,7 +4552,7 @@ var U = class {
|
|
|
4552
4552
|
outputPurpose: "PROPERTY",
|
|
4553
4553
|
example: "SELECT YEAR(birth_date) AS birth_year;"
|
|
4554
4554
|
} }),
|
|
4555
|
-
new
|
|
4555
|
+
new G("DAY_OF_WEEK", "Extracts the day of the week as an integer (e.g., Sunday=0 or 1, Monday=1 or 2, depending on locale/implementation - typically Sunday=0).", "function", { function: {
|
|
4556
4556
|
inputTypes: [
|
|
4557
4557
|
"DATE",
|
|
4558
4558
|
"TIMESTAMP",
|
|
@@ -4563,38 +4563,38 @@ var U = class {
|
|
|
4563
4563
|
outputPurpose: "PROPERTY",
|
|
4564
4564
|
example: "SELECT DAY_OF_WEEK(transaction_date) AS transaction_dow;"
|
|
4565
4565
|
} }),
|
|
4566
|
-
new
|
|
4567
|
-
new
|
|
4566
|
+
new G("String Functions", "Functions for manipulating text strings."),
|
|
4567
|
+
new G("SPLIT", "Splits the first string argument into an array of strings, using the second string argument as the delimiter.", "function", { function: {
|
|
4568
4568
|
inputTypes: ["STRING", "STRING"],
|
|
4569
4569
|
outputType: "LIST<STRING>",
|
|
4570
4570
|
outputPurpose: "PROPERTY",
|
|
4571
4571
|
example: "SELECT SPLIT(product_tags, \";\") AS tag_list;"
|
|
4572
4572
|
} }),
|
|
4573
|
-
new
|
|
4573
|
+
new G("UPPER", "Converts the input string to all uppercase letters according to locale rules.", "function", { function: {
|
|
4574
4574
|
inputTypes: ["STRING"],
|
|
4575
4575
|
outputType: "STRING",
|
|
4576
4576
|
outputPurpose: "PROPERTY",
|
|
4577
4577
|
example: "SELECT UPPER(country_code) AS normalized_country;"
|
|
4578
4578
|
} }),
|
|
4579
|
-
new
|
|
4579
|
+
new G("LOWER", "Converts the input string to all lowercase letters according to locale rules.", "function", { function: {
|
|
4580
4580
|
inputTypes: ["STRING"],
|
|
4581
4581
|
outputType: "STRING",
|
|
4582
4582
|
outputPurpose: "PROPERTY",
|
|
4583
4583
|
example: "SELECT LOWER(email_address) AS normalized_email;"
|
|
4584
4584
|
} }),
|
|
4585
|
-
new
|
|
4585
|
+
new G("CONTAINS", "Returns true if the first string contains the second string as a substring, false otherwise. Case-sensitive.", "function", { function: {
|
|
4586
4586
|
inputTypes: ["STRING", "STRING"],
|
|
4587
4587
|
outputType: "BOOL",
|
|
4588
4588
|
outputPurpose: "PROPERTY",
|
|
4589
4589
|
example: "SELECT description WHERE CONTAINS(description, \"urgent\");"
|
|
4590
4590
|
} }),
|
|
4591
|
-
new
|
|
4591
|
+
new G("STRPOS", "Returns the starting position (1-based index) of the first occurrence of the second string within the first string. Returns 0 if the substring is not found. Case-sensitive.", "function", { function: {
|
|
4592
4592
|
inputTypes: ["STRING", "STRING"],
|
|
4593
4593
|
outputType: "INTEGER",
|
|
4594
4594
|
outputPurpose: "PROPERTY",
|
|
4595
4595
|
example: "SELECT STRPOS(url, \"?\") AS query_start_pos;"
|
|
4596
4596
|
} }),
|
|
4597
|
-
new
|
|
4597
|
+
new G("SUBSTRING", "Extracts a substring from the first argument, starting at the 1-based index specified by the second argument, for a length specified by the third argument.", "function", { function: {
|
|
4598
4598
|
inputTypes: [
|
|
4599
4599
|
"STRING",
|
|
4600
4600
|
"INTEGER",
|
|
@@ -4604,13 +4604,13 @@ var U = class {
|
|
|
4604
4604
|
outputPurpose: "PROPERTY",
|
|
4605
4605
|
example: "SELECT SUBSTRING(phone_number, 2, 3) AS area_code; # Assuming format like +1XXX..."
|
|
4606
4606
|
} }),
|
|
4607
|
-
new
|
|
4607
|
+
new G("CONCAT", "Concatenates two or more string arguments together. NULL arguments are typically ignored or result in NULL depending on implementation.", "function", { function: {
|
|
4608
4608
|
inputTypes: ["STRING", "..."],
|
|
4609
4609
|
outputType: "STRING",
|
|
4610
4610
|
outputPurpose: "PROPERTY",
|
|
4611
4611
|
example: "SELECT CONCAT(first_name, \" \", last_name) AS full_name;"
|
|
4612
4612
|
} }),
|
|
4613
|
-
new
|
|
4613
|
+
new G("LENGTH", "Returns the length of a string (number of characters), the number of elements in an array/list, or the number of key-value pairs in a map.", "function", { function: {
|
|
4614
4614
|
inputTypes: [
|
|
4615
4615
|
"STRING",
|
|
4616
4616
|
"ARRAY",
|
|
@@ -4621,20 +4621,20 @@ var U = class {
|
|
|
4621
4621
|
outputPurpose: "PROPERTY",
|
|
4622
4622
|
example: "SELECT LENGTH(comment) AS comment_length;"
|
|
4623
4623
|
} }),
|
|
4624
|
-
new
|
|
4624
|
+
new G("LIKE", "Performs case-sensitive SQL LIKE pattern matching. Use '%' for any sequence of zero or more characters and '_' for any single character.", "function", { function: {
|
|
4625
4625
|
inputTypes: ["STRING", "STRING"],
|
|
4626
4626
|
outputType: "BOOL",
|
|
4627
4627
|
outputPurpose: "PROPERTY",
|
|
4628
4628
|
example: "SELECT product_name WHERE LIKE(product_name, \"Model %\");"
|
|
4629
4629
|
} }),
|
|
4630
|
-
new
|
|
4630
|
+
new G("ILIKE", "Performs case-insensitive SQL LIKE pattern matching (similar to Postgres ILIKE). Use '%' and '_' wildcards as with LIKE.", "function", { function: {
|
|
4631
4631
|
inputTypes: ["STRING", "STRING"],
|
|
4632
4632
|
outputType: "BOOL",
|
|
4633
4633
|
outputPurpose: "PROPERTY",
|
|
4634
4634
|
example: "SELECT email WHERE ILIKE(email, \"%@example.com\");"
|
|
4635
4635
|
} }),
|
|
4636
|
-
new
|
|
4637
|
-
new
|
|
4636
|
+
new G("Numeric Functions", "Functions for performing mathematical operations on numbers."),
|
|
4637
|
+
new G("ABS", "Computes the absolute value of a numeric argument.", "function", { function: {
|
|
4638
4638
|
inputTypes: [
|
|
4639
4639
|
"INTEGER",
|
|
4640
4640
|
"FLOAT",
|
|
@@ -4644,13 +4644,13 @@ var U = class {
|
|
|
4644
4644
|
outputPurpose: "PROPERTY",
|
|
4645
4645
|
example: "SELECT ABS(price_change) AS abs_price_change;"
|
|
4646
4646
|
} }),
|
|
4647
|
-
new
|
|
4647
|
+
new G("ROUND", "Rounds the first numeric argument to the number of decimal places specified by the second integer argument. If the second argument is omitted or zero, rounds to the nearest integer.", "function", { function: {
|
|
4648
4648
|
inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "INTEGER"],
|
|
4649
4649
|
outputType: "Same as first input type",
|
|
4650
4650
|
outputPurpose: "PROPERTY",
|
|
4651
4651
|
example: "SELECT ROUND(average_rating, 1) AS rounded_rating;"
|
|
4652
4652
|
} }),
|
|
4653
|
-
new
|
|
4653
|
+
new G("SQRT", "Calculates the square root of a non-negative numeric argument. The result is typically a FLOAT or NUMBER.", "function", { function: {
|
|
4654
4654
|
inputTypes: [
|
|
4655
4655
|
"INTEGER",
|
|
4656
4656
|
"FLOAT",
|
|
@@ -4661,80 +4661,80 @@ var U = class {
|
|
|
4661
4661
|
outputPurpose: "PROPERTY",
|
|
4662
4662
|
example: "SELECT SQRT(variance) AS standard_deviation;"
|
|
4663
4663
|
} }),
|
|
4664
|
-
new
|
|
4664
|
+
new G("RANDOM", "Returns a random floating-point value between 0.0 (inclusive) and 1.0 (exclusive) at the grain of the provided concept. If no concept is provided, all rows have the same value.", "function", { function: {
|
|
4665
4665
|
inputTypes: ["ANY"],
|
|
4666
4666
|
outputType: "FLOAT",
|
|
4667
4667
|
outputPurpose: "PROPERTY",
|
|
4668
4668
|
example: "SELECT order_id, RANDOM(order_id) AS random_sort_key ORDER BY random_sort_key;"
|
|
4669
4669
|
} }),
|
|
4670
|
-
new
|
|
4670
|
+
new G("ADD", "Performs arithmetic addition on two or more numeric arguments.", "function", { function: {
|
|
4671
4671
|
inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "..."],
|
|
4672
4672
|
outputType: "Appropriate numeric type based on inputs",
|
|
4673
4673
|
outputPurpose: "PROPERTY",
|
|
4674
4674
|
example: "SELECT ADD(subtotal, tax, shipping_fee) AS grand_total;"
|
|
4675
4675
|
} }),
|
|
4676
|
-
new
|
|
4676
|
+
new G("SUBTRACT", "Performs arithmetic subtraction. If two arguments, subtracts the second from the first. If more, behavior might vary (e.g., subtract all subsequent from first).", "function", { function: {
|
|
4677
4677
|
inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "..."],
|
|
4678
4678
|
outputType: "Appropriate numeric type based on inputs",
|
|
4679
4679
|
outputPurpose: "PROPERTY",
|
|
4680
4680
|
example: "SELECT SUBTRACT(total_revenue, total_cost) AS profit;"
|
|
4681
4681
|
} }),
|
|
4682
|
-
new
|
|
4682
|
+
new G("MULTIPLY", "Performs arithmetic multiplication on two or more numeric arguments.", "function", { function: {
|
|
4683
4683
|
inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "..."],
|
|
4684
4684
|
outputType: "Appropriate numeric type based on inputs",
|
|
4685
4685
|
outputPurpose: "PROPERTY",
|
|
4686
4686
|
example: "SELECT MULTIPLY(quantity, unit_price) AS line_item_total;"
|
|
4687
4687
|
} }),
|
|
4688
|
-
new
|
|
4688
|
+
new G("DIVIDE", "Performs arithmetic division. If two arguments, divides the first by the second. Division by zero typically results in an error or NULL/Infinity. Result type is often FLOAT or NUMBER.", "function", { function: {
|
|
4689
4689
|
inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "..."],
|
|
4690
4690
|
outputType: "FLOAT or NUMBER (usually)",
|
|
4691
4691
|
outputPurpose: "PROPERTY",
|
|
4692
4692
|
example: "SELECT DIVIDE(total_sales, number_of_units) AS price_per_unit;"
|
|
4693
4693
|
} }),
|
|
4694
|
-
new
|
|
4694
|
+
new G("MOD", "Computes the modulo (remainder) of the first numeric argument divided by the second integer argument.", "function", { function: {
|
|
4695
4695
|
inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "INTEGER"],
|
|
4696
4696
|
outputType: "INTEGER",
|
|
4697
4697
|
outputPurpose: "PROPERTY",
|
|
4698
4698
|
example: "SELECT user_id WHERE MOD(user_id, 2) = 0; # Even user IDs"
|
|
4699
4699
|
} }),
|
|
4700
|
-
new
|
|
4701
|
-
new
|
|
4700
|
+
new G("Utility Functions", "General-purpose functions for data manipulation, control flow, and type handling."),
|
|
4701
|
+
new G("ALIAS", "Assigns a new name (alias) to an expression or concept without changing its value, type, or purpose. Useful for clarity or renaming in SELECT clauses.", "function", { function: {
|
|
4702
4702
|
inputTypes: ["Any"],
|
|
4703
4703
|
outputType: "Same as input",
|
|
4704
4704
|
outputPurpose: "Same as input",
|
|
4705
4705
|
example: "SELECT user.profile.first_name ALIAS fname;"
|
|
4706
4706
|
} }),
|
|
4707
|
-
new
|
|
4707
|
+
new G("PARENTHETICAL", "Used for explicit grouping of expressions, primarily to control the order of operations. Does not change the value, type, or purpose of the enclosed expression.", "function", { function: {
|
|
4708
4708
|
inputTypes: ["Any"],
|
|
4709
4709
|
outputType: "Same as input",
|
|
4710
4710
|
outputPurpose: "Same as input",
|
|
4711
4711
|
example: "SELECT (price + tax) * quantity AS total;"
|
|
4712
4712
|
} }),
|
|
4713
|
-
new
|
|
4713
|
+
new G("COALESCE", "Returns the first non-null value from its list of arguments. All arguments must be of compatible types. Returns NULL if all arguments are NULL.", "function", { function: {
|
|
4714
4714
|
inputTypes: ["Any (compatible types)", "..."],
|
|
4715
4715
|
outputType: "Same as input types (common compatible type)",
|
|
4716
4716
|
outputPurpose: "PROPERTY",
|
|
4717
4717
|
example: "SELECT COALESCE(nickname, first_name, \"Valued Customer\") AS display_name;"
|
|
4718
4718
|
} }),
|
|
4719
|
-
new
|
|
4719
|
+
new G("CAST", "Explicitly converts an expression to a target data type specified as the second argument (using a DataType enum value or potentially a string literal).", "function", { function: {
|
|
4720
4720
|
inputTypes: ["Any", "DataType"],
|
|
4721
4721
|
outputType: "Specified by second argument (DataType)",
|
|
4722
4722
|
outputPurpose: "PROPERTY",
|
|
4723
4723
|
example: "SELECT CAST(order_id_string AS INTEGER) AS order_id_num;"
|
|
4724
4724
|
} }),
|
|
4725
|
-
new
|
|
4725
|
+
new G("IS_NULL", "Returns true if the input argument is NULL, false otherwise.", "function", { function: {
|
|
4726
4726
|
inputTypes: ["Any"],
|
|
4727
4727
|
outputType: "BOOL",
|
|
4728
4728
|
outputPurpose: "PROPERTY",
|
|
4729
4729
|
example: "SELECT order_id WHERE IS_NULL(shipping_address);"
|
|
4730
4730
|
} }),
|
|
4731
|
-
new
|
|
4731
|
+
new G("BOOL", "Attempts to convert the input argument to a boolean value. Behavior depends on the input type (e.g., 0 -> false, non-zero -> true; empty string -> false, non-empty -> true).", "function", { function: {
|
|
4732
4732
|
inputTypes: ["Any"],
|
|
4733
4733
|
outputType: "BOOL",
|
|
4734
4734
|
outputPurpose: "PROPERTY",
|
|
4735
4735
|
example: "SELECT product_id, BOOL(is_active_flag) AS is_active;"
|
|
4736
4736
|
} }),
|
|
4737
|
-
new
|
|
4737
|
+
new G("CASE", "Evaluates a series of conditions and returns a result based on the first condition that evaluates to true. Syntax: `CASE WHEN condition1 THEN result1 [WHEN condition2 THEN result2 ...] [ELSE default_result] END`. All result expressions must have compatible types.", "function", { function: {
|
|
4738
4738
|
inputTypes: [
|
|
4739
4739
|
"BOOL",
|
|
4740
4740
|
"Any",
|
|
@@ -4744,44 +4744,44 @@ var U = class {
|
|
|
4744
4744
|
outputPurpose: "PROPERTY",
|
|
4745
4745
|
example: "SELECT CASE WHEN score > 90 THEN \"A\" WHEN score > 80 THEN \"B\" ELSE \"C\" END AS grade;"
|
|
4746
4746
|
} }),
|
|
4747
|
-
new
|
|
4747
|
+
new G("CONSTANT", "Represents a literal value provided as an argument. The type and purpose are determined by the literal itself.", "function", { function: {
|
|
4748
4748
|
inputTypes: ["Literal (String, Number, Bool, etc.)"],
|
|
4749
4749
|
outputType: "Type of the literal",
|
|
4750
4750
|
outputPurpose: "CONSTANT",
|
|
4751
4751
|
example: "SELECT \"Default Category\" AS category, product_id;"
|
|
4752
4752
|
} }),
|
|
4753
|
-
new
|
|
4753
|
+
new G("CUSTOM", "Represents a user-defined or engine-specific function not built into the core Trilogy language. Input types, output type, and behavior depend on the specific custom function implementation.", "function", { function: {
|
|
4754
4754
|
inputTypes: ["Depends on custom function"],
|
|
4755
4755
|
outputType: "Depends on custom function",
|
|
4756
4756
|
outputPurpose: "Usually PROPERTY or METRIC",
|
|
4757
4757
|
example: "SELECT my_custom_scoring_function(user_data) AS score;"
|
|
4758
4758
|
} }),
|
|
4759
|
-
new
|
|
4760
|
-
new
|
|
4759
|
+
new G("Collection Functions", "Functions for working with complex data types like arrays/lists, maps, and structs."),
|
|
4760
|
+
new G("UNNEST", "Expands an array or list into a set of rows, where each row contains one element from the array/list. This typically changes the grain of the query, hence the KEY purpose.", "function", { function: {
|
|
4761
4761
|
inputTypes: ["ARRAY", "LIST"],
|
|
4762
4762
|
outputType: "Element type of the input collection",
|
|
4763
4763
|
outputPurpose: "KEY",
|
|
4764
4764
|
example: "SELECT order_id, UNNEST(product_ids) AS product_id;"
|
|
4765
4765
|
} }),
|
|
4766
|
-
new
|
|
4766
|
+
new G("INDEX_ACCESS", "Retrieves an element from an array or list using its zero-based integer index. Accessing an index outside the bounds may result in NULL or an error.", "function", { function: {
|
|
4767
4767
|
inputTypes: ["ARRAY/LIST", "INTEGER"],
|
|
4768
4768
|
outputType: "Element type of the input collection",
|
|
4769
4769
|
outputPurpose: "PROPERTY",
|
|
4770
4770
|
example: "SELECT INDEX_ACCESS(sensor_readings, 0) AS first_reading;"
|
|
4771
4771
|
} }),
|
|
4772
|
-
new
|
|
4772
|
+
new G("MAP_ACCESS", "Retrieves the value associated with a specified key from a map. The key type must match the map's key type (usually STRING or INTEGER). Returns NULL if the key is not found.", "function", { function: {
|
|
4773
4773
|
inputTypes: ["MAP", "INTEGER/STRING"],
|
|
4774
4774
|
outputType: "Value type of the map",
|
|
4775
4775
|
outputPurpose: "PROPERTY",
|
|
4776
4776
|
example: "SELECT MAP_ACCESS(user_properties, \"preferred_language\") AS language;"
|
|
4777
4777
|
} }),
|
|
4778
|
-
new
|
|
4778
|
+
new G("ATTR_ACCESS", "Retrieves the value of a named field (attribute) from a struct. The second argument is the attribute name (string). Accessing a non-existent attribute may result in NULL or an error.", "function", { function: {
|
|
4779
4779
|
inputTypes: ["STRUCT", "STRING"],
|
|
4780
4780
|
outputType: "Type of the accessed attribute",
|
|
4781
4781
|
outputPurpose: "PROPERTY",
|
|
4782
4782
|
example: "SELECT ATTR_ACCESS(address_struct, \"zip_code\") AS zip;"
|
|
4783
4783
|
} }),
|
|
4784
|
-
new
|
|
4784
|
+
new G("STRUCT", "Constructs a struct (similar to a record or object) dynamically from pairs of arguments: field name (string literal) followed by the field value.", "function", { function: {
|
|
4785
4785
|
inputTypes: [
|
|
4786
4786
|
"STRING (name)",
|
|
4787
4787
|
"Any (value)",
|
|
@@ -4791,20 +4791,20 @@ var U = class {
|
|
|
4791
4791
|
outputPurpose: "PROPERTY",
|
|
4792
4792
|
example: "SELECT STRUCT(\"id\", user_id, \"email\", user_email) AS user_info;"
|
|
4793
4793
|
} }),
|
|
4794
|
-
new
|
|
4794
|
+
new G("ARRAY", "Constructs an array containing the provided arguments. All arguments should ideally be of a compatible type; behavior/support depends on target DB. The output type depends on the input argument types.", "function", { function: {
|
|
4795
4795
|
inputTypes: ["Any (compatible types)", "..."],
|
|
4796
4796
|
outputType: "ARRAY of common compatible type of arguments",
|
|
4797
4797
|
outputPurpose: "PROPERTY",
|
|
4798
4798
|
example: "SELECT ARRAY(primary_color, secondary_color) AS palette;"
|
|
4799
4799
|
} }),
|
|
4800
|
-
new
|
|
4801
|
-
new
|
|
4800
|
+
new G("Set/Relational Functions", "Functions operating on sets or relations."),
|
|
4801
|
+
new G("UNION", "Combines the results of two or more compatible inputs (concepts or queries) into a single result set. Behaves like UNION ALL (does not remove duplicates). The inputs must have compatible data types.", "function", { function: {
|
|
4802
4802
|
inputTypes: ["Any (compatible concepts/queries)", "..."],
|
|
4803
4803
|
outputType: "Depends on inputs (structure of the combined set)",
|
|
4804
4804
|
outputPurpose: "KEY",
|
|
4805
4805
|
example: "SELECT * FROM UNION(active_customers, recently_lapsed_customers);"
|
|
4806
4806
|
} }),
|
|
4807
|
-
new
|
|
4807
|
+
new G("GROUP", "Explicitly specifies the grouping keys for an expression, often used with aggregate functions when a different grain is needed than the main query `BY` clause. The first argument is the expression to aggregate/group, subsequent arguments are the keys. Syntax is distinct in that is is group key by <over clause>, similar to a group by.", "function", { function: {
|
|
4808
4808
|
inputTypes: [
|
|
4809
4809
|
"Any (expression)",
|
|
4810
4810
|
"Any (key)",
|
|
@@ -4814,233 +4814,244 @@ var U = class {
|
|
|
4814
4814
|
outputPurpose: "Depends on first argument",
|
|
4815
4815
|
example: "SELECT SUM(revenue) AS total_revenue, GROUP SUM(revenue) BY product_category AS revenue_by_category, customer_id;"
|
|
4816
4816
|
} })
|
|
4817
|
-
]),
|
|
4818
|
-
if (
|
|
4819
|
-
var Dn =
|
|
4820
|
-
|
|
4817
|
+
]), En = J.paragraphs.findIndex((e) => e.title === "Aggregate Functions"), Y = J.paragraphs.findIndex((e) => e.title === "Date/Time Functions");
|
|
4818
|
+
if (En < 0 || Y < 0) throw Error("Built-in function documentation sections are incomplete");
|
|
4819
|
+
var Dn = J.paragraphs[1], On = new K("Aggregate Functions", [
|
|
4820
|
+
J.paragraphs[En],
|
|
4821
4821
|
Dn,
|
|
4822
|
-
...
|
|
4823
|
-
]), kn = new
|
|
4824
|
-
new
|
|
4822
|
+
...J.paragraphs.slice(En + 1, Y)
|
|
4823
|
+
]), kn = new K("Functions", [
|
|
4824
|
+
new G("Trilogy Built-in Functions", "Trilogy provides built-in scalar functions for transforming and manipulating individual values. They are categorized into date/time, string, numeric, collection, and utility functions. Aggregate functions, which summarize groups of rows, are documented separately."),
|
|
4825
4825
|
Dn,
|
|
4826
|
-
...
|
|
4827
|
-
]), An = new
|
|
4828
|
-
new
|
|
4829
|
-
new
|
|
4830
|
-
new
|
|
4826
|
+
...J.paragraphs.slice(Y)
|
|
4827
|
+
]), An = new K("Window Functions", [
|
|
4828
|
+
new G("Trilogy Built-in Functions", "Window functions are a set of built-in functions in the Trilogy query language that allow you to perform calculations across a set of rows related to the current row. These functions are essential for data analysis, reporting, and complex aggregations."),
|
|
4829
|
+
new G("Function Usage & Concepts", "Each function definition includes its input requirements and output characteristics. Key information includes:\n <ul>\n <li><b>Input Types:</b> The allowed data types for each argument.</li>\n <li><b>Output Type:</b> The data type of the value returned by the function.</li>\n <li><b>Output Purpose:</b> Describes the conceptual role of the output.\n <ul>\n <li><b>PROPERTY:</b> Represents an attribute derived from existing data, typically associated with the grain of the input(s). Example: <code>UPPER(name)</code>.</li>\n <li><b>METRIC:</b> Represents a calculated value that summarizes data across rows, respecting the query's grouping. Example: <code>SUM(revenue)</code>.</li>\n <li><b>KEY:</b> Represents an identifier or a dimension used for joining or grouping, often changing the grain of the data. Example: <code>UNNEST(tags)</code>.</li>\n <li><b>CONSTANT:</b> Represents a fixed value, independent of the input data rows. Example: <code>CURRENT_DATE()</code>.</li>\n </ul>\n </li>\n </ul>"),
|
|
4830
|
+
new G("RANK", "Computes the rank of each row within its partition, with ties receiving the same rank. The rank starts at 1 for the first row in each partition.", "function", { function: {
|
|
4831
4831
|
inputTypes: ["ANY"],
|
|
4832
4832
|
outputType: "INT",
|
|
4833
4833
|
outputPurpose: "PROPERTY",
|
|
4834
4834
|
example: "rank repo.license by license_repo_count desc;"
|
|
4835
4835
|
} }),
|
|
4836
|
-
new
|
|
4836
|
+
new G("LAG", "Accesses data from a previous row in the same partition without changing the partitioning or ordering of the result set. The offset can be specified to retrieve data from a specific number of rows back.", "function", { function: {
|
|
4837
4837
|
inputTypes: ["ANY", "INT"],
|
|
4838
4838
|
outputType: "ANY",
|
|
4839
4839
|
outputPurpose: "PROPERTY",
|
|
4840
4840
|
example: "lag repo.license by license_repo_count desc;"
|
|
4841
4841
|
} }),
|
|
4842
|
-
new
|
|
4842
|
+
new G("LEAD", "Accesses data from a subsequent row in the same partition without changing the partitioning or ordering of the result set. The offset can be specified to retrieve data from a specific number of rows ahead.", "function", { function: {
|
|
4843
4843
|
inputTypes: ["ANY", "INT"],
|
|
4844
4844
|
outputType: "ANY",
|
|
4845
4845
|
outputPurpose: "PROPERTY",
|
|
4846
4846
|
example: "lead repo.license by license_repo_count desc;"
|
|
4847
4847
|
} })
|
|
4848
|
-
]), jn = new
|
|
4849
|
-
new
|
|
4850
|
-
new
|
|
4851
|
-
new
|
|
4852
|
-
new
|
|
4853
|
-
new
|
|
4854
|
-
new
|
|
4855
|
-
new
|
|
4856
|
-
new
|
|
4857
|
-
new
|
|
4858
|
-
new
|
|
4859
|
-
new
|
|
4860
|
-
new
|
|
4861
|
-
new
|
|
4862
|
-
new
|
|
4863
|
-
new
|
|
4864
|
-
new
|
|
4865
|
-
new
|
|
4866
|
-
new
|
|
4867
|
-
new
|
|
4868
|
-
new
|
|
4869
|
-
new
|
|
4870
|
-
new
|
|
4871
|
-
], "Modeling"), Mn = new
|
|
4872
|
-
new
|
|
4873
|
-
new
|
|
4874
|
-
new
|
|
4875
|
-
new
|
|
4876
|
-
new
|
|
4877
|
-
new
|
|
4878
|
-
new
|
|
4879
|
-
new
|
|
4880
|
-
new
|
|
4881
|
-
new
|
|
4882
|
-
new
|
|
4883
|
-
new
|
|
4848
|
+
]), jn = new K("Modeling", [
|
|
4849
|
+
new G("What is a Model?", "Models are the heart of Trilogy's semantic layer. They provide a structured, logical representation of data that can evolve independently of the underlying physical model. A model ist just a collection of Trilogy .preql files available for import. In Trilogy Studio, these are editors that have been marked as `sources`. These sources define the concepts, properties, and data retrieval logic for your domain."),
|
|
4850
|
+
new G("Purpose and Benefits", "Models enable several key benefits:\n <ul>\n <li><strong>Abstraction:</strong> Define data logically using concepts (like 'customer' or 'order') independent of physical table names or structures.</li>\n <li><strong>Reusability:</strong> Define concepts and logic once in a model source and import them into any number of queries, reports, or other models.</li>\n <li><strong>Maintainability:</strong> If your database schema changes (e.g., table or column rename), you only need to update the corresponding datasource mapping in the model. Queries relying on the logical concepts remain unchanged.</li>\n <li><strong>Consistency:</strong> Ensures everyone querying the data uses the same definitions, metrics, and relationships.</li>\n <li><strong>Governance:</strong> Provides a centralized place to manage data definitions, access rules (via datasources), and metadata (types, traits).</li>\n </ul>", null, null),
|
|
4851
|
+
new G("Core Components", "Models have two core components: Concepts and Datasources. These are defined within Trilogy editors marked as model sources.", "section"),
|
|
4852
|
+
new G("Concepts", "Concepts keys represent the core entities or logical keys in your data model (e.g., `customer.id`, `product.sku`, `order.id`). Each concept typically has a unique key that identifies individual instances (like `customer.id`). Concepts form the nouns of your data language. They are defined implicitly through properties and datasources associated with them.", "subsection"),
|
|
4853
|
+
new G("Properties", "Concept properties are dependent concepts or characteristics associated with a key (e.g., `customer.name`, `order.order_date`, `product.price`). They define context/attributes of keys.\n Properties have defined data types (like `string`, `number`, `datetime`) and can be further annotated with metadata traits (like `::usd` or `::us_state_short`).\n New properties can be added to existing concepts using the `property` keyword:", "subsection"),
|
|
4854
|
+
new G("Properties Example", "property order.customer.nation.region.id.headquarters string;\n// This adds a 'headquarters' property of type string\n// under the key `order.customer.nation.region.id.`\nproperty <order.customer.nation.region.id>.headquarters string;\n// this does the same, but with explicit syntax\n// that can be useful when you have a property of multiple keys\nproperty <order.customer.nation.region.id,date.year>.point_in_time_hq string;\n", "code"),
|
|
4855
|
+
new G("Types and Traits", "Trilogy has a core type system (e.g., `string`, `number`, `list<T>`, `struct<...>`) that maps to database types.\n Additionally, a metadata-based trait system allows for richer semantic meaning. Traits are appended using `::`.\n Example: `auto states <- ['NY', 'CA', 'TX']::list<string::us_state_short>;` defines a list where each element is a string representing a US state abbreviation. Traits can influence validation, default visualizations (like maps for geo traits), or downstream system behavior.", "subsection", null),
|
|
4856
|
+
new G("Types and Traits Example", "import std.geography; // Import traits like us_state_short\n\n// Define a list of strings with a specific trait\nauto states <- ['NY', 'CA', 'TX']::list<string::us_state_short>;\n\n// Define a price with a currency trait\nauto item_price <- 19.99::number::usd;", "code", null),
|
|
4857
|
+
new G("Datasources", "Datasources are the bridge between the logical model of concept keys and properties and database tables. They define *how* and *where* to fetch the data for specific properties.\n A datasource maps logical properties to physical columns or calculations and specifies the source.\n Crucially, they also define the `grain` - the level of detail or the set of keys that uniquely identify a row returned by the datasource.", "subsection", null),
|
|
4858
|
+
new G("Datasource Example (Database)", "import lineitem; // Assume lineitem provides base concepts like order.id etc.\n\n// Add a new property first (as shown before)\nproperty order.customer.nation.region.id.headquarters string;\n\n// Define a datasource to populate the new property\ndatasource region_headquarters (\n region_id: order.customer.nation.region.id, // Map logical concept key to source column\n headquarters: order.customer.nation.region.headquarters, // Map logical property to source column\n)\ngrain (order.customer.nation.region.id) // Define the unique key for this data\naddress tbl_headquarters;\n''';", "code", null),
|
|
4859
|
+
new G("Datasource (Inline Example Description)", "Datasources can also use raw queries as a source, useful for constants, mappings, or small datasets directly within the model definition.", null, null),
|
|
4860
|
+
new G("Datasource Example (Inline Query)", "datasource region_headquarters_inline (\n region_id: order.customer.nation.region.id,\n headquarters: order.customer.nation.region.headquarters,\n)\ngrain (order.customer.nation.region.id)\nquery '''\n-- Using an inline query (syntax might vary based on backend)\nselect 1 as region_id, 'HQ1' as headquarters\nunion all\nselect 2 as region_id, 'HQ2' as headquarters\nunion all\nselect 3 as region_id, 'HQ3' as headquarters\nunion all\nselect 4 as region_id, 'HQ4' as headquarters\n''';", "code", null),
|
|
4861
|
+
new G("Using and Managing Models", "How to leverage and maintain your models.", "section", null),
|
|
4862
|
+
new G("Imports", "Models are composed using the `import` statement. You can import entire model source editors by their name (or alias if specified in the model config). This makes the concepts, properties, and datasources defined in that source available in the current editor.\n The standard library (`std`) is always available for import, providing common functions and traits.", "subsection", null),
|
|
4863
|
+
new G("Imports Example", "// Import concepts/properties defined in the 'lineitem' editor source\nimport lineitem;\n\n// Import geography traits from the standard library\nimport std.geography;\n\n// Import concepts from 'customer_details' editor source, aliasing it to 'cust'\nimport customer_details as cust;\n\n// Now you can use concepts like:\n// lineitem.order.id\n// std.geography.us_state_short\n// cust.customer.address", "code", null),
|
|
4864
|
+
new G("Constants", "The simplest form of a concept is a constant, defined using `const`. Constants do not require a datasource as their value is fixed directly in the definition. They are useful for defining fixed values, parameters, or variables within scripts or queries.", "subsection", null),
|
|
4865
|
+
new G("Constants Example", "const pi <- 3.14;\nconst company_founding_year <- 2023;\n\nselect pi * 2 as circumference_ratio;", "code", null),
|
|
4866
|
+
new G("Creating Model Sources", "Any Trilogy editor can be designated as a source file for a model. This is typically done through the Trilogy UI or configuration. Once an editor is part of a model, its definitions become importable.", "subsection", null),
|
|
4867
|
+
new G("Tip: Generating Models", "You can quickly create a basic model source from a database table. When browsing tables in a connection, use the \"Create model from table\" action. This generates an editor with properties mapped to columns and a basic datasource.", "tip", null),
|
|
4868
|
+
new G("Model Parsing and Validation", "Models are parsed and validated to ensure correctness and build the metadata necessary for queries and tooling. This typically happens automatically when a query referencing the model is run. You can also trigger parsing manually (e.g., via a 'Parse Model' button in the UI). Parsing checks syntax, type consistency, and resolves imports. Errors found during parsing will be reported, often linking back to the specific editor and line number causing the issue.", "subsection", null),
|
|
4869
|
+
new G("Standard Library", "Trilogy includes a standard library, typically available under the `std` namespace (e.g., `import std;` or `import std.math;`). This library provides common functions, operators, and useful metadata traits (like geo-location, currency, etc.).", "subsection", null),
|
|
4870
|
+
new G("Tip: Standard Library", "Review the documentation for the standard library (`std`) to discover always-available functions and traits you can use in your models and queries.", "tip", null)
|
|
4871
|
+
], "Modeling"), Mn = new q("Trilogy Reference", [
|
|
4872
|
+
new K("Overview/Goals", [
|
|
4873
|
+
new G("Introduction", "Trilogy is a new, SQL-derived language that compiles to SQL. Trilogy Studio provides you with both native Trilogy and SQL editors, allowing you to query your data in a way that is more expressive and easier to maintain than traditional SQL."),
|
|
4874
|
+
new G("Introduction", "Trilogy aspires to simplify hard parts of SQL, keep and enhance the good, incentivize the best, and add modern reusability, type-checking, and ergonomics. It should be approachable to query by someone that knows only SQL, and easy to model for someone familiar with SQL or Python."),
|
|
4875
|
+
new G("Iterative SQL Development", "To capture the iterative SQL development loop, Trilogy directly embeds a lightweight metadata definition syntax, allowing you to query and evolve the semantic layer in the same tool - and even in the same query session. This definition language (semantic layer, ERD, etc.) provides static typing/enforcement, and handles joins [1-1, 1-many, many-to-1], aggregations, filtering, and nulls automatically."),
|
|
4876
|
+
new G("Trilogy Query Structure", "Let's take a look. Standard Trilogy reads like SQL SELECT, without the FROM or GROUP BY clauses."),
|
|
4877
|
+
new G("Trilogy Query Example", "WHERE\n store_sales.date.year=2001 \n AND store_sales.date.month_of_year=1 \n AND store_sales.item.current_price > 1.2 * avg(store_sales.item.current_price) BY store_sales.item.category\nSELECT\n store_sales.customer.state,\n count(store_sales.customer.id) AS customer_count\nHAVING\n customer_count > 10\nORDER BY\n customer_count ASC NULLS FIRST,\n store_sales.customer.state ASC NULLS FIRST\nLIMIT 100;", "code"),
|
|
4878
|
+
new G("How Trilogy Differs from SQL", "Those aren't the only clauses to be gone - Trilogy removes the need for joins - and even tables - via the metadata layer. Information about how to join tables is encoded once and used automatically if required. Definitions look like creating a SQL table, but columns are bound to an additionally abstracted concept label, not raw datatypes. This enables Trilogy to dynamically traverse joins when needed."),
|
|
4879
|
+
new G("How Trilogy Differs from SQL", "key order_id int;\nkey store_id int;\nproperty store_id.store_name string;\n\ndatasource orders (\n order_id: order_id,\n store_id: store_id\n)\ngrain (order_id)\naddress tbl_orders;\n\ndatasource stores (\n store_id: store_id\n store_name: store_name\n)\ngrain (store_id)\naddress tbl_stores;\n", "code"),
|
|
4880
|
+
new G("The Value of Trilogy", "But writing out that datasource is work - a table can be queried quickly because someone else set it up. Trilogy provides the same experience through model imports. It's expected that datasources will be written once, and reused many times. Trilogy can be shared and imported through .preql files using a python-like import syntax."),
|
|
4881
|
+
new G("The Value of Trilogy", "Complex models spanning dozens of tables can be imported with a single line."),
|
|
4882
|
+
new G("Trilogy Equivalent", "import concepts.sales AS sales;\nSELECT\n sales.territory_name,\n sales.sub_total,\n sales.total_taxes,\n sales.total_sales\nORDER BY\n sales.territory_name DESC;", "code"),
|
|
4883
|
+
new G("How Trilogy Works", "Explore the rest of the reference documentation for more details on how Trilogy works and explore these concepts in more detail.")
|
|
4884
4884
|
]),
|
|
4885
|
-
new
|
|
4886
|
-
new
|
|
4887
|
-
new
|
|
4888
|
-
new
|
|
4889
|
-
new
|
|
4890
|
-
new
|
|
4891
|
-
new
|
|
4892
|
-
new
|
|
4893
|
-
new
|
|
4885
|
+
new K("Code", [new G("How Trilogy Works", "Studio and dependent code is open-source under the MIT license. The code is available on GitHub at the following locations:\n <li><a href='https://github.com/trilogy-data/trilogy-studio-core' target='_blank'>trilogy-studio-core</a> - the core code for the studio, including the language server and query editor.</li>\n <li><a href='https://github.com/trilogy-data/pytrilogy' target='_blank'>pytrilogy</a> - reference implementation of Trilogy language parsing/execution.</li>"), new G("How Trilogy Works", "Stars appreciated, contributions welcome! If you find a bug, please file an issue on the relevant repository.\n ")]),
|
|
4886
|
+
new K("Querying Data", [
|
|
4887
|
+
new G("SELECT Without FROM, JOIN, or GROUP BY", "In Trilogy, you write queries without explicitly specifying tables, joins, or grouping."),
|
|
4888
|
+
new G("Example Query", "SELECT\n product_line,\n revenue,\n revenue_from_top_customers,\n revenue_from_top_customers / revenue AS revenue_from_top_customers_pct\nWHERE\n product_line IN ('widgets', 'doodads');", "code"),
|
|
4889
|
+
new G("SELECT Without FROM, JOIN, or GROUP BY", "Queries begin with a SELECT, and can include WHERE, HAVING, ORDER BY, and LIMIT clauses. Trilogy automatically handles joins and grouping based on the defined model."),
|
|
4890
|
+
new G("SELECT Without FROM, JOIN, or GROUP BY", "Like SQL, the where clause filters results *before* the selection, and the having *after*. The order by clause sorts the results, and the limit clause restricts the number of rows returned."),
|
|
4891
|
+
new G("SELECT Without FROM, JOIN, or GROUP BY", "This query would restrict data to red products in Massachsusetts, then select the product name and aggregate total sales within the filter state, further filter to only those with total sales greater than 1000, ordering by total sales in descending order, and limiting the results to 10 rows."),
|
|
4892
|
+
new G("Example Query", "WHERE product_name like '%red%' and sales.state = 'MA' \nSELECT \n product_name, \n sum(sales) AS total_sales \nHAVING total_sales > 1000 \nORDER BY \n total_sales DESC \nLIMIT 10;", "code"),
|
|
4893
|
+
new G("SELECT Without FROM, JOIN, or GROUP BY", "All concepts and outputs must be explicitly named, using AS to bind any transformation. The having clause and order clause can only reference fields in the select list, while the where clause can reference any field in the model.")
|
|
4894
4894
|
]),
|
|
4895
|
-
new
|
|
4896
|
-
new
|
|
4897
|
-
new
|
|
4898
|
-
new
|
|
4899
|
-
new
|
|
4900
|
-
new
|
|
4901
|
-
new
|
|
4902
|
-
new
|
|
4903
|
-
new
|
|
4904
|
-
new
|
|
4905
|
-
new
|
|
4906
|
-
new
|
|
4907
|
-
new
|
|
4908
|
-
new
|
|
4909
|
-
new
|
|
4910
|
-
new
|
|
4911
|
-
new
|
|
4912
|
-
new
|
|
4913
|
-
new
|
|
4914
|
-
new
|
|
4915
|
-
new
|
|
4916
|
-
new
|
|
4917
|
-
new
|
|
4895
|
+
new K("Syntax", [
|
|
4896
|
+
new G("Trilogy Syntax", "Trilogy scripts contain semantic declarations and queries. Statements end with a semicolon, comments begin with #, and only SELECT statements return rows. Trilogy resembles SQL, but concepts replace table columns and the semantic model resolves datasources and joins."),
|
|
4897
|
+
new G("Basic SELECT", "where orders.status = 'complete'\nselect\n orders.customer.name,\n sum(orders.revenue) as total_revenue\nhaving sum(orders.revenue) > 1000\norder by sum(orders.revenue) desc\nlimit 100;", "code"),
|
|
4898
|
+
new G("SELECT Structure", "WHERE filters source rows before aggregation or window evaluation. SELECT defines the output and its grain. HAVING filters the projected result, including aggregates and windows. ORDER BY and LIMIT are applied last. WHERE is conventionally written before SELECT."),
|
|
4899
|
+
new G("SELECT Structure", "with <name> as?\nwhere <condition>?\nselect\n <expression> [as <alias>], ...\n <subset|union join clauses>?\nhaving <condition>?\norder by <expression> [asc|desc]?\nlimit <count>?;", "code"),
|
|
4900
|
+
new G("Fields and Aliases", "Use the full path for imported concepts, such as orders.customer.name. Existing concepts can be selected directly. Every new expression should be named with AS. An output alias is a label, not a reusable concept: do not reference it inside calculations or WHERE, HAVING, or ORDER BY. Define an AUTO or METRIC when an expression must be reused."),
|
|
4901
|
+
new G("Aliases", "select\n orders.customer.id,\n sum(orders.revenue) as total_revenue,\n sum(orders.revenue) / count(orders.id) as average_order_value\norder by sum(orders.revenue) desc;", "code"),
|
|
4902
|
+
new G("Automatic Grouping", "Trilogy has no GROUP BY clause. Aggregates automatically group to the non-aggregated dimensions selected by the query. Override one aggregate with BY, use BY * for a grand total, or place BY ROLLUP, BY CUBE, or BY GROUPING SETS after the select list for multi-level output."),
|
|
4903
|
+
new G("Explicit Aggregate Grain", "select\n orders.customer.region,\n sum(orders.revenue) as regional_revenue,\n sum(orders.revenue) by * as company_revenue,\n sum(orders.revenue) / (sum(orders.revenue) by *) as revenue_share;", "code"),
|
|
4904
|
+
new G("Inline Filters", "Use expression ? condition to filter the input to one expression without filtering the whole query. Parenthesize arithmetic on the left side. This is especially useful for conditional aggregates."),
|
|
4905
|
+
new G("Inline Filter", "select\n orders.customer.region,\n sum(orders.revenue) as all_revenue,\n sum(orders.revenue ? orders.status = 'complete') as completed_revenue;", "code"),
|
|
4906
|
+
new G("Imports", "IMPORT makes a reusable model available under an alias. Imported relationships are exposed through dot paths, so importing a fact model normally also provides access to its dimensions."),
|
|
4907
|
+
new G("Import", "import orders as orders;", "code"),
|
|
4908
|
+
new G("Parameters", "PARAMETER declares a runtime value. A parameter without a default must be supplied by the caller; a default makes it optional."),
|
|
4909
|
+
new G("Parameter", "parameter minimum_revenue numeric default 1000;\n\nwhere orders.revenue >= minimum_revenue\nselect orders.id, orders.revenue;", "code"),
|
|
4910
|
+
new G("Named Rowsets", "Prefix a SELECT with WITH name AS to create a reusable rowset. Its outputs are namespaced beneath that name and can be referenced by later statements."),
|
|
4911
|
+
new G("Named Rowset", "with large_orders as\nwhere orders.revenue > 1000\nselect\n orders.id,\n orders.customer.id,\n orders.revenue;\n\nselect\n large_orders.customer.id,\n sum(large_orders.revenue) as total_revenue;", "code"),
|
|
4912
|
+
new G("Scoped Joins", "Use SUBSET JOIN or UNION JOIN inside a SELECT when concepts from separate models share a value domain. SUBSET declares that the left domain is contained by the right; UNION preserves values exclusive to either side. Joins declare relationships and do not imply row filtering—use explicit predicates to restrict rows. Join every key in a composite grain."),
|
|
4913
|
+
new G("Scoped Join", "select\n actuals.account_id,\n sum(actuals.amount) as actual_amount,\n sum(budget.amount) as budget_amount\nunion join actuals.account_id = budget.account_id;", "code"),
|
|
4914
|
+
new G("Expressions", "Cast with ::type, call every function with parentheses, and use unquoted date parts such as date_part(orders.created_at, year). Logical AND binds more tightly than OR. The inline-filter operator ? binds to a primary expression, so write (revenue - cost) ? condition when filtering arithmetic."),
|
|
4915
|
+
new G("How Trilogy Differs from SQL", "Do not write FROM, GROUP BY, DISTINCT, SELECT *, SQL subqueries, or SQL-style set operators. Use model paths instead of FROM/JOIN, automatic grain instead of GROUP BY, count_distinct for non-key distinct counts, named rowsets instead of subqueries, and union(...) to stack rows. The -- prefix hides a selected field; it is not a comment.", "warning"),
|
|
4916
|
+
new G("Reusable Definitions", "KEY, PROPERTY, AUTO, and METRIC declare reusable concepts; their expressions are evaluated in the scope of the query that references them. DEF declares a reusable expression macro invoked with @name(...). See Concepts and Datasources and Joins for full modeling syntax."),
|
|
4917
|
+
new G("Definition Examples", "key customer_id int;\nproperty customer_id.customer_name string;\nauto gross_profit <- revenue - cost;\nmetric total_revenue <- sum(revenue);\ndef percent(part, whole) -> 100 * part / whole;", "code")
|
|
4918
4918
|
]),
|
|
4919
|
-
new
|
|
4920
|
-
new
|
|
4921
|
-
new
|
|
4922
|
-
new
|
|
4923
|
-
new
|
|
4924
|
-
new
|
|
4925
|
-
new
|
|
4926
|
-
new
|
|
4927
|
-
new
|
|
4928
|
-
new
|
|
4929
|
-
new
|
|
4930
|
-
new
|
|
4931
|
-
new
|
|
4932
|
-
new
|
|
4919
|
+
new K("Concepts", [
|
|
4920
|
+
new G("What Are Concepts?", "Concepts are core semantic building blocks in Trilogy. They represent keys, properties, or metrics: Keys (unique identifiers), Properties (additional values), and Metrics (aggregatable values)."),
|
|
4921
|
+
new G("Example", "key product_id int;# the unique identifier of a product \nproperty product_id.product_name string;# the name of a product \nmetric product_count <-count(product.id); # the count of products", "code"),
|
|
4922
|
+
new G("Keys", "Keys represent a unique conceptual grain. A combination of one or more keys will uniquely identify a set of properties. If familiar with databases, think of them as your primary/foreign keys. More generally, you can thing of them as something like a passport number, a product ID, a URL, or a stock ticker - a unique shorthand for an entity."),
|
|
4923
|
+
new G("Key Syntax", "Keys have straight forward syntax; the keyword, the name, and the type."),
|
|
4924
|
+
new G("Example", "key product_id int;# the unique identifier of a product", "code"),
|
|
4925
|
+
new G("Properties", "Properties are values associated with one or more keys"),
|
|
4926
|
+
new G("Property Syntax", "Properties have a richer syntax than keys - they require the associated keys, within <>, before the property name.. syntax: `\"property\" <key1, key2, ..> \".\" IDENTIFIER type;`"),
|
|
4927
|
+
new G("Single Key Property", "A property can be associated with a single key with a shorthand syntax without the full <> group. syntax: `\"property\" key \".\" IDENTIFIER type;`", "tip"),
|
|
4928
|
+
new G("code", "property product_id.product_name string;# the name of a product", "code"),
|
|
4929
|
+
new G("Metrics", "Metrics are aggregatable values, and can come from properties, keys, or other metrics. Like keys, they have a grain, though this is inferred from the aggregation"),
|
|
4930
|
+
new G("Example", "metric product_count <-count(product.id); # the count of products", "code"),
|
|
4931
|
+
new G("Dynamic Grain", "A basic metric - like `metric product_count <- count(product_id);` - will have a dynamic aggregation level that corresponds to the query it is used in."),
|
|
4932
|
+
new G("Static Grain", "A metric can be created by an aggregation with a defined grain, ex: `metric product_count <-count(product_id) by store;`, in which case it behaves similar to a property. However, where conditions for a select will still be pushed inside these aggregates.")
|
|
4933
4933
|
]),
|
|
4934
|
-
new
|
|
4935
|
-
new
|
|
4936
|
-
new
|
|
4937
|
-
new
|
|
4938
|
-
new
|
|
4939
|
-
new
|
|
4940
|
-
new
|
|
4934
|
+
new K("Datasources and Joins", [
|
|
4935
|
+
new G("Defining Datasources", "A table in a warehouse is defined as a dataset with each column bound to a concept. When datasources share a common key, trilogy can join between them. Not all joins are equal; some are 1-1, 1-many, or many-1. Trilogy handles these by looking at the grain of each datasource involved. If a join is on a key other than the grain of the sources, the output may need to be grouped up to avoid duplication. "),
|
|
4936
|
+
new G("Partial Keys", "A table may not have all values of a key. For example, a list of orders may not have every customer ID, as not all customers may have placed orders. A datasource can be defined with a key marked as partial by prefixing the ~ character. Trilogy will ensure that partial sources are used on the right side of an outer join to avoid implicitly reducing results."),
|
|
4937
|
+
new G("Example", "datasource sales (\n order_id: orders.id,\n cu_id: ~customers.id,\n revenue: orders.revenue\n)\ngrain (orders.id)\naddress warehouse.sales;\n\ndatasource customers (\n customer_id: customers.id,\n name: customers.name\n)\ngrain (customers.id)\naddress warehouse.customers;", "code"),
|
|
4938
|
+
new G("Partial Data", "Sometimes partial data is all we need. For example, imagine a recent and archive table. When you query the last 7 days, and the last 30 are in recent - we can prune the archive."),
|
|
4939
|
+
new G("Example", "partial datasource recent_sales (\n order_id: orders.id,\n cu_id: customers.id,\n revenue: orders.revenue,\n date: orders.date\n)\ngrain (orders.id)\ncomplete where orders.date > current_date - 30\naddress warehouse.sales;\n\npartial datasource archive_sales (\n order_id: orders.id,\n cu_id: customers.id,\n revenue: orders.revenue,\n date: orders.date\n)\ngrain (orders.id)\ncomplete where orders.date <= current_date - 30\naddress warehouse.sales_archive;", "code"),
|
|
4940
|
+
new G("Partial Keys", "A query of the form `where orders.date > current_date -7 can safely resolve to just our recent. This lets us efficiently prune and select relevant datasources.")
|
|
4941
4941
|
]),
|
|
4942
|
-
new
|
|
4943
|
-
new
|
|
4944
|
-
new
|
|
4945
|
-
new
|
|
4942
|
+
new K("Grains and Aggregation", [
|
|
4943
|
+
new G("What Is a Grain?", "A grain represents the unique combination of keys. Tables and aggregations both have grains, which determine the minimum keys required to uniquely identify a row of data. For example, finding the total sales by customer id, anme, and address would be an aggregation to the grain of a customer id, no matter how many other customer properties are included. Properties of a key are implicitly dropped from any grain that includes that key, though a grain without the key associated with a property will include that property in the grain."),
|
|
4944
|
+
new G("Example", "SELECT\n order_date,\n order_year,\n SUM(total_spent) AS total_revenue;", "code"),
|
|
4945
|
+
new G("What Is a Grain?", "This query would aggregate revenue to the grain of order_date, as order year is a property of order date.")
|
|
4946
4946
|
]),
|
|
4947
4947
|
kn,
|
|
4948
4948
|
On,
|
|
4949
4949
|
An,
|
|
4950
|
-
new
|
|
4951
|
-
new
|
|
4952
|
-
new
|
|
4953
|
-
new
|
|
4954
|
-
new
|
|
4955
|
-
new
|
|
4950
|
+
new K("Custom Functions", [
|
|
4951
|
+
new G("Defining Functions", "Custom functions can be used to extend the language with reusable code macros. Functions are defined using the def keyword and have a list of arguments and are mapped to an expression. Any argument alias will be locally scoped within the function, but external concepts can be referenced as well."),
|
|
4952
|
+
new G("Defining Functions", "This function will multiple the input concept by itself and then by whatever the value of the global scale_factor is. Note the @ - custom functions references require the @ prefix."),
|
|
4953
|
+
new G("Example", "const scale_factor<-2;\ndef square_scale(x) -> x * x *scale_factor;\n\nSELECT\n number,\n @square_scale(number) AS squared\n;", "code"),
|
|
4954
|
+
new G("Defining Functions", "Functions may have optional defaults by adding an `= value` after the argument name."),
|
|
4955
|
+
new G("Example", "def pretty_percent(x, digits=2) -> round(x*100, digits)::string || '%';\n\nconst number<-.4555;\n\nSELECT\n number,\n @pretty_percent(number) AS percent,\n @pretty_percent(number, 3) AS three_percent\n;", "code")
|
|
4956
4956
|
]),
|
|
4957
4957
|
jn
|
|
4958
|
-
]), Nn = new
|
|
4959
|
-
new
|
|
4960
|
-
new
|
|
4961
|
-
new
|
|
4962
|
-
new
|
|
4963
|
-
new
|
|
4964
|
-
new
|
|
4965
|
-
new
|
|
4966
|
-
new
|
|
4967
|
-
new
|
|
4968
|
-
new
|
|
4969
|
-
new
|
|
4970
|
-
new
|
|
4971
|
-
new
|
|
4972
|
-
new
|
|
4973
|
-
new
|
|
4974
|
-
new
|
|
4975
|
-
])]), Pn = [
|
|
4976
|
-
new G("Studio",
|
|
4977
|
-
|
|
4978
|
-
|
|
4958
|
+
]), Nn = new q("Terms of Service", [new K("Terms of Service", [
|
|
4959
|
+
new G("Last Updated", "February 22, 2025"),
|
|
4960
|
+
new G("Service Description", "Trilogy Studio (referred to as \"IDE\" henceforth) enables users to interact with public data or data in databases they own. The IDE communicates with non-user services only to do basic telemetry and preprocessing of Trilogy code using the default language server (if a local one is not configured). The primary purpose of this preprocessing is to generate SQL to be returned to the browser for execution.", "section"),
|
|
4961
|
+
new G("Data Privacy and Security", "No Data Collection: We do not collect, store, or process any of your data or database contents, beyond metadata provided in the model definition. All database interactions occur directly between your browser and your database through client-side JavaScript.", "section"),
|
|
4962
|
+
new G("Local Processing", "All code execution and data processing occur locally in your browser. We have no access to your database credentials, queries, or results.", "subsection"),
|
|
4963
|
+
new G("User Responsibility", "You are solely responsible for securing your database connections, managing database credentials, implementing appropriate security measures, backing up your data, and ensuring compliance with applicable data protection laws.", "subsection"),
|
|
4964
|
+
new G("Service Limitations", "Browser Limitations: The IDE is subject to your browser's technical limitations, including memory constraints and processing capabilities.", "section"),
|
|
4965
|
+
new G("Connection Requirements", "Stable internet connection required for IDE access. Database connectivity depends on your database configuration and network conditions.", "subsection"),
|
|
4966
|
+
new G("User Obligations", "You agree to use the IDE in compliance with applicable laws, not attempt to circumvent browser security measures, not use the IDE for illegal or unauthorized purposes, maintain the security of your database credentials, and not redistribute or modify the IDE code without permission.", "section"),
|
|
4967
|
+
new G("Disclaimers", "Service Availability: We provide the IDE \"as is\" and make no guarantees about its availability or functionality.", "section"),
|
|
4968
|
+
new G("Security Disclaimer", "While we implement standard security measures in our code, we cannot guarantee the security of your database connections or local environment.", "subsection"),
|
|
4969
|
+
new G("Liability Limitations", "We are not liable for data loss or corruption, database connection issues, browser performance problems, security breaches in your database, misuse of the IDE, or consequential damages.", "section"),
|
|
4970
|
+
new G("User Rights", "You retain all rights to your code, your database, your data, and any outputs generated using the IDE.", "subsection"),
|
|
4971
|
+
new G("Modifications", "We reserve the right to modify the IDE functionality, these Terms of Service, and supported features and libraries.", "section"),
|
|
4972
|
+
new G("Termination", "You may stop using the IDE at any time.", "section"),
|
|
4973
|
+
new G("Contact", "For questions about these terms, please create an issue on the github repository <a href='https://github.com/trilogy-data/trilogy-studio-core'>here</a>.", "section"),
|
|
4974
|
+
new G("Agreement", "By using the IDE, you agree to these terms and conditions.", "conclusion")
|
|
4975
|
+
])]), Pn = new K("Startup Scripts", [
|
|
4976
|
+
new G("Connection Setup", "Startup scripts prepare a connection before you use it, for example by creating tables, loading data, or applying session settings. Once enabled on a SQL editor, they run automatically whenever that connection connects or resets. No separate run-on-connect setting is needed. This does not make the connection itself connect automatically when Studio opens."),
|
|
4977
|
+
new G("Configure a Startup Script", "<ol>\n <li>Create or choose a connection in the Connections sidebar.</li>\n <li>Create a SQL editor associated with that connection and give it a descriptive name, such as <code>setup/load_sales.sql</code>.</li>\n <li>Write and test the setup SQL for your database.</li>\n <li>Enable <strong>Startup</strong> in the editor toolbar (the tooltip reads “Run this script on connection startup”).</li>\n <li>Save the editor and connection so the configuration is available after reloading Studio.</li>\n <li>Connect to run its startup scripts. If already connected, save your work, reload Studio, and reconnect to apply the setup.</li>\n </ol>"),
|
|
4978
|
+
new G("Example: Prepare a DuckDB Table", "For a DuckDB connection, the following SQL creates a small table that other editors can query. CREATE OR REPLACE makes it safe to rerun when reconnecting or resetting. Use the equivalent repeatable setup syntax for your database."),
|
|
4979
|
+
new G("DuckDB Setup SQL", "CREATE OR REPLACE TABLE startup_example AS\nSELECT 1 AS id, 'ready' AS status;", "code"),
|
|
4980
|
+
new G("When Scripts Run", "All editors marked Startup for that connection run after the database connection resets, including when it first connects. Changing the SQL or toggling Startup in the editor does not immediately rerun it. To rerun setup for an already connected session, save your work, reload Studio, and reconnect. The sidebar’s Refresh connection action only reloads database metadata when already connected; it does not reset the session or rerun startup scripts. Startup submits the editor contents directly as SQL, without Trilogy compilation. Only editors belonging to that connection are selected."),
|
|
4981
|
+
new G("Multiple Scripts and Dependencies", "Separate startup scripts are launched in parallel, with no guaranteed execution order. Put dependent SQL statements in one script, in the order they need to run, using a database that supports multi-statement scripts. Design setup to be repeatable: use CREATE OR REPLACE or IF NOT EXISTS where appropriate, and avoid inserts that duplicate data on each reconnect.", "tip"),
|
|
4982
|
+
new G("Diagnosing a Failure", "If a startup script fails, the connection is marked failed and the connection error popup identifies the script name (or remote file path), the connection, and the original database error. Open the named SQL editor, fix the SQL or the referenced data access, save it, and reconnect. For example, Startup script \"setup/load_sales.sql\" failed on connection \"analytics\" is followed by the database’s explanation. Other scripts may already have run; a startup failure does not roll back the entire group."),
|
|
4983
|
+
new G("Disable a Startup Script", "Open the SQL editor, turn off Startup, and save. Future connections and resets will skip that script. Disabling it does not undo SQL that already ran, such as creating a table or loading data.")
|
|
4984
|
+
]), Fn = [
|
|
4985
|
+
new q("Studio", [
|
|
4979
4986
|
wn,
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
new
|
|
4987
|
+
Sn,
|
|
4988
|
+
Cn,
|
|
4989
|
+
Tn,
|
|
4990
|
+
new K("Functionality", [
|
|
4991
|
+
new G("Tip", "Trilogy Studio is a statically hosted website that uses a mix of precompiled scripts and dynamic imports for some connections to reduce bundle size."),
|
|
4992
|
+
new G("Tip", "For Trilogy specific features, the studio relies on a pseudo-language server (there is a proper LSP available for vs-code, used in the vs-code extension, as well). By default this will use a cloud-hosted backend but studio can be configured to use a local address as well. "),
|
|
4993
|
+
new G("Tip", "Trilogy Studio requires internet access to download certain dependencies - such as DuckDB WASM - on demand when utilizing a relevant connection. It can then be used offline if a local backend server is running. A pure offline option may be available in the future.", "tip")
|
|
4985
4994
|
]),
|
|
4986
|
-
new
|
|
4987
|
-
new
|
|
4988
|
-
new
|
|
4989
|
-
new
|
|
4990
|
-
new
|
|
4995
|
+
new K("Navigation", [new G("Navigation", "On desktop, Trilogy Studio uses a left-hand navigation bar, a sidebar with context, and a main pane (which may be split) to present information. The sidebar is further split into sections; the first icons change the sidebar for the query editor, while the next section configures entirely new main screen experiences. Configuration/profile are accessible at the bottom. On mobile, Trilogy will instead use a top menu to access a navigation screen, and the main display will show only one particular field at a time."), new G("Tip", "Don't forget to save! By default, Trilogy Studio stores your editors, connections, and models in browser local storage. Use the save buttons on the left-hand nav to record changes. You can also use the keyboard shortcuts Ctrl + S to save your work.", "tip")]),
|
|
4996
|
+
new K("Querying", [
|
|
4997
|
+
new G("Querying", "A core goal of Trilogy Studio is to enable seamless running of Trilogy against backends. Raw SQL is fully supported as well to help with debugging, diagnostics, and other development tasks."),
|
|
4998
|
+
new G("Editor Selection", "When creating an editor, you will select a type. Trilogy editors run Trilogy code; SQL editors run SQL. An editor must be associated with a connection and may be associated with a model."),
|
|
4999
|
+
new G("Query Execution", "When running Trilogy queries, your command will first go to a backend server to be parsed/type-checked, then the output SQL will be returned to the editor to be run as a normal SQL query. SQL editors will submit directly to the configured connection without the first parsing pass.")
|
|
4991
5000
|
]),
|
|
4992
|
-
new
|
|
4993
|
-
new
|
|
4994
|
-
new
|
|
4995
|
-
new
|
|
5001
|
+
new K("Connections", [
|
|
5002
|
+
new G("Connections", "Editors must be associated with a connection. A connection represents a particular underlying backend database resource. Many IDEs can share one connection, but only a single query can be executed on a connection at a single point in time. Connections are also associated with a model, which enables configuration of additional editors as semantic sources."),
|
|
5003
|
+
new G("Managing Connections", "You can view current connections below. Edit the model associated with a connection by clicking the model name next to it (or 'set model' if not set). Connections will not automatically connect on startup by default; click the connection button to connect. This connection view is always accessible through the connections page on the left side."),
|
|
5004
|
+
new G("ConnectionList", "", "connections"),
|
|
5005
|
+
new G("Startup Scripts", "To prepare a connection automatically on connect or reset, enable Startup on a SQL editor associated with it. See Studio → Startup Scripts for setup instructions, examples, and troubleshooting.")
|
|
4996
5006
|
]),
|
|
4997
|
-
|
|
5007
|
+
Pn,
|
|
5008
|
+
new K("Scheduling", [new G("Scheduling", "Scheduling dashboards/scripts is a future feature. Please upvote this github issue: <a href=\"https://github.com/trilogy-data/trilogy-studio-core/issues/75\" target=\"_blank\">Scheduling</a>.")])
|
|
4998
5009
|
]),
|
|
4999
|
-
new
|
|
5000
|
-
new
|
|
5001
|
-
new
|
|
5002
|
-
new
|
|
5003
|
-
new
|
|
5010
|
+
new q("Supported Databases", [
|
|
5011
|
+
new K("Overview", [
|
|
5012
|
+
new G("Supported Databases", "Trilogy Studio supports two classes of database connections: local browser engines and remote databases. Every editor runs against one of these connections, and Trilogy queries are compiled to the SQL dialect selected by that connection."),
|
|
5013
|
+
new G("Local Browser Engines", "DuckDB and SQLite run locally inside the browser using WebAssembly. They require no database server or credentials, and uploaded data stays in the browser tab. These connections are convenient for local files, examples, prototyping, and offline query execution after their runtime assets have loaded."),
|
|
5014
|
+
new G("Remote Databases", "BigQuery and Snowflake connect to externally hosted databases and require authentication. Queries run against the remote service, while Trilogy Studio manages the connection configuration, SQL dialect, schema browser, and query results in the interface. Use narrowly scoped credentials and review the Stored Info documentation before saving secrets.")
|
|
5004
5015
|
]),
|
|
5005
|
-
new
|
|
5006
|
-
new
|
|
5007
|
-
new
|
|
5008
|
-
new
|
|
5009
|
-
new
|
|
5016
|
+
new K("DuckDB", [new G("DuckDB", "DuckDB is always available as an in-browser database using the DuckDB WASM integration. No authentication is required. DuckDB also natively supports CSV uploads through a widget available on the connection view. Most community DuckDB models reference publicly available CSV or Parquet files.")]),
|
|
5017
|
+
new K("SQLite", [
|
|
5018
|
+
new G("SQLite", "SQLite is available as an in-browser database using the sql.js WebAssembly integration. No server or authentication is required. Create a SQLite connection when you want to query an existing SQLite database or work with a lightweight local dataset using SQLite SQL."),
|
|
5019
|
+
new G("Importing Data", "Open the SQLite connection and select Upload to import a .db or .sqlite database file. Importing a database replaces the current in-memory database for that connection. You can also upload a CSV file; Trilogy Studio creates a table named after the file, sanitizes its column names, and infers INTEGER, REAL, or TEXT column types from the data."),
|
|
5020
|
+
new G("Browser Storage", "SQLite connection configuration is saved with the rest of your Studio workspace, but the contents of the in-browser database are memory-backed. After reloading the page, reconnect and upload the database or CSV files again before running queries that depend on them.", "tip")
|
|
5010
5021
|
]),
|
|
5011
|
-
new
|
|
5012
|
-
new
|
|
5022
|
+
new K("BigQuery", [new G("BigQuery", "BigQuery is supported through OAuth authentication using your Google identity. If you wish to use a service key file, please upvote this GitHub issue: <a href=\"https://github.com/trilogy-data/trilogy-studio-core/issues/33\" target=\"_blank\">BQ Service Account Support</a>.")]),
|
|
5023
|
+
new K("Snowflake", [new G("Snowflake", "Snowflake is supported with private key authentication. Read more at this link: <a href=\"https://docs.snowflake.com/en/user-guide/key-pair-auth\" target=\"_blank\">Snowflake Private Key Pairs</a>. Configure your Snowflake user with the public key, then provide the private key when connecting. Use caution when choosing whether to save this credential.")])
|
|
5013
5024
|
]),
|
|
5014
5025
|
Mn,
|
|
5015
|
-
new
|
|
5016
|
-
new
|
|
5017
|
-
new
|
|
5018
|
-
new
|
|
5019
|
-
new
|
|
5020
|
-
new
|
|
5021
|
-
new
|
|
5022
|
-
new
|
|
5023
|
-
new
|
|
5024
|
-
new
|
|
5025
|
-
new
|
|
5026
|
-
new
|
|
5027
|
-
new
|
|
5028
|
-
new
|
|
5029
|
-
new
|
|
5030
|
-
new
|
|
5026
|
+
new q("Privacy And Data", [
|
|
5027
|
+
new K("Privacy Policy", [
|
|
5028
|
+
new G("Last Updated", "February 22, 2025"),
|
|
5029
|
+
new G("Introduction", "This Privacy Policy explains how Trilogy Studio handles your information. While most processing occurs in your browser, we utilize some external services to enhance your development experience.", "section"),
|
|
5030
|
+
new G("Information We Collect", "Telemetry: We collect feature usage counts and patterns and coarse browser/geographic information to inform product decision and system performance using GoatTrack. This data is anonymous and does not include any personally identifiable information. Telemetry can be disabled in settings.", "section"),
|
|
5031
|
+
new G("Query Processing", "We process Trilogy queries to transform them to SQL based on your model inputs if you use the default query service. Results are returned to your browser for execution. No actual database connections or credentials are transmitted.", "subsection"),
|
|
5032
|
+
new G("What We Don't Collect", "We do not collect database credentials, database contents, query results, personal information, full source code, or authentication tokens, or any other personal information. Please report any issues with this to the github repository.", "section"),
|
|
5033
|
+
new G("Data Processing", "Query Execution: All database execution happens between your browser and remote service. Query results never pass through our servers.", "section"),
|
|
5034
|
+
new G("Security Measures", "Any saved local credentials will be encrypted in storage using a passphrase or Chrome credential storage. See Stored Info for more details.", "section"),
|
|
5035
|
+
new G("Children's Privacy", "Trilogy Studio is intended for adult data professionals.", "section"),
|
|
5036
|
+
new G("International Data", "Telemetry data may cross borders. SQL compilation happens in the US with default query service (fly.io).", "section"),
|
|
5037
|
+
new G("Changes to Privacy Policy", "We may update this policy to reflect new features or services, changed functionality, legal requirements, and security improvements.", "section"),
|
|
5038
|
+
new G("Google Account", "Trilogy Studio uses Google OAuth to authenticate users when using a Google Bigquery Oauth connection. Trilogy Studio uses a token provided by Google to authenticate your account. Trilogy Studio only requests scopes required for Bigquery read/write access, and the token never leaves your browser. It is used to communicate directly with Bigquery with the google javascript client library."),
|
|
5039
|
+
new G("Contact Information", "For privacy-related questions: please post on the github repository <a href='https://github.com/trilogy-data/trilogy-studio-core'>here</a>.", "section"),
|
|
5040
|
+
new G("Legal Rights", "You retain all legal rights regarding your personal data, your source code, your database contents, and generated outputs.", "section"),
|
|
5041
|
+
new G("Effective Date", "This policy is effective as of February 22, 2025.", "conclusion")
|
|
5031
5042
|
]),
|
|
5032
|
-
new
|
|
5033
|
-
new
|
|
5034
|
-
new
|
|
5035
|
-
new
|
|
5036
|
-
new
|
|
5043
|
+
new K("Stored Info", [
|
|
5044
|
+
new G("Local Data", "Trilogy Studio uses browser local storage for your editors and models. These do not leave your browser (except when a model is sent in to generate a query)."),
|
|
5045
|
+
new G("Secret Storage", "For databases and LLM connections that require credentials (password, API Key) to access, Trilogy Studio can optionally also store them locally for reuse. [They will never be sent to a remote server]. It will attempt to use secure browser credential storage but will fall back to storing the secret in local browser storage encrypted with a pass phrase if the browser APIs are not available. Be careful storing credentials and be prepared to rotate - consider using a password manager."),
|
|
5046
|
+
new G("Secret Storage", "Local storage is convenient, but if you have a browser secret manager, that is a great place to store secrets as well!", "tip"),
|
|
5047
|
+
new G("Secret Storage", "A best practice is to use unique API tokens (such as for LLMs) and passwords in the studio, both to easily track usage and make rotation simple.", "tip")
|
|
5037
5048
|
]),
|
|
5038
|
-
new
|
|
5039
|
-
new
|
|
5040
|
-
new
|
|
5049
|
+
new K("Google", [new G("Google Account", "Trilogy Studio uses Google OAuth to authenticate users when using a Google Bigquery connection. Trilogy Studio uses a token provided by Google to authenticate your account through the interactive sign-in flow. Trilogy Studio only requests scopes required for Bigquery read/write access, and the token never leaves your browser. This token is only used to communicate directly with BigQuery with the standard google javascript client library.")]),
|
|
5050
|
+
new K("Snowflake", [new G("Google Account", "Trilogy Studio supports private key authentication for Snowflake. You will need to provide the private key to connect after configuring your user with the public key portion in Snowflake directly. The rest of the authentication header can be derived from this.")]),
|
|
5051
|
+
new K("Telemetry Details", [new G("Telemetry", "<a href=\"https://www.goatcounter.com/\">GoatCounter</a> is used to collect anonymized\n statistics about usage in the studio, such as feature usage. No uniquely identifying information is collected, though the system type, browser, screensize and country are recorded. You can disable telemetry in the settings menu.")])
|
|
5041
5052
|
]),
|
|
5042
5053
|
Nn
|
|
5043
|
-
],
|
|
5054
|
+
], In = /* @__PURE__ */ new Set([
|
|
5044
5055
|
"editors",
|
|
5045
5056
|
"connections",
|
|
5046
5057
|
"llm-connections",
|
|
@@ -5050,27 +5061,27 @@ var Dn = K.paragraphs[1], On = new W("Aggregate Functions", [
|
|
|
5050
5061
|
"editor-validator",
|
|
5051
5062
|
"connection-validator",
|
|
5052
5063
|
"model-validator"
|
|
5053
|
-
]),
|
|
5054
|
-
function
|
|
5064
|
+
]), Ln = /* @__PURE__ */ new Set(["Trilogy Reference"]), X = "Trilogy Language", Rn = "Example: ";
|
|
5065
|
+
function zn(e) {
|
|
5055
5066
|
return e.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
5056
5067
|
}
|
|
5057
|
-
function
|
|
5058
|
-
let t =
|
|
5068
|
+
function Bn(e) {
|
|
5069
|
+
let t = zn(e.content || ""), n = e.data?.function;
|
|
5059
5070
|
return n && (t = [
|
|
5060
5071
|
t,
|
|
5061
5072
|
`Inputs: ${n.inputTypes.join(", ")}. Output: ${n.outputType} (${n.outputPurpose}).`,
|
|
5062
5073
|
`Example: ${n.example}`
|
|
5063
5074
|
].filter(Boolean).join(" ")), t;
|
|
5064
5075
|
}
|
|
5065
|
-
var
|
|
5066
|
-
function
|
|
5067
|
-
if (
|
|
5076
|
+
var Vn = null;
|
|
5077
|
+
function Hn() {
|
|
5078
|
+
if (Vn) return Vn;
|
|
5068
5079
|
let e = [];
|
|
5069
|
-
for (let t of
|
|
5070
|
-
let n =
|
|
5080
|
+
for (let t of Fn) {
|
|
5081
|
+
let n = Ln.has(t.title);
|
|
5071
5082
|
for (let r of t.articles) for (let i of r.paragraphs) {
|
|
5072
|
-
if (i.type &&
|
|
5073
|
-
let a =
|
|
5083
|
+
if (i.type && In.has(i.type)) continue;
|
|
5084
|
+
let a = Bn(i);
|
|
5074
5085
|
a && e.push({
|
|
5075
5086
|
id: `${t.title}/${r.title}/${i.title}`,
|
|
5076
5087
|
nodeTitle: t.title,
|
|
@@ -5081,27 +5092,37 @@ function Bn() {
|
|
|
5081
5092
|
});
|
|
5082
5093
|
}
|
|
5083
5094
|
}
|
|
5084
|
-
let t =
|
|
5095
|
+
let t = S.split(/^(?=#{1,3} )/m);
|
|
5085
5096
|
for (let n of t) {
|
|
5086
5097
|
let t = n.match(/^#{1,3} (.+)/);
|
|
5087
5098
|
if (!t) continue;
|
|
5088
5099
|
let r = t[1].trim(), i = n.slice(t[0].length).trim();
|
|
5089
5100
|
i && e.push({
|
|
5090
|
-
id: `${
|
|
5091
|
-
nodeTitle:
|
|
5101
|
+
id: `${X}/Syntax Reference/${r}`,
|
|
5102
|
+
nodeTitle: X,
|
|
5092
5103
|
articleTitle: "Syntax Reference",
|
|
5093
5104
|
paragraphTitle: r,
|
|
5094
5105
|
content: i,
|
|
5095
5106
|
kind: "language"
|
|
5096
5107
|
});
|
|
5097
5108
|
}
|
|
5098
|
-
|
|
5109
|
+
for (let t of re) t.body?.trim() && e.push({
|
|
5110
|
+
id: `${X}/${Rn}${t.name}/${t.title}`,
|
|
5111
|
+
nodeTitle: X,
|
|
5112
|
+
articleTitle: `${Rn}${t.name}`,
|
|
5113
|
+
paragraphTitle: t.title,
|
|
5114
|
+
content: `${t.summary}
|
|
5115
|
+
|
|
5116
|
+
${t.body.trim()}`,
|
|
5117
|
+
kind: "language"
|
|
5118
|
+
});
|
|
5119
|
+
return Vn = e, e;
|
|
5099
5120
|
}
|
|
5100
|
-
function
|
|
5121
|
+
function Z(e) {
|
|
5101
5122
|
return e.toLowerCase().split(/[^a-z0-9_]+/).filter((e) => e.length > 1);
|
|
5102
5123
|
}
|
|
5103
|
-
function
|
|
5104
|
-
let n =
|
|
5124
|
+
function Un(e, t) {
|
|
5125
|
+
let n = Z(e.paragraphTitle), r = Z(e.articleTitle), i = e.content.toLowerCase(), a = Z(e.content), o = 0;
|
|
5105
5126
|
for (let e of t) {
|
|
5106
5127
|
n.includes(e) ? o += 3 : e.length >= 4 && n.some((t) => t.startsWith(e)) && (o += 1.5), r.includes(e) && (o += 2);
|
|
5107
5128
|
let t = a.filter((t) => t === e).length;
|
|
@@ -5109,29 +5130,29 @@ function Vn(e, t) {
|
|
|
5109
5130
|
}
|
|
5110
5131
|
return o / Math.log(Math.max(e.content.length, 50));
|
|
5111
5132
|
}
|
|
5112
|
-
function
|
|
5133
|
+
function Wn(e, t) {
|
|
5113
5134
|
let n = e.content.toLowerCase(), r = -1;
|
|
5114
5135
|
for (let e of t) if (r = n.indexOf(e), r >= 0) break;
|
|
5115
5136
|
r < 0 && (r = 0);
|
|
5116
5137
|
let i = Math.max(0, r - 80), a = Math.min(e.content.length, r + 160);
|
|
5117
5138
|
return `${i > 0 ? "…" : ""}${e.content.slice(i, a)}${a < e.content.length ? "…" : ""}`;
|
|
5118
5139
|
}
|
|
5119
|
-
function
|
|
5120
|
-
let n =
|
|
5140
|
+
function Gn(e, t = {}) {
|
|
5141
|
+
let n = Z(e);
|
|
5121
5142
|
if (n.length === 0) return [];
|
|
5122
5143
|
let r = t.limit ?? 5;
|
|
5123
|
-
return
|
|
5144
|
+
return Hn().filter((e) => !t.kind || e.kind === t.kind).map((e) => ({
|
|
5124
5145
|
entry: e,
|
|
5125
|
-
score:
|
|
5146
|
+
score: Un(e, n)
|
|
5126
5147
|
})).filter((e) => e.score > 0).sort((e, t) => t.score - e.score).slice(0, r).map((e) => ({
|
|
5127
5148
|
...e,
|
|
5128
|
-
snippet:
|
|
5149
|
+
snippet: Wn(e.entry, n)
|
|
5129
5150
|
}));
|
|
5130
5151
|
}
|
|
5131
|
-
function
|
|
5152
|
+
function Kn(e) {
|
|
5132
5153
|
let [t, n] = e.split("/");
|
|
5133
5154
|
if (!t || !n) return null;
|
|
5134
|
-
let r =
|
|
5155
|
+
let r = Hn().filter((e) => e.nodeTitle === t && e.articleTitle === n);
|
|
5135
5156
|
return r.length === 0 ? null : {
|
|
5136
5157
|
id: `${t}/${n}`,
|
|
5137
5158
|
nodeTitle: t,
|
|
@@ -5144,7 +5165,7 @@ function Wn(e) {
|
|
|
5144
5165
|
}
|
|
5145
5166
|
//#endregion
|
|
5146
5167
|
//#region llm/registry/packs/docsPack.ts
|
|
5147
|
-
function
|
|
5168
|
+
function qn() {
|
|
5148
5169
|
return [
|
|
5149
5170
|
{
|
|
5150
5171
|
pack: "docs",
|
|
@@ -5176,7 +5197,7 @@ function Gn() {
|
|
|
5176
5197
|
}
|
|
5177
5198
|
},
|
|
5178
5199
|
execute: async (e) => {
|
|
5179
|
-
let t =
|
|
5200
|
+
let t = Gn(String(e.query ?? ""), {
|
|
5180
5201
|
limit: Number(e.limit) || 5,
|
|
5181
5202
|
kind: e.kind
|
|
5182
5203
|
});
|
|
@@ -5204,7 +5225,7 @@ function Gn() {
|
|
|
5204
5225
|
}
|
|
5205
5226
|
},
|
|
5206
5227
|
execute: async (e) => {
|
|
5207
|
-
let t =
|
|
5228
|
+
let t = Kn(String(e.doc_id ?? ""));
|
|
5208
5229
|
return t ? {
|
|
5209
5230
|
success: !0,
|
|
5210
5231
|
message: `${t.nodeTitle} > ${t.articleTitle}\n\n${t.paragraphs.map((e) => `## ${e.title}\n${e.content}`).join("\n\n")}`
|
|
@@ -5233,7 +5254,7 @@ function Gn() {
|
|
|
5233
5254
|
hint: "App navigation is not available in this embedding context."
|
|
5234
5255
|
},
|
|
5235
5256
|
execute: async (e, t) => {
|
|
5236
|
-
let n =
|
|
5257
|
+
let n = Kn(String(e.doc_id ?? ""));
|
|
5237
5258
|
if (!n) return {
|
|
5238
5259
|
success: !1,
|
|
5239
5260
|
error: `Documentation "${e.doc_id}" not found. Use search_docs to find valid ids.`
|
|
@@ -5253,7 +5274,7 @@ function Gn() {
|
|
|
5253
5274
|
}
|
|
5254
5275
|
//#endregion
|
|
5255
5276
|
//#region llm/registry/packs/contextPack.ts
|
|
5256
|
-
function
|
|
5277
|
+
function Jn() {
|
|
5257
5278
|
return [{
|
|
5258
5279
|
pack: "context",
|
|
5259
5280
|
definition: {
|
|
@@ -5276,20 +5297,20 @@ function Kn() {
|
|
|
5276
5297
|
}
|
|
5277
5298
|
//#endregion
|
|
5278
5299
|
//#region llm/registry/index.ts
|
|
5279
|
-
function
|
|
5280
|
-
let e = new
|
|
5281
|
-
return e.registerAll(
|
|
5300
|
+
function Yn() {
|
|
5301
|
+
let e = new Vt();
|
|
5302
|
+
return e.registerAll(Jt()), e.registerAll(Yt()), e.registerAll(en()), e.registerAll(ln()), e.registerAll(hn()), e.registerAll(xn()), e.registerAll(qn()), e.registerAll(Jn()), e.registerAll(Xt()), e;
|
|
5282
5303
|
}
|
|
5283
|
-
var
|
|
5284
|
-
function
|
|
5285
|
-
return
|
|
5304
|
+
var Xn = null;
|
|
5305
|
+
function Zn() {
|
|
5306
|
+
return Xn ||= Yn(), Xn;
|
|
5286
5307
|
}
|
|
5287
5308
|
//#endregion
|
|
5288
5309
|
//#region llm/architectAgentPrompt.ts
|
|
5289
|
-
function
|
|
5310
|
+
function Qn(e) {
|
|
5290
5311
|
return ` - ${e.name} (${e.type}, ${e.size} bytes)`;
|
|
5291
5312
|
}
|
|
5292
|
-
function
|
|
5313
|
+
function $n() {
|
|
5293
5314
|
return `You are an ARCHITECT subchat. Your job is to build a Trilogy data model from the raw files attached to this project. The user does not see this conversation — your overseer does. Work autonomously; when finished, call return_to_user with a brief summary.
|
|
5294
5315
|
|
|
5295
5316
|
TYPICAL WORKFLOW:
|
|
@@ -5304,7 +5325,7 @@ TYPICAL WORKFLOW:
|
|
|
5304
5325
|
6. return_to_user with a list of the files created and any caveats.
|
|
5305
5326
|
|
|
5306
5327
|
TRILOGY LANGUAGE REFERENCE:
|
|
5307
|
-
${
|
|
5328
|
+
${S}
|
|
5308
5329
|
|
|
5309
5330
|
OPERATIONAL GUIDELINES:
|
|
5310
5331
|
- One .preql file per logical entity (one per CSV is a good default).
|
|
@@ -5313,15 +5334,15 @@ OPERATIONAL GUIDELINES:
|
|
|
5313
5334
|
- Don't return until at least one file validates cleanly and a smoke query succeeds, OR you've exhausted reasonable attempts and need to report blockers.
|
|
5314
5335
|
- Errors from the resolver are authoritative — read them carefully; don't guess fixes.`;
|
|
5315
5336
|
}
|
|
5316
|
-
function
|
|
5317
|
-
let t = e.files.length > 0 ? `FILES IN THIS PROJECT:\n${e.files.map(
|
|
5318
|
-
return `${e.instructionsOverride?.trim() ||
|
|
5337
|
+
function er(e) {
|
|
5338
|
+
let t = e.files.length > 0 ? `FILES IN THIS PROJECT:\n${e.files.map(Qn).join("\n")}` : "No files in this project yet.", n = e.dataConnectionName ? `DATA CONNECTION: ${e.dataConnectionName}${e.isDataConnectionActive ? "" : " (not yet connected — first run_trilogy_query will boot it)"}` : "No data connection bound.";
|
|
5339
|
+
return `${e.instructionsOverride?.trim() || $n()}
|
|
5319
5340
|
|
|
5320
5341
|
${t}
|
|
5321
5342
|
${n}
|
|
5322
5343
|
`;
|
|
5323
5344
|
}
|
|
5324
|
-
var
|
|
5345
|
+
var tr = [
|
|
5325
5346
|
{
|
|
5326
5347
|
name: "list_project_files",
|
|
5327
5348
|
description: "List every file attached to the current project: name, type (preql / csv / sql / markdown / etc.), size in bytes.",
|
|
@@ -5461,12 +5482,12 @@ var Qn = [
|
|
|
5461
5482
|
required: ["query"]
|
|
5462
5483
|
}
|
|
5463
5484
|
},
|
|
5464
|
-
|
|
5465
|
-
],
|
|
5466
|
-
function
|
|
5467
|
-
|
|
5485
|
+
y
|
|
5486
|
+
], nr = [];
|
|
5487
|
+
function rr(e) {
|
|
5488
|
+
nr.push(e);
|
|
5468
5489
|
}
|
|
5469
|
-
var
|
|
5490
|
+
var ir = Ee("chats", {
|
|
5470
5491
|
state: () => ({
|
|
5471
5492
|
chats: {},
|
|
5472
5493
|
activeChatId: "",
|
|
@@ -5486,7 +5507,7 @@ var tr = Oe("chats", {
|
|
|
5486
5507
|
},
|
|
5487
5508
|
actions: {
|
|
5488
5509
|
newChat(e, t = "", n, r = "", i = {}) {
|
|
5489
|
-
let a = new
|
|
5510
|
+
let a = new o({
|
|
5490
5511
|
llmConnectionName: e,
|
|
5491
5512
|
dataConnectionName: t,
|
|
5492
5513
|
dataConnectionId: r,
|
|
@@ -5498,7 +5519,7 @@ var tr = Oe("chats", {
|
|
|
5498
5519
|
this.chats[e.id] = e;
|
|
5499
5520
|
},
|
|
5500
5521
|
removeChat(e) {
|
|
5501
|
-
this.chats[e] && (this.chats[e].deleted = !0, this.chats[e].changed = !0, this.activeChatId === e && (this.activeChatId = ""),
|
|
5522
|
+
this.chats[e] && (this.chats[e].deleted = !0, this.chats[e].changed = !0, this.activeChatId === e && (this.activeChatId = ""), nr.forEach((t) => {
|
|
5502
5523
|
try {
|
|
5503
5524
|
t(e);
|
|
5504
5525
|
} catch (e) {
|
|
@@ -5544,7 +5565,7 @@ var tr = Oe("chats", {
|
|
|
5544
5565
|
},
|
|
5545
5566
|
loadChats(e) {
|
|
5546
5567
|
Object.entries(e).forEach(([e, t]) => {
|
|
5547
|
-
this.chats[e] =
|
|
5568
|
+
this.chats[e] = o.fromSerialized(t);
|
|
5548
5569
|
});
|
|
5549
5570
|
},
|
|
5550
5571
|
serializeChats() {
|
|
@@ -5619,7 +5640,7 @@ var tr = Oe("chats", {
|
|
|
5619
5640
|
error: !0
|
|
5620
5641
|
}), { response: "No LLM connection available. Please configure an LLM provider first." };
|
|
5621
5642
|
let d = a.connections[l];
|
|
5622
|
-
d && await
|
|
5643
|
+
d && await ve(d, i), this.startExecution(e);
|
|
5623
5644
|
let f = this.chatExecutions[e], p = () => this.chatExecutions[e] === f, m = f?.abortController?.signal, h = !1;
|
|
5624
5645
|
this.addMessageToChat(e, {
|
|
5625
5646
|
role: "user",
|
|
@@ -5627,7 +5648,7 @@ var tr = Oe("chats", {
|
|
|
5627
5648
|
hidden: r.hiddenUserMessage ?? !1
|
|
5628
5649
|
});
|
|
5629
5650
|
try {
|
|
5630
|
-
let
|
|
5651
|
+
let d = { generateCompletion: async (t, n, r) => {
|
|
5631
5652
|
let o = {
|
|
5632
5653
|
...n,
|
|
5633
5654
|
signal: m,
|
|
@@ -5636,7 +5657,7 @@ var tr = Oe("chats", {
|
|
|
5636
5657
|
}
|
|
5637
5658
|
}, s = await a.generateCompletion(t, o, r);
|
|
5638
5659
|
return this.clearRateLimitBackoff(e), s.usage && (i.lastContextTokens = s.usage.promptTokens + (s.usage.cacheCreationTokens ?? 0) + (s.usage.cacheReadTokens ?? 0), i.changed = !0), s;
|
|
5639
|
-
} },
|
|
5660
|
+
} }, f = {
|
|
5640
5661
|
addMessage: (t) => {
|
|
5641
5662
|
this.addMessageToChat(e, t);
|
|
5642
5663
|
},
|
|
@@ -5649,16 +5670,16 @@ var tr = Oe("chats", {
|
|
|
5649
5670
|
});
|
|
5650
5671
|
},
|
|
5651
5672
|
getMessages: () => i.getLLMMessages()
|
|
5652
|
-
},
|
|
5673
|
+
}, p = r.overrides ? { getToolExecutor: () => ({ executeToolCall: r.overrides.executeToolCall }) } : i.kind === "overseer" ? (() => {
|
|
5653
5674
|
if (!n.projectStore) throw Error("Overseer chats require deps.projectStore");
|
|
5654
|
-
let t = new
|
|
5675
|
+
let t = new jt(this, n.projectStore, e, n, n.dashboardStore);
|
|
5655
5676
|
return { getToolExecutor: () => t };
|
|
5656
5677
|
})() : i.kind === "architect" ? (() => {
|
|
5657
5678
|
if (!n.projectStore) throw Error("Architect subchats require deps.projectStore");
|
|
5658
|
-
let t = new
|
|
5679
|
+
let t = new Pt(this, n.projectStore, c, o, s, e);
|
|
5659
5680
|
return { getToolExecutor: () => t };
|
|
5660
5681
|
})() : (() => {
|
|
5661
|
-
let t =
|
|
5682
|
+
let t = Zn().createExecutor("chat", {
|
|
5662
5683
|
connectionStore: o,
|
|
5663
5684
|
editorStore: c,
|
|
5664
5685
|
chatStore: this,
|
|
@@ -5682,20 +5703,20 @@ var tr = Oe("chats", {
|
|
|
5682
5703
|
}
|
|
5683
5704
|
} };
|
|
5684
5705
|
return { getToolExecutor: () => a };
|
|
5685
|
-
})(),
|
|
5706
|
+
})(), g = {
|
|
5686
5707
|
setActiveToolName: (t) => this.setActiveToolName(e, t),
|
|
5687
5708
|
checkAborted: () => m?.aborted ?? !1,
|
|
5688
5709
|
isPaused: () => this.chatExecutions[e]?.paused ?? !1
|
|
5689
|
-
},
|
|
5710
|
+
}, ee = r.overrides ? r.overrides.buildSystemPrompt : () => this.buildSystemPrompt(e, n, r.disabledTools), te = r.overrides ? void 0 : async (e, t) => {
|
|
5690
5711
|
t.triggersSymbolRefresh && r.onSymbolsRefresh && await r.onSymbolsRefresh();
|
|
5691
5712
|
};
|
|
5692
|
-
h = !!(await
|
|
5693
|
-
tools: r.overrides ? r.overrides.tools : i.kind === "overseer" ?
|
|
5713
|
+
h = !!(await u(t, l, d, f, p, g, {
|
|
5714
|
+
tools: r.overrides ? r.overrides.tools : i.kind === "overseer" ? We : i.kind === "architect" ? tr : Ce(fe(Zn().getToolsetForContext("chat"), r.disabledTools), r.extraTools),
|
|
5694
5715
|
maxIterations: r.overrides?.maxIterations ?? 50,
|
|
5695
5716
|
noToolCallReminder: r.overrides?.noToolCallReminder ?? "You must call a tool to proceed. If you are finished, call return_to_user with a summary of what was done. Do not respond with text only.",
|
|
5696
5717
|
terminateOnNoToolCall: r.overrides?.terminateOnNoToolCall,
|
|
5697
|
-
buildSystemPrompt:
|
|
5698
|
-
onToolResult:
|
|
5718
|
+
buildSystemPrompt: ee,
|
|
5719
|
+
onToolResult: te
|
|
5699
5720
|
})).stopped;
|
|
5700
5721
|
} catch (t) {
|
|
5701
5722
|
if (t instanceof DOMException && t.name === "AbortError" || m?.aborted) {
|
|
@@ -5720,7 +5741,7 @@ var tr = Oe("chats", {
|
|
|
5720
5741
|
if (!r) return;
|
|
5721
5742
|
let i = t.llmConnectionStore.connections[n.llmConnectionName] ?? t.llmConnectionStore.connections[r.llmConnectionName] ?? null, a;
|
|
5722
5743
|
if (i) try {
|
|
5723
|
-
a = await
|
|
5744
|
+
a = await le(i, n, "completion");
|
|
5724
5745
|
} catch (e) {
|
|
5725
5746
|
console.error("subchat summarization failed; falling back", e), a = n.messages[n.messages.length - 1]?.content?.trim() || "(no summary available — subchat finished but the auto-summary call failed)";
|
|
5726
5747
|
}
|
|
@@ -5743,7 +5764,7 @@ var tr = Oe("chats", {
|
|
|
5743
5764
|
id: e.id,
|
|
5744
5765
|
name: e.name.replace(/\//g, "."),
|
|
5745
5766
|
alias: ""
|
|
5746
|
-
})) : [], d =
|
|
5767
|
+
})) : [], d = xe({
|
|
5747
5768
|
dataConnectionName: o,
|
|
5748
5769
|
availableConnections: s,
|
|
5749
5770
|
availableConcepts: [],
|
|
@@ -5754,7 +5775,7 @@ var tr = Oe("chats", {
|
|
|
5754
5775
|
});
|
|
5755
5776
|
if (r.kind === "analyst") {
|
|
5756
5777
|
let e = r.parentProjectId || t.projectStore?.activeProjectId || "";
|
|
5757
|
-
return `${(e ? t.projectStore?.projects[e] : null)?.promptOverrides.analyst?.trim() ||
|
|
5778
|
+
return `${(e ? t.projectStore?.projects[e] : null)?.promptOverrides.analyst?.trim() || ze}\n\n${d}`;
|
|
5758
5779
|
}
|
|
5759
5780
|
return d;
|
|
5760
5781
|
},
|
|
@@ -5766,7 +5787,7 @@ var tr = Oe("chats", {
|
|
|
5766
5787
|
type: e.type,
|
|
5767
5788
|
size: (e.contents || "").length
|
|
5768
5789
|
})) : [], o = i?.dataConnectionId && t.connectionStore.connections[i.dataConnectionId] || null;
|
|
5769
|
-
return
|
|
5790
|
+
return er({
|
|
5770
5791
|
projectName: i?.name,
|
|
5771
5792
|
projectDescription: i?.description,
|
|
5772
5793
|
files: a,
|
|
@@ -5790,7 +5811,7 @@ var tr = Oe("chats", {
|
|
|
5790
5811
|
});
|
|
5791
5812
|
}
|
|
5792
5813
|
let s = a ? a.editorIds.map((e) => i?.editors[e]?.name).filter((e) => !!e) : [];
|
|
5793
|
-
return
|
|
5814
|
+
return Ue({
|
|
5794
5815
|
projectName: a?.name,
|
|
5795
5816
|
projectDescription: a?.description,
|
|
5796
5817
|
subchats: o,
|
|
@@ -5803,14 +5824,14 @@ var tr = Oe("chats", {
|
|
|
5803
5824
|
});
|
|
5804
5825
|
//#endregion
|
|
5805
5826
|
//#region llm/editorRefinementRuntime.ts
|
|
5806
|
-
function
|
|
5807
|
-
let { editorStore: n, chatStore: r, llmConnectionStore: i } = t, a = n.editors[e],
|
|
5808
|
-
if (!a || !
|
|
5809
|
-
if (
|
|
5810
|
-
let e = r.chats[
|
|
5811
|
-
if (e && !e.deleted) return
|
|
5827
|
+
function ar(e, t) {
|
|
5828
|
+
let { editorStore: n, chatStore: r, llmConnectionStore: i } = t, a = n.editors[e], s = a?.refinementSession;
|
|
5829
|
+
if (!a || !s) return null;
|
|
5830
|
+
if (s.refinementChatId) {
|
|
5831
|
+
let e = r.chats[s.refinementChatId];
|
|
5832
|
+
if (e && !e.deleted) return s.refinementChatId;
|
|
5812
5833
|
}
|
|
5813
|
-
let
|
|
5834
|
+
let c = new o({
|
|
5814
5835
|
name: `Refine: ${a.name}`,
|
|
5815
5836
|
llmConnectionName: i.activeConnection || "",
|
|
5816
5837
|
dataConnectionName: a.connection,
|
|
@@ -5818,29 +5839,29 @@ function nr(e, t) {
|
|
|
5818
5839
|
sourceRefId: e,
|
|
5819
5840
|
storage: "ephemeral"
|
|
5820
5841
|
});
|
|
5821
|
-
return r.addChat(
|
|
5842
|
+
return r.addChat(c), n.updateRefinementSession(e, { refinementChatId: c.id }), c.id;
|
|
5822
5843
|
}
|
|
5823
5844
|
var Q = /* @__PURE__ */ new Map(), $ = /* @__PURE__ */ new Map();
|
|
5824
|
-
function
|
|
5845
|
+
function or(e) {
|
|
5825
5846
|
let t = $.get(e);
|
|
5826
|
-
return t || (t =
|
|
5847
|
+
return t || (t = me(), $.set(e, t)), t;
|
|
5827
5848
|
}
|
|
5828
|
-
|
|
5849
|
+
rr((e) => {
|
|
5829
5850
|
Q.delete(e), $.delete(e);
|
|
5830
5851
|
});
|
|
5831
|
-
function
|
|
5852
|
+
function sr(e) {
|
|
5832
5853
|
Q.delete(e);
|
|
5833
5854
|
}
|
|
5834
|
-
async function
|
|
5835
|
-
let { editorId: t, message: n, stores: r, deps:
|
|
5836
|
-
if (!
|
|
5837
|
-
let
|
|
5838
|
-
if (!
|
|
5839
|
-
let
|
|
5840
|
-
let e =
|
|
5855
|
+
async function cr(e) {
|
|
5856
|
+
let { editorId: t, message: n, stores: r, deps: i, callbacks: a = {} } = e, { editorStore: o, chatStore: s } = r, c = o.editors[t];
|
|
5857
|
+
if (!c || !c.refinementSession) return;
|
|
5858
|
+
let l = ar(t, r);
|
|
5859
|
+
if (!l) return;
|
|
5860
|
+
let u = !1, d, f = () => {
|
|
5861
|
+
let e = o.editors[t]?.refinementSession;
|
|
5841
5862
|
return e ? {
|
|
5842
|
-
editorType:
|
|
5843
|
-
connectionName:
|
|
5863
|
+
editorType: c.type,
|
|
5864
|
+
connectionName: c.connection,
|
|
5844
5865
|
editorContents: e.currentContent,
|
|
5845
5866
|
selectedText: e.selectedText,
|
|
5846
5867
|
selectionRange: e.selectionRange,
|
|
@@ -5849,7 +5870,7 @@ async function ar(e) {
|
|
|
5849
5870
|
let i = n;
|
|
5850
5871
|
if (r && e.selectionRange) {
|
|
5851
5872
|
let r = e.currentContent.slice(0, e.selectionRange.start), a = e.currentContent.slice(e.selectionRange.end);
|
|
5852
|
-
i = r + n + a,
|
|
5873
|
+
i = r + n + a, o.updateRefinementSession(t, {
|
|
5853
5874
|
currentContent: i,
|
|
5854
5875
|
selectedText: n,
|
|
5855
5876
|
selectionRange: {
|
|
@@ -5857,20 +5878,20 @@ async function ar(e) {
|
|
|
5857
5878
|
end: e.selectionRange.start + n.length
|
|
5858
5879
|
}
|
|
5859
5880
|
});
|
|
5860
|
-
} else
|
|
5861
|
-
|
|
5881
|
+
} else o.updateRefinementSession(t, { currentContent: i });
|
|
5882
|
+
a.onContentChange?.(i, r);
|
|
5862
5883
|
},
|
|
5863
5884
|
onChartConfigChange: (e) => {
|
|
5864
|
-
|
|
5885
|
+
o.updateRefinementSession(t, { currentChartConfig: e }), a.onChartConfigChange?.(e);
|
|
5865
5886
|
},
|
|
5866
5887
|
onFinish: (e) => {
|
|
5867
|
-
|
|
5888
|
+
u = !0, d = e;
|
|
5868
5889
|
},
|
|
5869
|
-
onRunActiveEditorQuery:
|
|
5870
|
-
getCurrentResults: () =>
|
|
5890
|
+
onRunActiveEditorQuery: a.onRunActiveEditorQuery ? () => a.onRunActiveEditorQuery() : void 0,
|
|
5891
|
+
getCurrentResults: () => o.editors[t]?.results ?? null
|
|
5871
5892
|
} : {
|
|
5872
|
-
editorType:
|
|
5873
|
-
connectionName:
|
|
5893
|
+
editorType: c.type,
|
|
5894
|
+
connectionName: c.connection,
|
|
5874
5895
|
editorContents: "",
|
|
5875
5896
|
selectedText: void 0,
|
|
5876
5897
|
selectionRange: void 0,
|
|
@@ -5880,32 +5901,32 @@ async function ar(e) {
|
|
|
5880
5901
|
onFinish: () => {}
|
|
5881
5902
|
};
|
|
5882
5903
|
};
|
|
5883
|
-
await
|
|
5884
|
-
tools:
|
|
5885
|
-
executeToolCall: (e, t) => new
|
|
5904
|
+
await s.executeMessage(l, n, i, { overrides: {
|
|
5905
|
+
tools: p(c.type),
|
|
5906
|
+
executeToolCall: (e, t) => new Se(i.queryExecutionService, i.connectionStore, f(), o, or(l)).executeToolCall(e, t),
|
|
5886
5907
|
buildSystemPrompt: () => {
|
|
5887
|
-
let e = Q.get(
|
|
5908
|
+
let e = Q.get(l);
|
|
5888
5909
|
if (!e) {
|
|
5889
|
-
let n =
|
|
5890
|
-
e =
|
|
5891
|
-
editorType:
|
|
5892
|
-
connectionName:
|
|
5893
|
-
editorContents: n?.currentContent ??
|
|
5910
|
+
let n = o.editors[t]?.refinementSession;
|
|
5911
|
+
e = m({
|
|
5912
|
+
editorType: c.type,
|
|
5913
|
+
connectionName: c.connection,
|
|
5914
|
+
editorContents: n?.currentContent ?? c.contents,
|
|
5894
5915
|
selectedText: n?.selectedText,
|
|
5895
5916
|
selectionRange: n?.selectionRange,
|
|
5896
5917
|
chartConfig: n?.currentChartConfig,
|
|
5897
|
-
completionSymbols:
|
|
5898
|
-
}), Q.set(
|
|
5918
|
+
completionSymbols: c.completionSymbols || []
|
|
5919
|
+
}), Q.set(l, e);
|
|
5899
5920
|
}
|
|
5900
5921
|
return e;
|
|
5901
5922
|
},
|
|
5902
5923
|
maxIterations: 20,
|
|
5903
5924
|
noToolCallReminder: "You must call a tool to proceed. If you are finished with the requested changes, call request_close to wrap up the session. Do not respond with text only."
|
|
5904
|
-
} }),
|
|
5925
|
+
} }), u && o.editors[t]?.refinementSession && o.acceptRefinement(t, { onFinish: () => a.onFinish?.(d) });
|
|
5905
5926
|
}
|
|
5906
|
-
function
|
|
5927
|
+
function lr(e, t) {
|
|
5907
5928
|
let n = t.editorStore.editors[e]?.refinementSession?.refinementChatId;
|
|
5908
|
-
n && (t.chatStore.isChatExecuting(n) && t.chatStore.stopExecution(n), t.chatStore.removeChat(n),
|
|
5929
|
+
n && (t.chatStore.isChatExecuting(n) && t.chatStore.stopExecution(n), t.chatStore.removeChat(n), sr(n), $.delete(n));
|
|
5909
5930
|
}
|
|
5910
5931
|
//#endregion
|
|
5911
|
-
export {
|
|
5932
|
+
export { Ae as A, D as C, E as D, O as E, je as M, ke as N, Pe as O, k as S, Oe as T, nt as _, Zn as a, Le as b, yt as c, N as d, at as f, tt as g, it as h, ir as i, Ne as j, Me as k, vt as l, rt as m, cr as n, Fn as o, ot as p, rr as r, At as s, lr as t, mt as u, et as v, T as w, Ie as x, M as y };
|