@lightdash/cli 3.124.0 → 3.126.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -208,6 +208,14 @@ order, `sortBy` describes result ordering, `totalColumnCount` exposes truncation
|
|
|
208
208
|
plain text. `result` is the object `useLightdash` returns (keep a reference
|
|
209
209
|
to it even when destructuring), never a rebuilt `{ data, columns }`. Hide the control when `available` is false; trigger from a user
|
|
210
210
|
action. Full contract in `/app/references/ai-analysis.md`.
|
|
211
|
+
- `shared-ai-commentary`: `useSharedAiCommentary({ selectionKey, prompt,
|
|
212
|
+
sources, focus? })` saves a generated answer in shared app state keyed to
|
|
213
|
+
the current selection: the first viewer generates, later viewers of the
|
|
214
|
+
same selection read the saved text, anyone may edit it (`saveEdit`), and
|
|
215
|
+
the edited version is what others see. Build `selectionKey` with
|
|
216
|
+
`commentarySelectionKey(...)` from bounded controls (filters, tabs), never
|
|
217
|
+
row data. Needs both AI and saved app state available. See "Saved
|
|
218
|
+
commentary shared between viewers" in `/app/references/ai-analysis.md`.
|
|
211
219
|
- `space-charts`: `useSpaceCharts('<space slug or uuid>')` returns
|
|
212
220
|
`{ available, space, charts, loading, error, refresh }` — the saved charts
|
|
213
221
|
the viewer can see in that space, resolved at runtime with the viewer's own
|
|
@@ -234,6 +234,76 @@ function RevenueTakeaway() {
|
|
|
234
234
|
- Trigger `ask` from a user action, or at most once per loaded view. Never on a timer, never per row. Prompts are rate limited per viewer.
|
|
235
235
|
- Prompts are capped at 2000 characters; write them as instructions to an analyst, and name the audience and the length you want.
|
|
236
236
|
|
|
237
|
+
## Saved commentary shared between viewers (`useSharedAiCommentary`)
|
|
238
|
+
|
|
239
|
+
> Use this when the user wants generated commentary to be **saved** — "save the summary for the team", "everyone should see the same commentary", "let people edit the AI text", "keep the narrative with this view". It needs BOTH AI analysis and saved app state available (see the prompt's availability lines); without either, fall back to `useAiPrompt` (no saving) or plain `useSharedAppState` (no generation).
|
|
240
|
+
|
|
241
|
+
`useAiPrompt` answers one viewer once; `useSharedAiCommentary` keys the answer to the current selection and stores it in shared app state, so the first viewer generates it, later viewers of the same selection read the saved text, and anyone may edit it. The edited text is what later viewers see.
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import {
|
|
245
|
+
commentarySelectionKey,
|
|
246
|
+
useLightdash,
|
|
247
|
+
useSharedAiCommentary,
|
|
248
|
+
} from '@lightdash/query-sdk';
|
|
249
|
+
|
|
250
|
+
function RegionCommentary({ region, period }: { region: string; period: string }) {
|
|
251
|
+
const revenue = useLightdash(revenueQuery);
|
|
252
|
+
const c = useSharedAiCommentary({
|
|
253
|
+
// Same selection => same saved commentary, for every viewer.
|
|
254
|
+
selectionKey: commentarySelectionKey('region', region, 'period', period),
|
|
255
|
+
prompt: `In three sentences, what should a sales lead know about ${region} revenue for ${period}?`,
|
|
256
|
+
sources: [{ result: revenue, label: 'Revenue' }],
|
|
257
|
+
});
|
|
258
|
+
const [draft, setDraft] = useState<string | null>(null);
|
|
259
|
+
|
|
260
|
+
if (c.meta.loading || revenue.loading) return null;
|
|
261
|
+
return (
|
|
262
|
+
<section>
|
|
263
|
+
{c.commentary ? (
|
|
264
|
+
draft === null ? (
|
|
265
|
+
<>
|
|
266
|
+
<p>{c.commentary.text}</p>
|
|
267
|
+
<footer className="text-xs text-muted-foreground">
|
|
268
|
+
{c.commentary.edited
|
|
269
|
+
? `Edited by ${c.meta.updatedBy?.name ?? 'a viewer'}`
|
|
270
|
+
: 'AI-generated'}
|
|
271
|
+
</footer>
|
|
272
|
+
<button onClick={() => setDraft(c.commentary!.text)}>Edit</button>
|
|
273
|
+
{c.canGenerate && (
|
|
274
|
+
<button disabled={c.generating} onClick={() => c.generate()}>
|
|
275
|
+
Regenerate
|
|
276
|
+
</button>
|
|
277
|
+
)}
|
|
278
|
+
</>
|
|
279
|
+
) : (
|
|
280
|
+
<>
|
|
281
|
+
<textarea value={draft} onChange={(e) => setDraft(e.target.value)} />
|
|
282
|
+
<button onClick={() => { c.saveEdit(draft); setDraft(null); }}>Save</button>
|
|
283
|
+
<button onClick={() => setDraft(null)}>Cancel</button>
|
|
284
|
+
</>
|
|
285
|
+
)
|
|
286
|
+
) : (
|
|
287
|
+
c.canGenerate && (
|
|
288
|
+
<button disabled={c.generating} onClick={() => c.generate()}>
|
|
289
|
+
{c.generating ? 'Generating…' : 'Generate commentary'}
|
|
290
|
+
</button>
|
|
291
|
+
)
|
|
292
|
+
)}
|
|
293
|
+
</section>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Rules:
|
|
299
|
+
|
|
300
|
+
- **`selectionKey` names the selection, not the data**: build it with `commentarySelectionKey(...)` from the filter values, segment ids or tab names that define what the viewer is looking at. Never include row values or timestamps. One key per distinct selection; shared app state allows 200 keys per app, so key on controls with bounded choices (a region picker, a period), not free-text search terms.
|
|
301
|
+
- `prompt`, `sources` and `focus` work exactly as in `useAiPrompt`; the generated text is saved with the prompt and a `promptId` for provenance, `edited: false`.
|
|
302
|
+
- Generation replaces any saved text (the Regenerate case) — trigger it from a click, never on load. Reading saved commentary is free and works even when `canGenerate` is false, so always render saved text regardless.
|
|
303
|
+
- Label the text "AI-generated" until `commentary.edited` is true; then show who edited it from `meta.updatedBy`.
|
|
304
|
+
- The saved text is visible to **every** viewer of the app, including viewers whose data permissions would show them different rows. That is the point of the feature — it behaves like a comment — but do not build it over data some viewers must not see described in words.
|
|
305
|
+
- Storage errors surface on `meta.error`; generation errors on `error`. Handle both like any other state.
|
|
306
|
+
|
|
237
307
|
## What the app must not do
|
|
238
308
|
|
|
239
309
|
- Do not send rows or field values anywhere. The hooks expose results; Lightdash already has the data.
|
|
@@ -781,7 +781,7 @@ For any external HTTP API call, read `/app/references/external-apis.md` and use
|
|
|
781
781
|
|
|
782
782
|
### AI analysis — executive summary and anomalies
|
|
783
783
|
|
|
784
|
-
When the user asks for AI analysis, an executive summary, "what changed and why", anomaly callouts, or any AI-generated insight about the data on the page, read `/app/references/ai-analysis.md` and build its **Default build** with the shipped components in `src/components/insights/` (`InsightsSummary`, `InsightMarker` / `insightCellProps`, `InvestigateMenuItem`, `InvestigationCard`): summary block at the top, markers on every query-bound chart, Investigate in every point menu, the investigation card under the chart. A short ask gets the whole set; only a narrower ask ("just a summary") gets a subset. Never rewrite those components. Lightdash runs the analysis over the queries the app already loaded and pushes the result in; the app renders it and never sends rows or keys. The components render nothing when the host has no analysis to offer. For an author-written question over loaded results (a takeaway under a chart, "explain this row"), use `useAiPrompt` from the same reference. When the user wants the summaries written a certain way (language, currency, domain vocabulary), pass it via the `instructions` prop on `InsightsSummary` — see "Author instructions" in the same reference. When the user wants the analysis to run as soon as the app loads, set the `analyseOnLoad` prop on `InsightsSummary` — see "Analyse on load" in the same reference; never set it unasked.
|
|
784
|
+
When the user asks for AI analysis, an executive summary, "what changed and why", anomaly callouts, or any AI-generated insight about the data on the page, read `/app/references/ai-analysis.md` and build its **Default build** with the shipped components in `src/components/insights/` (`InsightsSummary`, `InsightMarker` / `insightCellProps`, `InvestigateMenuItem`, `InvestigationCard`): summary block at the top, markers on every query-bound chart, Investigate in every point menu, the investigation card under the chart. A short ask gets the whole set; only a narrower ask ("just a summary") gets a subset. Never rewrite those components. Lightdash runs the analysis over the queries the app already loaded and pushes the result in; the app renders it and never sends rows or keys. The components render nothing when the host has no analysis to offer. For an author-written question over loaded results (a takeaway under a chart, "explain this row"), use `useAiPrompt` from the same reference. When the user wants the summaries written a certain way (language, currency, domain vocabulary), pass it via the `instructions` prop on `InsightsSummary` — see "Author instructions" in the same reference. When the user wants the analysis to run as soon as the app loads, set the `analyseOnLoad` prop on `InsightsSummary` — see "Analyse on load" in the same reference; never set it unasked. When the user wants generated commentary **saved and shared** — "save the summary for the team", "everyone sees the same commentary", "let people edit the AI text" — and the prompt says saved app state is available, use `useSharedAiCommentary` from the same reference ("Saved commentary shared between viewers") instead of `useAiPrompt`; it keys the text to the current selection so later viewers read and edit the saved version instead of regenerating.
|
|
785
785
|
|
|
786
786
|
## Visual Design
|
|
787
787
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.126.0",
|
|
4
4
|
"description": "Lightdash CLI tool",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"@actions/core": "1.11.1",
|
|
23
23
|
"@aws-sdk/credential-providers": "3.995.0",
|
|
24
24
|
"@casl/ability": "6.8.0",
|
|
25
|
-
"@lightdash/common": "3.
|
|
26
|
-
"@lightdash/warehouses": "3.
|
|
25
|
+
"@lightdash/common": "3.126.0",
|
|
26
|
+
"@lightdash/warehouses": "3.126.0",
|
|
27
27
|
"@sidvind/better-ajv-errors": "7.0.0",
|
|
28
28
|
"@types/columnify": "1.5.1",
|
|
29
29
|
"ajv": "8.18.0",
|