udi-yac 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -1
- package/dist/app/UDIChatConfig.d.ts +57 -0
- package/dist/app/UDIChatContext.d.ts +29 -0
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/button.d.ts +3 -2
- package/dist/features/chat/components/ChatPanel.d.ts +6 -1
- package/dist/features/chat/hooks/useApiKey.d.ts +24 -0
- package/dist/features/chat/hooks/useChatApi.d.ts +19 -1
- package/dist/features/dashboard/index.d.ts +1 -0
- package/dist/features/dashboard/types.d.ts +50 -0
- package/dist/features/tool-calls/types.d.ts +8 -0
- package/dist/index.d.ts +3 -2
- package/dist/lib/utils.d.ts +6 -0
- package/dist/udi-yac.css +1 -1
- package/dist/udi-yac.js +6040 -5717
- package/package.json +4 -1
- package/dist/images/yac-mascot.svg +0 -22
package/README.md
CHANGED
|
@@ -80,6 +80,11 @@ import 'udi-yac/style.css';
|
|
|
80
80
|
| `authToken` | `string?` | JWT bearer token for API auth |
|
|
81
81
|
| `requireApiKey` | `boolean?` | Show API key input before chatting |
|
|
82
82
|
| `model` | `string?` | LLM model name override |
|
|
83
|
+
| `downloadActions` | `DownloadAction[]?` | Extra items appended to the Download Data dropdown. See [Custom download actions](#custom-download-actions). |
|
|
84
|
+
| `entityIcons` | `EntityIconMap?` | Icon overrides for entity count chips. See [Custom entity icons](#custom-entity-icons). |
|
|
85
|
+
| `mascot` | `ReactNode \| null?` | Replace or hide the welcome mascot. See [Custom mascot](#custom-mascot). |
|
|
86
|
+
| `splashMessages` | `readonly string[]?` | Override or hide the randomised prompt above the mascot. See [Custom splash messages](#custom-splash-messages). |
|
|
87
|
+
| `onEvent` | `TrackerFn?` | Analytics callback invoked on key user actions. See [Analytics events](#analytics-events). |
|
|
83
88
|
| `className` | `string?` | CSS class for the root element |
|
|
84
89
|
| `style` | `CSSProperties?` | Inline styles for the root element |
|
|
85
90
|
|
|
@@ -215,9 +220,166 @@ See [`src/data/hubmapRemote.ts`](src/data/hubmapRemote.ts) for the canonical inl
|
|
|
215
220
|
### Data Management
|
|
216
221
|
|
|
217
222
|
- **Entity counts**: per-entity row counts with dynamic filtered counts
|
|
218
|
-
- **Download**: filtered data as ZIP of CSVs, or manifest (hubmap_id extraction)
|
|
223
|
+
- **Download**: filtered data as ZIP of CSVs, or manifest (hubmap_id extraction). Consumers can extend the dropdown with custom actions via the `downloadActions` prop — see [Custom download actions](#custom-download-actions).
|
|
219
224
|
- Data package loading with domain computation (Arquero)
|
|
220
225
|
|
|
226
|
+
### Custom download actions
|
|
227
|
+
|
|
228
|
+
Pass `downloadActions` on `UDIChatConfig` to append consumer-specific entries to the Download Data dropdown. Each action's `onClick` receives a snapshot of the current filters and the per-source rows the built-in "Download Raw Data" would have used, so you can export to custom formats, post to an API, or route to another tool.
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { UDIChat } from 'udi-yac';
|
|
232
|
+
import type { DownloadAction } from 'udi-yac';
|
|
233
|
+
|
|
234
|
+
const sendToWorkspaces: DownloadAction = {
|
|
235
|
+
label: 'Open in Workspaces',
|
|
236
|
+
disabled: (ctx) => ctx.rowsBySource.every((r) => r.rows.length === 0),
|
|
237
|
+
onClick: async ({ rowsBySource, filters }) => {
|
|
238
|
+
const ids = rowsBySource.flatMap(({ rows }) =>
|
|
239
|
+
rows.map((r) => String(r['hubmap_id'] ?? '')).filter(Boolean),
|
|
240
|
+
);
|
|
241
|
+
await fetch('/api/workspaces', {
|
|
242
|
+
method: 'POST',
|
|
243
|
+
body: JSON.stringify({ ids, filters }),
|
|
244
|
+
});
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
<UDIChat apiBaseUrl="http://localhost:8007" downloadActions={[sendToWorkspaces]} />;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The `DownloadActionContext` passed to each callback contains:
|
|
252
|
+
|
|
253
|
+
| Field | Type | Notes |
|
|
254
|
+
| -------------- | ----------------------------------- | ------------------------------------------------------------------ |
|
|
255
|
+
| `rowsBySource` | `{ source: string; rows: Row[] }[]` | Post-filter, post-brush rows — same data the built-in ZIP exports. |
|
|
256
|
+
| `filters` | `DataSelections` | Active filter selections keyed by filter id. |
|
|
257
|
+
| `dataPackage` | `DataPackage \| null` | The loaded data package; null until first resolution completes. |
|
|
258
|
+
|
|
259
|
+
Custom actions render after the two built-in items, separated by a divider.
|
|
260
|
+
|
|
261
|
+
### Custom entity icons
|
|
262
|
+
|
|
263
|
+
Pass `entityIcons` on `UDIChatConfig` to change the icon rendered on each entity count chip in the dashboard header. Keys are entity names exactly as they appear in the data package (`resources[].name`). Any component that accepts a `className` prop works — lucide-react icons are typical.
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
import { UDIChat } from 'udi-yac';
|
|
267
|
+
import type { EntityIconMap } from 'udi-yac';
|
|
268
|
+
import { Dna, FlaskConical } from 'lucide-react';
|
|
269
|
+
|
|
270
|
+
const icons: EntityIconMap = {
|
|
271
|
+
// Override the default icon for an existing entity:
|
|
272
|
+
samples: FlaskConical,
|
|
273
|
+
// Add an icon for a custom entity:
|
|
274
|
+
sequencing_runs: Dna,
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
<UDIChat apiBaseUrl="http://localhost:8007" entityIcons={icons} />;
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Consumer entries are merged on top of the built-in icons (`donors`, `samples`, `datasets`, …) — you only need to supply the names you want to override or add. Entities with no match fall back to a generic table icon.
|
|
281
|
+
|
|
282
|
+
### Custom mascot
|
|
283
|
+
|
|
284
|
+
The empty-dashboard welcome splash renders a YAC mascot by default. Consumers can replace it or hide it via the `mascot` prop on `UDIChatConfig`:
|
|
285
|
+
|
|
286
|
+
| Value | Result |
|
|
287
|
+
| ------------------ | ---------------------------------------------------------------------- |
|
|
288
|
+
| `undefined` (omit) | Renders the built-in YAC mascot image. |
|
|
289
|
+
| `null` | Hides the mascot entirely. The speech-bubble prompt above still shows. |
|
|
290
|
+
| Any `ReactNode` | Renders the provided node in place of the mascot image. |
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
import { UDIChat } from 'udi-yac';
|
|
294
|
+
|
|
295
|
+
// Replace with a custom image:
|
|
296
|
+
<UDIChat
|
|
297
|
+
apiBaseUrl="http://localhost:8007"
|
|
298
|
+
mascot={<img src="/my-mascot.svg" alt="" className="w-60 h-60 object-contain" />}
|
|
299
|
+
/>
|
|
300
|
+
|
|
301
|
+
// Hide entirely:
|
|
302
|
+
<UDIChat apiBaseUrl="http://localhost:8007" mascot={null} />
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Custom splash messages
|
|
306
|
+
|
|
307
|
+
One prompt is picked at random from a built-in pool (`"Ask me for a visualization!"`, `"What data would you like to explore?"`, etc.) and shown in the speech bubble above the mascot. Override via `splashMessages` on `UDIChatConfig`:
|
|
308
|
+
|
|
309
|
+
| Value | Result |
|
|
310
|
+
| ------------------ | ------------------------------------------------------ |
|
|
311
|
+
| `undefined` (omit) | Random pick from the built-in defaults. |
|
|
312
|
+
| Non-empty array | Random pick from the provided strings exclusively. |
|
|
313
|
+
| `[]` | Hides the speech bubble entirely (mascot still shows). |
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
<UDIChat
|
|
317
|
+
apiBaseUrl="http://localhost:8007"
|
|
318
|
+
splashMessages={[
|
|
319
|
+
'Ask me about donors, samples, or datasets.',
|
|
320
|
+
'Try “average age by sex”.',
|
|
321
|
+
]}
|
|
322
|
+
/>
|
|
323
|
+
|
|
324
|
+
// Hide the speech bubble:
|
|
325
|
+
<UDIChat apiBaseUrl="http://localhost:8007" splashMessages={[]} />
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
The selection is made once per `UDIChat` mount, so the message doesn't flicker between renders.
|
|
329
|
+
|
|
330
|
+
### Analytics events
|
|
331
|
+
|
|
332
|
+
Pass an `onEvent` callback on `UDIChatConfig` to receive events for key user actions. The signature —
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
type TrackerFn = (name: string, properties?: Record<string, unknown>) => void;
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
— matches the call shape of every major analytics tool, so it can usually be forwarded directly:
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
import { UDIChat } from 'udi-yac';
|
|
342
|
+
import type { TrackerFn } from 'udi-yac';
|
|
343
|
+
|
|
344
|
+
// Google Analytics 4 (via gtag):
|
|
345
|
+
const track: TrackerFn = (name, props) => window.gtag?.('event', name, props);
|
|
346
|
+
|
|
347
|
+
// Segment / Amplitude / PostHog:
|
|
348
|
+
// const track: TrackerFn = (name, props) => window.analytics?.track(name, props);
|
|
349
|
+
|
|
350
|
+
<UDIChat apiBaseUrl="http://localhost:8007" onEvent={track} />;
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Event names are stable, snake_case strings. **Properties carry metadata only — never raw message content or OpenAI key material.** If the callback throws, the error is swallowed so an analytics failure never breaks the chat.
|
|
354
|
+
|
|
355
|
+
**Correlation ids.** Every event carries a `sessionId` (minted once per `UDIChat` mount) so all events from one chat instance can be stitched together and two tabs can be told apart. A subset of events — the ones bound to a single send↔response round trip — also carries a shared `turnId`, making it trivial to match a `message_sent` to the `response_received`, `rebuff_received`, or `request_failed` it produced. A retry after entering an API key reuses the original `turnId` so the retry's response still pairs back to the originating send.
|
|
356
|
+
|
|
357
|
+
| Event | Fired when | Properties |
|
|
358
|
+
| -------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
359
|
+
| `message_sent` | User submits a message through the chat input | `turnId: string`<br>`charCount: number` — length of the text, not the text itself<br>`conversationLength: number` — messages already in the conversation when this one was added<br>`hasUserApiKey: boolean` — whether the request will carry the user's key |
|
|
360
|
+
| `response_received` | Server completes a `/v1/yac/completions` call (including rebuffs) | `turnId: string` — matches the originating `message_sent`<br>`durationMs: number`<br>`toolCallNames: string[]` — e.g. `["RenderVisualization", "FreeTextExplain"]`<br>`toolCallCount: number`<br>`hadUserKey: boolean`<br>`hasRebuff: boolean` |
|
|
361
|
+
| `rebuff_received` | The response contains a `Rebuff` tool_call | `turnId: string`<br>`reason?: string` — machine-readable discriminator; `"budget_exceeded"` for quota rebuffs, absent for ordinary rebuffs<br>`hadUserKey: boolean` |
|
|
362
|
+
| `request_failed` | `/v1/yac/completions` throws (network error, HTTP !ok) | `turnId: string`<br>`durationMs: number`<br>`hadUserKey: boolean` |
|
|
363
|
+
| `api_key_set` | User submits a key through the `ApiKeyInput` | `inResponseToQuota: boolean` — true if a budget-exceeded rebuff had triggered the prompt (distinguishes first-time entry from quota-recovery entry) |
|
|
364
|
+
| `api_key_cleared` | User clears their stored key via the header icon | _(none)_ |
|
|
365
|
+
| `conversation_reset` | User clicks the Reset button (clears messages, pinned viz, filters, memory bank) | `conversationLength: number` — message count at the moment of reset |
|
|
366
|
+
| `visualization_pinned` | A new visualization is auto-pinned from an assistant response | `hasTitle: boolean`<br>`toolCallIndex: number` — position within the assistant message's tool_calls |
|
|
367
|
+
| `visualization_closed` | User clicks the × on a pinned visualization card | `hasTitle: boolean` |
|
|
368
|
+
| `download_raw_data` | User clicks "Download Raw Data" in the Download Data dropdown | `sources: number` — count of sources contributing rows<br>`rowsTotal: number` |
|
|
369
|
+
| `download_manifest` | User clicks "Download Manifest" | `idsTotal: number` — count of `hubmap_id` values in the exported manifest |
|
|
370
|
+
| `download_<slug>` | User clicks a custom `downloadActions` entry | `label: string` — the original menu label |
|
|
371
|
+
| `filter_range_changed` | User commits a new range on a quantitative (interval) filter slider, including the reset button | `entity: string`<br>`field: string`<br>`isReset: boolean` — true when triggered by the reset button<br>`isFullRange: boolean` — true when the new range covers the full domain |
|
|
372
|
+
| `filter_selection_changed` | User toggles a checkbox or clicks "Clear all" on a categorical (point) filter | `entity: string`<br>`field: string`<br>`action: 'toggle' \| 'clear_all'`<br>`checked?: boolean` — present only when `action === 'toggle'`<br>`selectionCount: number` — count of selected values after the change (no values themselves) |
|
|
373
|
+
| `filter_entity_changed` | User changes the target entity on a tweakable filter card | `filterType: 'interval' \| 'point'`<br>`entity: string` — the newly selected entity<br>`field: string` — the field carried over to the new entity (may be empty) |
|
|
374
|
+
| `filter_field_changed` | User changes the target field on a tweakable filter card | `filterType: 'interval' \| 'point'`<br>`entity: string`<br>`field: string` — the newly selected field |
|
|
375
|
+
| `visualization_tweaked` | User swaps the field bound to an encoding via a `VizTweakComponent` dropdown | `encoding: string` — the encoding channel (e.g. `"x"`, `"color"`) |
|
|
376
|
+
|
|
377
|
+
Every row in the table above also includes `sessionId: string`; it's omitted from the per-event cells to keep the table scannable.
|
|
378
|
+
|
|
379
|
+
**Custom download slug.** The suffix is derived from the action's `label`: a leading `"Download "` is stripped, the rest is lowercased and non-alphanumeric runs are replaced with `_`. So a `{ label: "Download All TSVs" }` action emits `download_all_tsvs`; `{ label: "Open in Workspaces" }` emits `download_open_in_workspaces`. The original label is always echoed back in `properties.label` so consumers can distinguish collisions.
|
|
380
|
+
|
|
381
|
+
**Properties you will _not_ see.** By design, the following never cross the tracker boundary: raw message text, tool_call `arguments`, OpenAI API keys, data rows, filter values. Only counts, booleans, tool-call names, ids, and short slug strings are emitted.
|
|
382
|
+
|
|
221
383
|
### Debug Mode (type `!/admin` in chat)
|
|
222
384
|
|
|
223
385
|
- System prompts toggle (show/hide system messages)
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
1
2
|
import { DataPackage, DataFieldDomain } from '../types/dataPackage';
|
|
3
|
+
import { DownloadAction, EntityIconMap } from '../features/dashboard';
|
|
4
|
+
/**
|
|
5
|
+
* Signature for the optional analytics callback — deliberately untyped in
|
|
6
|
+
* the `properties` bag so consumers can forward directly to GA4, Segment,
|
|
7
|
+
* Amplitude, PostHog, or any other tool that accepts
|
|
8
|
+
* `(eventName, properties)`.
|
|
9
|
+
*
|
|
10
|
+
* Event names are stable, snake_case strings. Properties carry metadata
|
|
11
|
+
* only — never raw message content or API key material.
|
|
12
|
+
*/
|
|
13
|
+
export type TrackerFn = (name: string, properties?: Record<string, unknown>) => void;
|
|
2
14
|
/**
|
|
3
15
|
* Public configuration surface for the `UDIChat` root component. Extracted
|
|
4
16
|
* from UDIChat.tsx so that `validateConfig` and other app-layer utilities
|
|
@@ -18,6 +30,51 @@ export interface UDIChatConfig {
|
|
|
18
30
|
/** If true, prompt the user to enter an OpenAI API key before chatting. */
|
|
19
31
|
requireApiKey?: boolean;
|
|
20
32
|
model?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Extra items to append to the Download Data dropdown. Each action's
|
|
35
|
+
* `onClick` receives a {@link DownloadActionContext} snapshot of the
|
|
36
|
+
* current filters and per-source rows, so consumers can export, route,
|
|
37
|
+
* or post-process that data in consumer-specific ways. Rendered after
|
|
38
|
+
* the built-in "Download Raw Data" and "Download Manifest" items,
|
|
39
|
+
* separated by a divider.
|
|
40
|
+
*/
|
|
41
|
+
downloadActions?: readonly DownloadAction[];
|
|
42
|
+
/**
|
|
43
|
+
* Map from entity name (as it appears in the data package `resources[].name`)
|
|
44
|
+
* to an icon component. Merged on top of the built-in icons (donors,
|
|
45
|
+
* samples, datasets, …) with consumer entries winning, so you can supply
|
|
46
|
+
* icons for additional entities or override the defaults. Any component
|
|
47
|
+
* that accepts a `className` prop works — lucide-react icons are typical.
|
|
48
|
+
*/
|
|
49
|
+
entityIcons?: EntityIconMap;
|
|
50
|
+
/**
|
|
51
|
+
* Controls the mascot rendered on the empty-dashboard splash:
|
|
52
|
+
* - `undefined` (prop omitted): the default YAC mascot image is rendered.
|
|
53
|
+
* - `null`: the mascot is hidden entirely.
|
|
54
|
+
* - any other ReactNode: the provided node is rendered in place of the mascot.
|
|
55
|
+
*
|
|
56
|
+
* The speech-bubble prompt above the mascot is not affected by this prop.
|
|
57
|
+
*/
|
|
58
|
+
mascot?: ReactNode | null;
|
|
59
|
+
/**
|
|
60
|
+
* Override the randomised prompts shown in the speech bubble above the
|
|
61
|
+
* mascot on the empty-dashboard splash.
|
|
62
|
+
* - `undefined` (prop omitted): use the built-in defaults.
|
|
63
|
+
* - A non-empty array: pick one of the provided messages at random.
|
|
64
|
+
* - An empty array (`[]`): hide the speech bubble entirely.
|
|
65
|
+
*/
|
|
66
|
+
splashMessages?: readonly string[];
|
|
67
|
+
/**
|
|
68
|
+
* Optional analytics callback. When provided, UDIChat emits events for
|
|
69
|
+
* key user actions (messages sent, responses received, rebuffs, API key
|
|
70
|
+
* changes, visualization pin/close, conversation reset, downloads).
|
|
71
|
+
*
|
|
72
|
+
* Callbacks carry metadata only — never raw message content or OpenAI
|
|
73
|
+
* key material — and thrown errors are swallowed so an analytics failure
|
|
74
|
+
* never breaks the chat. See the "Analytics events" section of the
|
|
75
|
+
* README for the full event catalog and the properties each one emits.
|
|
76
|
+
*/
|
|
77
|
+
onEvent?: TrackerFn;
|
|
21
78
|
className?: string;
|
|
22
79
|
style?: React.CSSProperties;
|
|
23
80
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { StoreApi } from 'zustand';
|
|
3
|
+
import { DownloadAction, EntityIconMap } from '../features/dashboard';
|
|
4
|
+
import { TrackerFn } from './UDIChatConfig';
|
|
3
5
|
import { ConversationState } from '../features/chat/stores/conversationStore';
|
|
4
6
|
import { DashboardState } from '../features/dashboard/stores/dashboardStore';
|
|
5
7
|
import { DataPackageState } from '../features/data-package/stores/dataPackageStore';
|
|
@@ -24,3 +26,30 @@ export declare function useMemoryBank<T>(selector: (state: MemoryBankState) => T
|
|
|
24
26
|
export declare function useMemoryBankStore(): StoreApi<MemoryBankState>;
|
|
25
27
|
export declare function useGlobal<T>(selector: (state: GlobalState) => T): T;
|
|
26
28
|
export declare function useGlobalStore(): StoreApi<GlobalState>;
|
|
29
|
+
export declare function DownloadActionsProvider({ actions, children, }: {
|
|
30
|
+
actions: readonly DownloadAction[] | undefined;
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
export declare function useDownloadActions(): readonly DownloadAction[];
|
|
34
|
+
export declare function EntityIconsProvider({ icons, children, }: {
|
|
35
|
+
icons: EntityIconMap | undefined;
|
|
36
|
+
children: ReactNode;
|
|
37
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
export declare function useEntityIcons(): EntityIconMap;
|
|
39
|
+
type MascotValue = ReactNode | null | undefined;
|
|
40
|
+
export declare function MascotProvider({ mascot, children }: {
|
|
41
|
+
mascot: MascotValue;
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
export declare function useMascot(): MascotValue;
|
|
45
|
+
export declare function SplashMessagesProvider({ messages, children, }: {
|
|
46
|
+
messages: readonly string[] | undefined;
|
|
47
|
+
children: ReactNode;
|
|
48
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
49
|
+
export declare function useSplashMessages(): readonly string[] | undefined;
|
|
50
|
+
export declare function TrackerProvider({ onEvent, children, }: {
|
|
51
|
+
onEvent: TrackerFn | undefined;
|
|
52
|
+
children: ReactNode;
|
|
53
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
export declare function useTracker(): TrackerFn;
|
|
55
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRender } from '@base-ui/react/use-render';
|
|
2
2
|
import { VariantProps } from 'class-variance-authority';
|
|
3
3
|
declare const badgeVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "
|
|
4
|
+
variant?: "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | null | undefined;
|
|
5
5
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
6
6
|
declare function Badge({ className, variant, render, ...props }: useRender.ComponentProps<'span'> & VariantProps<typeof badgeVariants>): import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>>;
|
|
7
7
|
export { Badge, badgeVariants };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Button as ButtonPrimitive } from '@base-ui/react/button';
|
|
2
2
|
import { VariantProps } from 'class-variance-authority';
|
|
3
3
|
declare const buttonVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "
|
|
4
|
+
variant?: "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | null | undefined;
|
|
5
5
|
size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
|
|
6
6
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
7
|
-
|
|
7
|
+
type ButtonProps = ButtonPrimitive.Props & VariantProps<typeof buttonVariants>;
|
|
8
|
+
declare const Button: import('react').ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & import('react').RefAttributes<HTMLButtonElement>>;
|
|
8
9
|
export { Button, buttonVariants };
|
|
@@ -3,11 +3,16 @@ interface ChatPanelProps {
|
|
|
3
3
|
config: QueryConfig;
|
|
4
4
|
needsApiKey: boolean;
|
|
5
5
|
hasApiKey: boolean;
|
|
6
|
+
userKeyQuotaExceeded: boolean;
|
|
7
|
+
pendingQuotaRetry: boolean;
|
|
6
8
|
onSetApiKey: (key: string) => void;
|
|
7
9
|
onClearApiKey: () => void;
|
|
10
|
+
onQuotaRebuff: (hadUserKey: boolean) => void;
|
|
11
|
+
onNormalResponse: () => void;
|
|
12
|
+
onConsumePendingRetry: () => void;
|
|
8
13
|
showDrawerToggle?: boolean;
|
|
9
14
|
drawerOpen?: boolean;
|
|
10
15
|
onToggleDrawer?: () => void;
|
|
11
16
|
}
|
|
12
|
-
export declare function ChatPanel({ config, needsApiKey, hasApiKey, onSetApiKey, onClearApiKey, showDrawerToggle, onToggleDrawer, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function ChatPanel({ config, needsApiKey, hasApiKey, userKeyQuotaExceeded, pendingQuotaRetry, onSetApiKey, onClearApiKey, onQuotaRebuff, onNormalResponse, onConsumePendingRetry, showDrawerToggle, onToggleDrawer, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
13
18
|
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owns all state related to the user-supplied OpenAI API key: the key
|
|
3
|
+
* itself (persisted to localStorage), the two quota flags that react to
|
|
4
|
+
* budget-exceeded rebuffs from the server, and the one-shot retry signal
|
|
5
|
+
* that fires after the user submits a key in response to a quota event.
|
|
6
|
+
*
|
|
7
|
+
* The retry effect itself lives in `ChatPanel`, because it needs
|
|
8
|
+
* `retryLastUserMessage` from `useChatApi` — see `pendingQuotaRetry` /
|
|
9
|
+
* `consumePendingRetry` for the hand-off.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useApiKey({ requireApiKey }: {
|
|
12
|
+
requireApiKey: boolean;
|
|
13
|
+
}): {
|
|
14
|
+
openAiKey: string | null;
|
|
15
|
+
needsApiKey: boolean;
|
|
16
|
+
hasApiKey: boolean;
|
|
17
|
+
userKeyQuotaExceeded: boolean;
|
|
18
|
+
pendingQuotaRetry: boolean;
|
|
19
|
+
setApiKey: (key: string) => void;
|
|
20
|
+
clearApiKey: () => void;
|
|
21
|
+
onQuotaRebuff: (hadUserKey: boolean) => void;
|
|
22
|
+
onNormalResponse: () => void;
|
|
23
|
+
consumePendingRetry: () => void;
|
|
24
|
+
};
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { QueryConfig } from '../api/completions';
|
|
2
|
-
|
|
2
|
+
interface UseChatApiOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Fired when the latest assistant response is a budget-exceeded rebuff
|
|
5
|
+
* (server returns a `Rebuff` tool_call with `arguments.reason ===
|
|
6
|
+
* "budget_exceeded"`). `hadUserKey` is true iff `config.openAiKey` was
|
|
7
|
+
* present on the request, which lets the parent distinguish the
|
|
8
|
+
* server-key-exhausted case from user-key-exhausted.
|
|
9
|
+
*/
|
|
10
|
+
onQuotaRebuff?: (hadUserKey: boolean) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Fired on any successful response that is NOT a budget-exceeded rebuff,
|
|
13
|
+
* so the parent can clear a stale quota flag — e.g. after an admin
|
|
14
|
+
* refills the server key, the prompt should go away on its own.
|
|
15
|
+
*/
|
|
16
|
+
onNormalResponse?: () => void;
|
|
17
|
+
}
|
|
18
|
+
export declare function useChatApi(config: QueryConfig, options?: UseChatApiOptions): {
|
|
3
19
|
sendMessage: (text: string) => Promise<void>;
|
|
20
|
+
retryLastUserMessage: () => Promise<void>;
|
|
4
21
|
isLoading: boolean;
|
|
5
22
|
error: string | null;
|
|
6
23
|
};
|
|
24
|
+
export {};
|
|
@@ -8,3 +8,4 @@ export { createDashboardStore, extractAllUdiSpecsFromMessage, type DashboardStat
|
|
|
8
8
|
export { createDataFiltersStore, extractFilterSpecFromMessage, messageFilterKeyWithToolCall, messageFilterKey, type DataFiltersState, type DataSelection, type DataSelections, } from './stores/dataFiltersStore';
|
|
9
9
|
export { createSelectionsStore, type SelectionsState } from './stores/selectionsStore';
|
|
10
10
|
export { createMemoryBankStore, type MemoryBankState } from './stores/memoryBankStore';
|
|
11
|
+
export type { DownloadAction, DownloadActionContext, EntityIconComponent, EntityIconMap, } from './types';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { DataPackage, Row } from '../../types/dataPackage';
|
|
3
|
+
import { DataSelections } from './stores/dataFiltersStore';
|
|
4
|
+
/**
|
|
5
|
+
* An icon component suitable for the entity count chips. Any component that
|
|
6
|
+
* accepts a `className` prop and renders an icon works — lucide-react icons
|
|
7
|
+
* are the typical choice.
|
|
8
|
+
*/
|
|
9
|
+
export type EntityIconComponent = ComponentType<{
|
|
10
|
+
className?: string;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* Consumer-provided mapping from entity name to icon. Merged on top of the
|
|
14
|
+
* built-in defaults (donors, samples, datasets, …) so consumers can supply
|
|
15
|
+
* icons for additional entities or override existing ones.
|
|
16
|
+
*/
|
|
17
|
+
export type EntityIconMap = Record<string, EntityIconComponent>;
|
|
18
|
+
/**
|
|
19
|
+
* Snapshot of dashboard state passed to a {@link DownloadAction}'s callbacks.
|
|
20
|
+
* The consumer reads this to decide what to export / where to send it.
|
|
21
|
+
*/
|
|
22
|
+
export interface DownloadActionContext {
|
|
23
|
+
/**
|
|
24
|
+
* Per-source rows reflecting the currently applied filters and brush
|
|
25
|
+
* selections — the same data the built-in "Download Raw Data" action uses.
|
|
26
|
+
*/
|
|
27
|
+
rowsBySource: {
|
|
28
|
+
source: string;
|
|
29
|
+
rows: Row[];
|
|
30
|
+
}[];
|
|
31
|
+
/** Active filter selections, keyed by filter id. */
|
|
32
|
+
filters: DataSelections;
|
|
33
|
+
/** The loaded data package, or null if it hasn't resolved yet. */
|
|
34
|
+
dataPackage: DataPackage | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Consumer-configurable item appended to the Download Data dropdown.
|
|
38
|
+
* Custom items render after the built-in actions, separated by a divider.
|
|
39
|
+
*/
|
|
40
|
+
export interface DownloadAction {
|
|
41
|
+
/** Text shown in the menu item. */
|
|
42
|
+
label: string;
|
|
43
|
+
/** Invoked when the menu item is clicked. May be async. */
|
|
44
|
+
onClick: (ctx: DownloadActionContext) => void | Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* When `true` (or a function returning `true`), the item is rendered as
|
|
47
|
+
* disabled. Defaults to `false`.
|
|
48
|
+
*/
|
|
49
|
+
disabled?: boolean | ((ctx: DownloadActionContext) => boolean);
|
|
50
|
+
}
|
|
@@ -5,9 +5,17 @@
|
|
|
5
5
|
* ORCHESTRATOR_TOOLS list and represent the payload the frontend receives
|
|
6
6
|
* inside `tool_calls[].function.arguments`.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* Machine-readable rebuff discriminator. Emitted by the backend only for
|
|
10
|
+
* cases the frontend needs to branch on (e.g. prompt the user for their
|
|
11
|
+
* own API key when the server key is over quota). Ordinary rebuffs omit
|
|
12
|
+
* the field — callers should treat absence as "plain rebuff".
|
|
13
|
+
*/
|
|
14
|
+
export type RebuffReason = 'budget_exceeded';
|
|
8
15
|
export interface RebuffArgs {
|
|
9
16
|
message: string;
|
|
10
17
|
suggestions: string[];
|
|
18
|
+
reason?: RebuffReason;
|
|
11
19
|
}
|
|
12
20
|
export interface ClarifyCandidate {
|
|
13
21
|
field_name: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { UDIChat } from './app/UDIChat';
|
|
2
|
-
export type { UDIChatConfig } from './app/UDIChatConfig';
|
|
3
|
-
export type { DataPackage, DataPackageResource, DataFieldDomain, IntervalDomain, CategoricalDomain, } from './types/dataPackage';
|
|
2
|
+
export type { UDIChatConfig, TrackerFn } from './app/UDIChatConfig';
|
|
3
|
+
export type { DataPackage, DataPackageResource, DataFieldDomain, IntervalDomain, CategoricalDomain, Row, } from './types/dataPackage';
|
|
4
|
+
export type { DownloadAction, DownloadActionContext, EntityIconComponent, EntityIconMap, } from './features/dashboard';
|
|
4
5
|
export { joinDataPath } from './features/data-package/utils/joinDataPath';
|
|
5
6
|
export type { LoadingPhase } from './features/data-package/stores/dataPackageStore';
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import { ClassValue } from 'clsx';
|
|
2
2
|
export declare function cn(...inputs: ClassValue[]): string;
|
|
3
|
+
/**
|
|
4
|
+
* Short unique ID suitable for analytics correlation (sessionId, turnId).
|
|
5
|
+
* Prefers `crypto.randomUUID` when available; falls back to a
|
|
6
|
+
* timestamp+random concatenation for environments without WebCrypto.
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateEventId(): string;
|