@dbx-tools/genie-shared 0.1.20 → 0.1.21
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 +206 -0
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# @dbx-tools/genie-shared
|
|
2
|
+
|
|
3
|
+
Pure types and sync helpers for `@dbx-tools/genie`. Wire-format zod
|
|
4
|
+
schemas (extending the generated `@dbx-tools/sdk-shared` Genie
|
|
5
|
+
shapes), the high-level `GenieChatEvent` discriminated union the
|
|
6
|
+
`genieEventChat` driver emits, and the per-event detectors that
|
|
7
|
+
derive those events from a `GenieMessage` snapshot diff.
|
|
8
|
+
|
|
9
|
+
No `node:*`, no `WorkspaceClient`, no I/O. Safe to import from any
|
|
10
|
+
runtime, including browser bundles.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import {
|
|
14
|
+
// Wire schemas (extended over @dbx-tools/sdk-shared)
|
|
15
|
+
GenieMessageSchema,
|
|
16
|
+
GenieAttachmentSchema,
|
|
17
|
+
GenieQueryAttachmentSchema,
|
|
18
|
+
GenieThoughtSchema,
|
|
19
|
+
// High-level event union
|
|
20
|
+
GenieChatEventSchema,
|
|
21
|
+
type GenieChatEvent,
|
|
22
|
+
type GenieChatLocation,
|
|
23
|
+
// Status helpers
|
|
24
|
+
TERMINAL_STATUSES,
|
|
25
|
+
isTerminalStatus,
|
|
26
|
+
humanizeStatus,
|
|
27
|
+
// Attachment discriminator
|
|
28
|
+
detectAttachmentType,
|
|
29
|
+
tagAttachment,
|
|
30
|
+
// Pure event detectors + orchestrator
|
|
31
|
+
eventsFromMessage,
|
|
32
|
+
detectStatus,
|
|
33
|
+
detectThinking,
|
|
34
|
+
detectText,
|
|
35
|
+
detectQuery,
|
|
36
|
+
detectStatement,
|
|
37
|
+
detectRows,
|
|
38
|
+
detectSuggestedQuestions,
|
|
39
|
+
detectAttachmentAdded,
|
|
40
|
+
} from "@dbx-tools/genie-shared";
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Server-side chat driving (`genieChat`, `genieEventChat`) lives in
|
|
44
|
+
`@dbx-tools/genie` and pulls these types in. Frontends only need this
|
|
45
|
+
package.
|
|
46
|
+
|
|
47
|
+
## Widened wire schemas
|
|
48
|
+
|
|
49
|
+
The SDK shapes from `@dbx-tools/sdk-shared` are re-exported with a
|
|
50
|
+
few fields Genie ships on the wire that the upstream `.d.ts` doesn't
|
|
51
|
+
currently type:
|
|
52
|
+
|
|
53
|
+
| Schema | Extension |
|
|
54
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
55
|
+
| `GenieMessageSchema` | Adds `auto_regenerate_count: number` and re-types `attachments` to the local `GenieAttachmentSchema`. |
|
|
56
|
+
| `GenieAttachmentSchema` | Re-types `query` to the thoughts-aware `GenieQueryAttachmentSchema`, adds `attachment_type` discriminator literal so consumers can `switch (att.attachment_type)` instead of probing which sub-object is populated. |
|
|
57
|
+
| `GenieQueryAttachmentSchema` | Adds `thoughts: GenieThought[]` (the streamed reasoning payload). |
|
|
58
|
+
| `GenieThoughtSchema` | New: `{ thought_type, content }`. See [`GenieThoughtType`](#thought-types) for the known kinds. |
|
|
59
|
+
|
|
60
|
+
### Thought types
|
|
61
|
+
|
|
62
|
+
Open at the type level (`| (string & {})`) so a new server-side
|
|
63
|
+
thought type doesn't break compilation; the four known types still
|
|
64
|
+
narrow correctly under `switch`:
|
|
65
|
+
|
|
66
|
+
| `thought_type` | Content |
|
|
67
|
+
| ------------------------------- | -------------------------------------------------------------------------------------- |
|
|
68
|
+
| `THOUGHT_TYPE_DESCRIPTION` | One-paragraph restatement of what the user asked. |
|
|
69
|
+
| `THOUGHT_TYPE_DATA_SOURCING` | Markdown bullets of the fully-qualified `catalog.schema.table` sources Genie chose. |
|
|
70
|
+
| `THOUGHT_TYPE_STEPS` | High-level plan Genie wrote before running SQL (one bullet per step). |
|
|
71
|
+
| `THOUGHT_TYPE_UNDERSTANDING` | Ambiguity / interpretation notes ("'revenue' could be gross, net, or recognized..."). |
|
|
72
|
+
|
|
73
|
+
### Attachment discriminator
|
|
74
|
+
|
|
75
|
+
Genie populates only one of `query` / `text` / `suggested_questions`
|
|
76
|
+
per attachment slot. `tagAttachment(att)` stamps the matching
|
|
77
|
+
discriminator literal onto `att.attachment_type`; `detectAttachmentType(att)`
|
|
78
|
+
just returns it without copying.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { tagAttachment, detectAttachmentType } from "@dbx-tools/genie-shared";
|
|
82
|
+
|
|
83
|
+
const tagged = tagAttachment(att);
|
|
84
|
+
switch (tagged.attachment_type) {
|
|
85
|
+
case "query": // tagged.query is non-null
|
|
86
|
+
case "text": // tagged.text is non-null
|
|
87
|
+
case "suggested_questions": // tagged.suggested_questions is non-null
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Terminal-status helpers
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { TERMINAL_STATUSES, isTerminalStatus, humanizeStatus } from "@dbx-tools/genie-shared";
|
|
95
|
+
|
|
96
|
+
TERMINAL_STATUSES; // ["COMPLETED", "FAILED", "CANCELLED"] as const
|
|
97
|
+
|
|
98
|
+
if (isTerminalStatus(msg.status)) {
|
|
99
|
+
// msg.status is now TerminalStatus
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
humanizeStatus("EXECUTING_QUERY"); // "Running SQL query"
|
|
103
|
+
humanizeStatus("FUTURE_NEW_STATE"); // "Future new state" (falls back to tokenizer)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`humanizeStatus` is the single source of truth for status pill
|
|
107
|
+
labels. Both the server (via `@dbx-tools/genie`) and any UI that
|
|
108
|
+
subscribes to `status` events call it so labels stay in lock-step
|
|
109
|
+
across the wire.
|
|
110
|
+
|
|
111
|
+
## `GenieChatEvent` union
|
|
112
|
+
|
|
113
|
+
`genieEventChat` (in `@dbx-tools/genie`) drives a turn and yields a
|
|
114
|
+
stream of these. Each variant is a flat `{ type, ...fields }` object
|
|
115
|
+
with `type` as the discriminator and snake_case payload fields hoisted
|
|
116
|
+
to the top level - no `payload` wrapper.
|
|
117
|
+
|
|
118
|
+
| `type` | Source | Notable fields |
|
|
119
|
+
| --------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
120
|
+
| `question` | Lifecycle. Fires once per turn, on the first `message` yield. | `content`, `message_id`, `conversation_id`, `space_id` |
|
|
121
|
+
| `message` | Lifecycle. Fires once per poll yield. | `message: GenieMessage` |
|
|
122
|
+
| `status` | Top-level `message.status` transitioned. | `status`, `previous_status` |
|
|
123
|
+
| `attachment` | A new attachment slot appeared in `message.attachments[]`. | `index`, `attachment_type` |
|
|
124
|
+
| `thinking` | A new `(thought_type, content)` tuple on a query attachment (value-based dedupe). | `text`, `thought_type` |
|
|
125
|
+
| `text` | Text-attachment `content` appeared or changed. | `text` |
|
|
126
|
+
| `query` | SQL was finalized on a query attachment (transitioned undefined -> string or rewrote). | `sql`, `title?`, `description?` |
|
|
127
|
+
| `statement` | SQL submitted to a warehouse and a `statement_id` was assigned. | `statement_id` |
|
|
128
|
+
| `rows` | Row count on a query attachment changed (fires for `undefined -> 0` and `0 -> N`). | `row_count`, `previous_row_count`, `statement_id` |
|
|
129
|
+
| `suggested_questions` | Follow-up suggested-questions array appeared or rewrote. | `questions: string[]` |
|
|
130
|
+
| `result` | Lifecycle. Fires once on the terminal snapshot. | `status: TerminalStatus`, `message: GenieMessage` |
|
|
131
|
+
|
|
132
|
+
Every variant carries a `GenieChatLocation` mixin (`space_id`,
|
|
133
|
+
`conversation_id?`, `message_id?`, `attachment_id?`) so subscribers
|
|
134
|
+
can route, log, or correlate without re-walking the message.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { type GenieChatEvent } from "@dbx-tools/genie-shared";
|
|
138
|
+
|
|
139
|
+
function handleEvent(event: GenieChatEvent) {
|
|
140
|
+
switch (event.type) {
|
|
141
|
+
case "question":
|
|
142
|
+
console.log(`[Q]`, event.content);
|
|
143
|
+
break;
|
|
144
|
+
case "thinking":
|
|
145
|
+
console.log(`[think:${event.thought_type}]`, event.text);
|
|
146
|
+
break;
|
|
147
|
+
case "query":
|
|
148
|
+
console.log(`[sql]`, event.title, "\n", event.sql);
|
|
149
|
+
break;
|
|
150
|
+
case "rows":
|
|
151
|
+
console.log(`[rows]`, event.previous_row_count, "->", event.row_count);
|
|
152
|
+
break;
|
|
153
|
+
case "result":
|
|
154
|
+
console.log(`[done]`, event.status);
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Stream order per turn
|
|
161
|
+
|
|
162
|
+
1. `question` (deferred to the first `message` yield so the
|
|
163
|
+
assigned `message_id` is present).
|
|
164
|
+
2. `message` for every poll yield (carries the raw snapshot on
|
|
165
|
+
`event.message`).
|
|
166
|
+
3. Any derived events the snapshot diff produced (`status`,
|
|
167
|
+
`attachment`, `thinking`, `text`, `query`, `statement`, `rows`,
|
|
168
|
+
`suggested_questions`) in that fixed order.
|
|
169
|
+
4. On the terminal snapshot, a final `result` event.
|
|
170
|
+
|
|
171
|
+
Errors propagate via the generator throwing - there is no `error`
|
|
172
|
+
variant on the union. Wrap the `for await` in `try / catch` if you
|
|
173
|
+
need to handle failures.
|
|
174
|
+
|
|
175
|
+
## Detectors + `eventsFromMessage`
|
|
176
|
+
|
|
177
|
+
If you want to derive events from `GenieMessage` snapshots on the
|
|
178
|
+
client (e.g. replaying persisted history, or driving your own loop
|
|
179
|
+
that bypasses `genieEventChat`), use the pure detectors directly:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import {
|
|
183
|
+
eventsFromMessage,
|
|
184
|
+
detectStatus,
|
|
185
|
+
detectText,
|
|
186
|
+
} from "@dbx-tools/genie-shared";
|
|
187
|
+
|
|
188
|
+
// All detectors, in stable wire order:
|
|
189
|
+
for (const event of eventsFromMessage(current, previous, spaceId)) {
|
|
190
|
+
handleEvent(event);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Or one detector at a time:
|
|
194
|
+
const statusEvent = detectStatus.detect(current, previous, spaceId);
|
|
195
|
+
const textEvent = detectText.detect(currAttachment, prevAttachment, location, idx);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Each detector is built with `eventDetector(name, detect)` so the
|
|
199
|
+
event name (`"status"`) is a string literal that TS uses to look up
|
|
200
|
+
the diff signature (`message` vs `attachment`) and the allowed
|
|
201
|
+
return-fields shape (`DetectorResult<T>`). Mis-typing the event name
|
|
202
|
+
or returning the wrong fields fails to compile.
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
Apache-2.0
|
package/package.json
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"name": "@dbx-tools/genie-shared",
|
|
12
|
-
"version": "0.1.
|
|
12
|
+
"version": "0.1.21",
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@dbx-tools/sdk-shared": "0.1.
|
|
15
|
-
"@dbx-tools/shared": "0.1.
|
|
14
|
+
"@dbx-tools/sdk-shared": "0.1.21",
|
|
15
|
+
"@dbx-tools/shared": "0.1.21",
|
|
16
16
|
"zod": "^4.3.6"
|
|
17
17
|
},
|
|
18
18
|
"module": "index.ts",
|