@dxos/assistant 0.7.4 → 0.7.5-labs.5f04cf6

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.
Files changed (109) hide show
  1. package/dist/lib/browser/chunk-37SDA65J.mjs +565 -0
  2. package/dist/lib/browser/chunk-37SDA65J.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +936 -47
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +14 -8
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node/chunk-UYNIAO5N.cjs +577 -0
  9. package/dist/lib/node/chunk-UYNIAO5N.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +936 -54
  11. package/dist/lib/node/index.cjs.map +4 -4
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +20 -13
  14. package/dist/lib/node/testing/index.cjs.map +4 -4
  15. package/dist/lib/node-esm/chunk-AR24K7TY.mjs +567 -0
  16. package/dist/lib/node-esm/chunk-AR24K7TY.mjs.map +7 -0
  17. package/dist/lib/node-esm/index.mjs +936 -47
  18. package/dist/lib/node-esm/index.mjs.map +4 -4
  19. package/dist/lib/node-esm/meta.json +1 -1
  20. package/dist/lib/node-esm/testing/index.mjs +14 -8
  21. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  22. package/dist/types/src/ai-service/client.d.ts +16 -32
  23. package/dist/types/src/ai-service/client.d.ts.map +1 -1
  24. package/dist/types/src/ai-service/defs.d.ts +134 -0
  25. package/dist/types/src/ai-service/defs.d.ts.map +1 -0
  26. package/dist/types/src/ai-service/index.d.ts +3 -1
  27. package/dist/types/src/ai-service/index.d.ts.map +1 -1
  28. package/dist/types/src/ai-service/parser.d.ts +40 -0
  29. package/dist/types/src/ai-service/parser.d.ts.map +1 -0
  30. package/dist/types/src/ai-service/stream.d.ts +35 -0
  31. package/dist/types/src/ai-service/stream.d.ts.map +1 -0
  32. package/dist/types/src/ai-service/stream.test.d.ts +2 -0
  33. package/dist/types/src/ai-service/stream.test.d.ts.map +1 -0
  34. package/dist/types/src/ai-service/util/index.d.ts +2 -0
  35. package/dist/types/src/ai-service/util/index.d.ts.map +1 -0
  36. package/dist/types/src/ai-service/{sse.d.ts → util/sse.d.ts} +5 -0
  37. package/dist/types/src/ai-service/util/sse.d.ts.map +1 -0
  38. package/dist/types/src/conversation/conversation.d.ts +56 -5
  39. package/dist/types/src/conversation/conversation.d.ts.map +1 -1
  40. package/dist/types/src/conversation/index.d.ts +0 -1
  41. package/dist/types/src/conversation/index.d.ts.map +1 -1
  42. package/dist/types/src/conversation/types.d.ts +44 -13
  43. package/dist/types/src/conversation/types.d.ts.map +1 -1
  44. package/dist/types/src/cypher/combinators.d.ts.map +1 -1
  45. package/dist/types/src/cypher/parjs.d.ts +1 -1
  46. package/dist/types/src/cypher/parjs.d.ts.map +1 -1
  47. package/dist/types/src/cypher/schema.d.ts.map +1 -1
  48. package/dist/types/src/testing/artifacts.test.d.ts +1 -0
  49. package/dist/types/src/testing/artifacts.test.d.ts.map +1 -0
  50. package/dist/types/src/testing/data.d.ts +2 -0
  51. package/dist/types/src/testing/data.d.ts.map +1 -0
  52. package/dist/types/src/testing/defs.d.ts +5 -0
  53. package/dist/types/src/testing/defs.d.ts.map +1 -0
  54. package/dist/types/src/testing/index.d.ts +1 -0
  55. package/dist/types/src/testing/index.d.ts.map +1 -1
  56. package/dist/types/src/testing/logger.d.ts +1 -1
  57. package/dist/types/src/testing/logger.d.ts.map +1 -1
  58. package/dist/types/src/testing/query-promts.d.ts +1 -1
  59. package/dist/types/src/testing/query-promts.d.ts.map +1 -1
  60. package/dist/types/src/testing/test-data.d.ts +1 -1
  61. package/dist/types/src/testing/test-data.d.ts.map +1 -1
  62. package/dist/types/src/testing/test-schema.d.ts +4 -4
  63. package/package.json +20 -18
  64. package/src/ai-service/client.test.ts +28 -28
  65. package/src/ai-service/client.ts +31 -177
  66. package/src/ai-service/defs.ts +98 -0
  67. package/src/ai-service/index.ts +3 -1
  68. package/src/ai-service/parser.ts +183 -0
  69. package/src/ai-service/stream.test.ts +106 -0
  70. package/src/ai-service/stream.test.txt +33 -0
  71. package/src/ai-service/stream.ts +182 -0
  72. package/src/ai-service/util/index.ts +5 -0
  73. package/src/ai-service/{sse.ts → util/sse.ts} +5 -0
  74. package/src/bin/chat.ts +8 -10
  75. package/src/conversation/conversation.test.ts +13 -17
  76. package/src/conversation/conversation.ts +113 -18
  77. package/src/conversation/index.ts +0 -1
  78. package/src/conversation/types.ts +55 -16
  79. package/src/cypher/combinators.test.ts +4 -4
  80. package/src/cypher/combinators.ts +0 -1
  81. package/src/cypher/execution-plan.test.ts +5 -3
  82. package/src/cypher/parjs-shims.d.ts +4 -0
  83. package/src/cypher/parjs.ts +2 -2
  84. package/src/cypher/query-executor.test.ts +1 -1
  85. package/src/cypher/schema.ts +0 -1
  86. package/src/index.ts +1 -1
  87. package/src/testing/artifacts.test.ts +182 -0
  88. package/src/testing/data.ts +13 -0
  89. package/src/testing/defs.ts +8 -0
  90. package/src/testing/index.ts +1 -0
  91. package/src/testing/logger.ts +3 -2
  92. package/src/testing/query-promts.ts +3 -3
  93. package/src/testing/query.test.ts +11 -10
  94. package/src/testing/test-data.ts +1 -1
  95. package/vendor/parjs-browser.mjs +4 -0
  96. package/vendor/parjs-default.mjs +8 -0
  97. package/dist/lib/browser/chunk-AKP45LQH.mjs +0 -1394
  98. package/dist/lib/browser/chunk-AKP45LQH.mjs.map +0 -7
  99. package/dist/lib/node/chunk-G6CGWEMO.cjs +0 -1418
  100. package/dist/lib/node/chunk-G6CGWEMO.cjs.map +0 -7
  101. package/dist/lib/node-esm/chunk-WUPUPEFB.mjs +0 -1396
  102. package/dist/lib/node-esm/chunk-WUPUPEFB.mjs.map +0 -7
  103. package/dist/types/src/ai-service/schema.d.ts +0 -296
  104. package/dist/types/src/ai-service/schema.d.ts.map +0 -1
  105. package/dist/types/src/ai-service/sse.d.ts.map +0 -1
  106. package/dist/types/src/sanity.test.d.ts +0 -2
  107. package/dist/types/src/sanity.test.d.ts.map +0 -1
  108. package/src/ai-service/schema.ts +0 -260
  109. package/src/sanity.test.ts +0 -9
@@ -1,71 +1,960 @@
1
1
  import {
2
- AIServiceClientImpl,
3
2
  EchoDataSource,
4
- GenerateRequest,
5
- GenerationStream,
6
- ImageSource,
7
- LLMModel,
8
- LLMTool,
9
- LLMToolDefinition,
10
- LLMToolResult,
11
- Message,
12
- MessageContentBlock,
13
- MessageFromDb,
14
- MessageImageContentBlock,
15
- MessageRole,
16
- MessageTextContentBlock,
17
- MessageToolResultContentBlock,
18
- MessageToolUseContentBlock,
19
- ObjectId,
20
- ObjectIdBrand,
21
- Space,
22
- SpaceIdSchema,
23
- Thread,
24
- ToolTypes,
25
- createUserMessage,
26
- defineTool,
27
3
  executeQuery,
28
4
  executeQueryPlan,
29
5
  formatInferredRelationshipLabel,
30
6
  formatJsonSchemaForLLM,
31
7
  formatNodeLabel,
32
8
  isReference,
33
- parseCypherQuery,
34
- runLLM
35
- } from "./chunk-AKP45LQH.mjs";
9
+ parseCypherQuery
10
+ } from "./chunk-37SDA65J.mjs";
11
+
12
+ // packages/experimental/assistant/src/ai-service/client.ts
13
+ import { Schema as S } from "@effect/schema";
14
+ import { Message } from "@dxos/artifact";
15
+ import { invariant as invariant2 } from "@dxos/invariant";
16
+
17
+ // packages/experimental/assistant/src/ai-service/stream.ts
18
+ import { Trigger } from "@dxos/async";
19
+ import { invariant } from "@dxos/invariant";
20
+ import { log } from "@dxos/log";
21
+
22
+ // packages/experimental/assistant/src/ai-service/util/sse.ts
23
+ async function* iterSSEMessages(response, controller) {
24
+ if (!response.body) {
25
+ controller.abort();
26
+ throw new Error("Attempted to iterate over a response with no body");
27
+ }
28
+ const sseDecoder = new SSEDecoder();
29
+ const lineDecoder = new LineDecoder();
30
+ const iter = readableStreamAsyncIterable(response.body);
31
+ for await (const sseChunk of iterSSEChunks(iter)) {
32
+ for (const line of lineDecoder.decode(sseChunk)) {
33
+ const sse = sseDecoder.decode(line);
34
+ if (sse) {
35
+ yield sse;
36
+ }
37
+ }
38
+ }
39
+ for (const line of lineDecoder.flush()) {
40
+ const sse = sseDecoder.decode(line);
41
+ if (sse) {
42
+ yield sse;
43
+ }
44
+ }
45
+ }
46
+ async function* iterSSEChunks(iterator) {
47
+ let data = new Uint8Array();
48
+ for await (const chunk of iterator) {
49
+ if (chunk == null) {
50
+ continue;
51
+ }
52
+ const binaryChunk = chunk instanceof ArrayBuffer ? new Uint8Array(chunk) : typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk;
53
+ const newData = new Uint8Array(data.length + binaryChunk.length);
54
+ newData.set(data);
55
+ newData.set(binaryChunk, data.length);
56
+ data = newData;
57
+ let patternIndex;
58
+ while ((patternIndex = findDoubleNewlineIndex(data)) !== -1) {
59
+ yield data.slice(0, patternIndex);
60
+ data = data.slice(patternIndex);
61
+ }
62
+ }
63
+ if (data.length > 0) {
64
+ yield data;
65
+ }
66
+ }
67
+ var findDoubleNewlineIndex = (buffer) => {
68
+ const newline = 10;
69
+ const carriage = 13;
70
+ for (let i = 0; i < buffer.length - 2; i++) {
71
+ if (buffer[i] === newline && buffer[i + 1] === newline) {
72
+ return i + 2;
73
+ }
74
+ if (buffer[i] === carriage && buffer[i + 1] === carriage) {
75
+ return i + 2;
76
+ }
77
+ if (buffer[i] === carriage && buffer[i + 1] === newline && i + 3 < buffer.length && buffer[i + 2] === carriage && buffer[i + 3] === newline) {
78
+ return i + 4;
79
+ }
80
+ }
81
+ return -1;
82
+ };
83
+ var SSEDecoder = class {
84
+ constructor() {
85
+ this.event = null;
86
+ this.data = [];
87
+ this.chunks = [];
88
+ }
89
+ decode(line) {
90
+ if (line.endsWith("\r")) {
91
+ line = line.substring(0, line.length - 1);
92
+ }
93
+ if (!line) {
94
+ if (!this.event && !this.data.length) {
95
+ return null;
96
+ }
97
+ const sse = {
98
+ event: this.event,
99
+ data: this.data.join("\n"),
100
+ raw: this.chunks
101
+ };
102
+ this.event = null;
103
+ this.data = [];
104
+ this.chunks = [];
105
+ return sse;
106
+ }
107
+ this.chunks.push(line);
108
+ if (line.startsWith(":")) {
109
+ return null;
110
+ }
111
+ let [fieldname, _, value] = partition(line, ":");
112
+ if (value.startsWith(" ")) {
113
+ value = value.substring(1);
114
+ }
115
+ if (fieldname === "event") {
116
+ this.event = value;
117
+ } else if (fieldname === "data") {
118
+ this.data.push(value);
119
+ }
120
+ return null;
121
+ }
122
+ };
123
+ var partition = (str, delimiter) => {
124
+ const index = str.indexOf(delimiter);
125
+ if (index !== -1) {
126
+ return [
127
+ str.substring(0, index),
128
+ delimiter,
129
+ str.substring(index + delimiter.length)
130
+ ];
131
+ }
132
+ return [
133
+ str,
134
+ "",
135
+ ""
136
+ ];
137
+ };
138
+ function readableStreamAsyncIterable(stream) {
139
+ if (stream[Symbol.asyncIterator]) {
140
+ return stream;
141
+ }
142
+ const reader = stream.getReader();
143
+ return {
144
+ next: async () => {
145
+ try {
146
+ const result = await reader.read();
147
+ if (result?.done) {
148
+ reader.releaseLock();
149
+ }
150
+ return result;
151
+ } catch (e) {
152
+ reader.releaseLock();
153
+ throw e;
154
+ }
155
+ },
156
+ return: async () => {
157
+ const cancelPromise = reader.cancel();
158
+ reader.releaseLock();
159
+ await cancelPromise;
160
+ return {
161
+ done: true,
162
+ value: void 0
163
+ };
164
+ },
165
+ [Symbol.asyncIterator]() {
166
+ return this;
167
+ }
168
+ };
169
+ }
170
+ var LineDecoder = class _LineDecoder {
171
+ static {
172
+ // prettier-ignore
173
+ this.NEWLINE_CHARS = /* @__PURE__ */ new Set([
174
+ "\n",
175
+ "\r"
176
+ ]);
177
+ }
178
+ static {
179
+ this.NEWLINE_REGEXP = /\r\n|[\n\r]/g;
180
+ }
181
+ constructor() {
182
+ this.buffer = [];
183
+ this.trailingCR = false;
184
+ }
185
+ decode(chunk) {
186
+ let text = this.decodeText(chunk);
187
+ if (this.trailingCR) {
188
+ text = "\r" + text;
189
+ this.trailingCR = false;
190
+ }
191
+ if (text.endsWith("\r")) {
192
+ this.trailingCR = true;
193
+ text = text.slice(0, -1);
194
+ }
195
+ if (!text) {
196
+ return [];
197
+ }
198
+ const trailingNewline = _LineDecoder.NEWLINE_CHARS.has(text[text.length - 1] || "");
199
+ let lines = text.split(_LineDecoder.NEWLINE_REGEXP);
200
+ if (trailingNewline) {
201
+ lines.pop();
202
+ }
203
+ if (lines.length === 1 && !trailingNewline) {
204
+ this.buffer.push(lines[0]);
205
+ return [];
206
+ }
207
+ if (this.buffer.length > 0) {
208
+ lines = [
209
+ this.buffer.join("") + lines[0],
210
+ ...lines.slice(1)
211
+ ];
212
+ this.buffer = [];
213
+ }
214
+ if (!trailingNewline) {
215
+ this.buffer = [
216
+ lines.pop() || ""
217
+ ];
218
+ }
219
+ return lines;
220
+ }
221
+ decodeText(bytes) {
222
+ if (bytes == null) {
223
+ return "";
224
+ }
225
+ if (typeof bytes === "string") {
226
+ return bytes;
227
+ }
228
+ if (typeof Buffer !== "undefined") {
229
+ if (bytes instanceof Buffer) {
230
+ return bytes.toString();
231
+ }
232
+ if (bytes instanceof Uint8Array) {
233
+ return Buffer.from(bytes).toString();
234
+ }
235
+ throw new Error(`Unexpected: received non-Uint8Array (${bytes.constructor.name}) stream chunk in an environment with a global "Buffer" defined, which this library assumes to be Node. Please report this error.`);
236
+ }
237
+ if (typeof TextDecoder !== "undefined") {
238
+ if (bytes instanceof Uint8Array || bytes instanceof ArrayBuffer) {
239
+ this.textDecoder ??= new TextDecoder("utf8");
240
+ return this.textDecoder.decode(bytes);
241
+ }
242
+ throw new Error(`Unexpected: received non-Uint8Array/ArrayBuffer (${bytes.constructor.name}) in a web platform. Please report this error.`);
243
+ }
244
+ throw new Error("Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.");
245
+ }
246
+ flush() {
247
+ if (!this.buffer.length && !this.trailingCR) {
248
+ return [];
249
+ }
250
+ const lines = [
251
+ this.buffer.join("")
252
+ ];
253
+ this.buffer = [];
254
+ this.trailingCR = false;
255
+ return lines;
256
+ }
257
+ };
258
+
259
+ // packages/experimental/assistant/src/ai-service/stream.ts
260
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/stream.ts";
261
+ var GenerationStream = class _GenerationStream {
262
+ /**
263
+ * Creates a stream from an SSE response.
264
+ */
265
+ static fromSSEResponse(params, response, controller = new AbortController()) {
266
+ const iterator = async function* () {
267
+ for await (const sse of iterSSEMessages(response, controller)) {
268
+ if (sse.event === "completion") {
269
+ try {
270
+ yield JSON.parse(sse.data);
271
+ } catch (err) {
272
+ log.error("could not parse message into JSON:", {
273
+ data: sse.data,
274
+ raw: sse.raw
275
+ }, {
276
+ F: __dxlog_file,
277
+ L: 31,
278
+ S: this,
279
+ C: (f, a) => f(...a)
280
+ });
281
+ throw err;
282
+ }
283
+ }
284
+ if (sse.event === "message_start" || sse.event === "message_delta" || sse.event === "message_stop" || sse.event === "content_block_start" || sse.event === "content_block_delta" || sse.event === "content_block_stop") {
285
+ try {
286
+ yield JSON.parse(sse.data);
287
+ } catch (err) {
288
+ log.error("could not parse message into JSON:", {
289
+ data: sse.data,
290
+ raw: sse.raw
291
+ }, {
292
+ F: __dxlog_file,
293
+ L: 47,
294
+ S: this,
295
+ C: (f, a) => f(...a)
296
+ });
297
+ throw err;
298
+ }
299
+ }
300
+ if (sse.event === "ping") {
301
+ continue;
302
+ }
303
+ if (sse.event === "error") {
304
+ throw new Error(`Message generation error: ${sse.data}`);
305
+ }
306
+ }
307
+ };
308
+ return new _GenerationStream(controller, iterator, params);
309
+ }
310
+ constructor(_controller, _getIterator, _params) {
311
+ this._controller = _controller;
312
+ this._getIterator = _getIterator;
313
+ this._params = _params;
314
+ this._done = new Trigger();
315
+ this._previousMessages = [];
316
+ this._accumulatedMessage = void 0;
317
+ this._iterator = void 0;
318
+ }
319
+ [Symbol.asyncIterator]() {
320
+ return this._createIterator();
321
+ }
322
+ get accumulatedMessages() {
323
+ return [
324
+ ...this._previousMessages,
325
+ ...this._accumulatedMessage == null ? [] : [
326
+ this._accumulatedMessage
327
+ ]
328
+ ];
329
+ }
330
+ /**
331
+ * Aborts the stream.
332
+ */
333
+ abort() {
334
+ this._controller.abort();
335
+ }
336
+ /**
337
+ * Returns the complete message list generated by the AI service.
338
+ */
339
+ async complete() {
340
+ await this._done.wait();
341
+ return this.accumulatedMessages;
342
+ }
343
+ _createIterator() {
344
+ const self = this;
345
+ return this._iterator ??= (() => {
346
+ const generator = async function* () {
347
+ try {
348
+ for await (const event of self._getIterator()) {
349
+ self._processEvent(event);
350
+ yield event;
351
+ }
352
+ this._done.wake();
353
+ } catch (err) {
354
+ this._done.throw(err);
355
+ }
356
+ };
357
+ return generator.call(this);
358
+ })();
359
+ }
360
+ _processEvent(event) {
361
+ switch (event.type) {
362
+ case "message_start": {
363
+ if (this._accumulatedMessage) {
364
+ this._previousMessages.push(this._accumulatedMessage);
365
+ this._accumulatedMessage = void 0;
366
+ }
367
+ this._accumulatedMessage = {
368
+ ...event.message,
369
+ threadId: this._params.threadId,
370
+ spaceId: this._params.spaceId
371
+ };
372
+ break;
373
+ }
374
+ case "message_delta": {
375
+ if (this._accumulatedMessage === void 0) {
376
+ throw new Error("Received message delta without a message start");
377
+ }
378
+ break;
379
+ }
380
+ case "message_stop": {
381
+ if (this._accumulatedMessage === void 0) {
382
+ throw new Error("Received message stop without a message start");
383
+ }
384
+ break;
385
+ }
386
+ case "content_block_start": {
387
+ invariant(this._accumulatedMessage, void 0, {
388
+ F: __dxlog_file,
389
+ L: 149,
390
+ S: this,
391
+ A: [
392
+ "this._accumulatedMessage",
393
+ ""
394
+ ]
395
+ });
396
+ this._accumulatedMessage.content.push(event.content);
397
+ break;
398
+ }
399
+ case "content_block_delta": {
400
+ invariant(this._accumulatedMessage, void 0, {
401
+ F: __dxlog_file,
402
+ L: 155,
403
+ S: this,
404
+ A: [
405
+ "this._accumulatedMessage",
406
+ ""
407
+ ]
408
+ });
409
+ const snapshotContent = this._accumulatedMessage.content.at(event.index);
410
+ if (snapshotContent?.type === "text" && event.delta.type === "text_delta") {
411
+ snapshotContent.text += event.delta.text;
412
+ } else if (snapshotContent?.type === "tool_use" && event.delta.type === "input_json_delta") {
413
+ snapshotContent.inputJson ??= "";
414
+ snapshotContent.inputJson += event.delta.partial_json;
415
+ }
416
+ break;
417
+ }
418
+ case "content_block_stop": {
419
+ invariant(this._accumulatedMessage, void 0, {
420
+ F: __dxlog_file,
421
+ L: 172,
422
+ S: this,
423
+ A: [
424
+ "this._accumulatedMessage",
425
+ ""
426
+ ]
427
+ });
428
+ const lastBlock = this._accumulatedMessage.content.at(-1);
429
+ if (lastBlock?.type === "tool_use") {
430
+ lastBlock.input = lastBlock.inputJson == null || lastBlock.inputJson === "" ? {} : JSON.parse(lastBlock.inputJson ?? "");
431
+ }
432
+ break;
433
+ }
434
+ }
435
+ }
436
+ };
437
+
438
+ // packages/experimental/assistant/src/ai-service/client.ts
439
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/client.ts";
440
+ var AIServiceClientImpl = class {
441
+ constructor({ endpoint }) {
442
+ invariant2(endpoint, "endpoint is required", {
443
+ F: __dxlog_file2,
444
+ L: 36,
445
+ S: this,
446
+ A: [
447
+ "endpoint",
448
+ "'endpoint is required'"
449
+ ]
450
+ });
451
+ this._endpoint = endpoint;
452
+ }
453
+ async getSpace(spaceId) {
454
+ throw new Error("Not implemented");
455
+ }
456
+ async getThread(spaceId, threadId) {
457
+ throw new Error("Not implemented");
458
+ }
459
+ async getMessages(spaceId, threadId) {
460
+ const res = await fetch(`${this._endpoint}/space/${spaceId}/thread/${threadId}/message`);
461
+ return S.decodePromise(S.Array(Message).pipe(S.mutable))((await res.json()).data.results);
462
+ }
463
+ async appendMessages(messages) {
464
+ const url = `${this._endpoint}/message`;
465
+ const res = await fetch(url, {
466
+ method: "POST",
467
+ headers: {
468
+ "Content-Type": "application/json"
469
+ },
470
+ body: JSON.stringify({
471
+ messages: await S.encodePromise(S.Array(Message))(messages)
472
+ })
473
+ });
474
+ if (!res.ok) {
475
+ throw new Error(`${await res.text()} [${url}]`);
476
+ }
477
+ }
478
+ // TODO(dmaretskyi): Accept partial message schema.
479
+ async updateMessage(spaceId, threadId, messageId, message) {
480
+ throw new Error("Not implemented");
481
+ }
482
+ /**
483
+ * Open message stream.
484
+ */
485
+ async generate(request) {
486
+ const controller = new AbortController();
487
+ const signal = controller.signal;
488
+ const response = await fetch(`${this._endpoint}/generate`, {
489
+ method: "POST",
490
+ headers: {
491
+ "Content-Type": "application/json"
492
+ },
493
+ body: JSON.stringify(request),
494
+ signal
495
+ });
496
+ invariant2(response.body instanceof ReadableStream, void 0, {
497
+ F: __dxlog_file2,
498
+ L: 87,
499
+ S: this,
500
+ A: [
501
+ "response.body instanceof ReadableStream",
502
+ ""
503
+ ]
504
+ });
505
+ return GenerationStream.fromSSEResponse({
506
+ spaceId: request.spaceId,
507
+ threadId: request.threadId
508
+ }, response, controller);
509
+ }
510
+ };
511
+
512
+ // packages/experimental/assistant/src/ai-service/defs.ts
513
+ import { Schema as S2 } from "@effect/schema";
514
+ import { Tool, Message as Message2, SpaceIdSchema } from "@dxos/artifact";
515
+ import { ObjectId } from "@dxos/echo-schema";
516
+ var createArtifactElement = (id) => `<artifact id=${id} />`;
517
+ var LLMModel = S2.Literal("@anthropic/claude-3-5-haiku-20241022", "@anthropic/claude-3-5-sonnet-20241022", "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", "@hf/nousresearch/hermes-2-pro-mistral-7b", "@ollama/llama-3-2-3b", "@ollama/llama-3-1-nemotron-70b-instruct", "@ollama/llama-3-1-nemotron-mini-4b-instruct", "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b");
518
+ var ToolTypes = Object.freeze({
519
+ // TODO(dmaretskyi): Not implemented yet.
520
+ // DatabaseQuery: 'database_query',
521
+ TextToImage: "text_to_image"
522
+ });
523
+ var GenerateRequest = S2.Struct({
524
+ spaceId: S2.optional(SpaceIdSchema),
525
+ threadId: S2.optional(ObjectId),
526
+ model: LLMModel,
527
+ /**
528
+ * History of messages to include in the context window.
529
+ */
530
+ history: S2.optional(S2.Array(Message2)),
531
+ /**
532
+ * System instructions to the LLM.
533
+ */
534
+ // TODO(dmaretskyi): Should this be part of the thread?.
535
+ systemPrompt: S2.optional(S2.String),
536
+ /**
537
+ * Tools available for the LLM.
538
+ */
539
+ tools: S2.optional(S2.Array(Tool).pipe(S2.mutable))
540
+ });
541
+
542
+ // packages/experimental/assistant/src/ai-service/parser.ts
543
+ var StreamingParser = class {
544
+ constructor(onBlock) {
545
+ this.onBlock = onBlock;
546
+ this.buffer = "";
547
+ }
548
+ /**
549
+ * Write a chunk of text to the parser.
550
+ */
551
+ write(chunk) {
552
+ this.buffer += chunk;
553
+ this.processBuffer();
554
+ }
555
+ /**
556
+ * Signal that no more input will be written.
557
+ */
558
+ end() {
559
+ if (this.buffer.length > 0) {
560
+ this.emitTextBlock(this.buffer);
561
+ this.buffer = "";
562
+ }
563
+ }
564
+ processBuffer() {
565
+ while (this.buffer.length > 0) {
566
+ if (this.tryParseCodeBlock()) {
567
+ continue;
568
+ }
569
+ const tagStartIndex = this.buffer.indexOf("<");
570
+ if (tagStartIndex === -1) {
571
+ return;
572
+ }
573
+ if (tagStartIndex > 0) {
574
+ this.emitTextBlock(this.buffer.slice(0, tagStartIndex));
575
+ this.buffer = this.buffer.slice(tagStartIndex);
576
+ continue;
577
+ }
578
+ const tagEndIndex = this.buffer.indexOf(">", tagStartIndex);
579
+ if (tagEndIndex === -1) {
580
+ return;
581
+ }
582
+ const isSelfClosing = this.buffer[tagEndIndex - 1] === "/";
583
+ const actualTagEndIndex = isSelfClosing ? tagEndIndex - 1 : tagEndIndex;
584
+ const tagContent = this.buffer.slice(tagStartIndex + 1, actualTagEndIndex).trim();
585
+ const [tagName, ...attrParts] = tagContent.split(/\s+/);
586
+ const attributes = this.parseAttributes(attrParts.join(" "));
587
+ if (isSelfClosing) {
588
+ this.emitTagBlock(tagName, "", attributes, true);
589
+ this.buffer = this.buffer.slice(tagEndIndex + 1);
590
+ continue;
591
+ }
592
+ const closingTagIndex = this.buffer.indexOf("</" + tagName + ">", tagEndIndex);
593
+ if (closingTagIndex === -1) {
594
+ return;
595
+ }
596
+ const content = this.buffer.slice(tagEndIndex + 1, closingTagIndex);
597
+ this.emitTagBlock(tagName, content, attributes, false);
598
+ this.buffer = this.buffer.slice(closingTagIndex + tagName.length + 3);
599
+ }
600
+ }
601
+ tryParseCodeBlock() {
602
+ const codeBlockStart = this.buffer.indexOf("```");
603
+ if (codeBlockStart === -1) {
604
+ return false;
605
+ }
606
+ if (codeBlockStart > 0) {
607
+ this.emitTextBlock(this.buffer.slice(0, codeBlockStart));
608
+ this.buffer = this.buffer.slice(codeBlockStart);
609
+ return true;
610
+ }
611
+ const nextNewline = this.buffer.indexOf("\n", codeBlockStart);
612
+ if (nextNewline === -1) {
613
+ return false;
614
+ }
615
+ const codeBlockEnd = this.buffer.indexOf("\n```", nextNewline);
616
+ if (codeBlockEnd === -1) {
617
+ return false;
618
+ }
619
+ const languageLine = this.buffer.slice(3, nextNewline).trim();
620
+ const content = this.buffer.slice(nextNewline + 1, codeBlockEnd);
621
+ this.emitCodeBlock(languageLine, content);
622
+ this.buffer = this.buffer.slice(codeBlockEnd + 4);
623
+ return true;
624
+ }
625
+ parseAttributes(attrString) {
626
+ const attributes = {};
627
+ if (!attrString) {
628
+ return attributes;
629
+ }
630
+ const attrPattern = /(\w+)\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+))/g;
631
+ let match;
632
+ while ((match = attrPattern.exec(attrString)) !== null) {
633
+ const [, name, doubleQuoted, singleQuoted, unquoted] = match;
634
+ attributes[name] = doubleQuoted ?? singleQuoted ?? unquoted;
635
+ }
636
+ return attributes;
637
+ }
638
+ emitTextBlock(text) {
639
+ if (text.trim().length > 0) {
640
+ this.onBlock({
641
+ type: "text",
642
+ content: text
643
+ });
644
+ }
645
+ }
646
+ emitTagBlock(name, content, attributes, selfClosing) {
647
+ this.onBlock({
648
+ type: "tag",
649
+ name,
650
+ content: content.trim(),
651
+ attributes,
652
+ selfClosing
653
+ });
654
+ }
655
+ emitCodeBlock(language, content) {
656
+ this.onBlock({
657
+ type: "code",
658
+ language,
659
+ content: content.trim()
660
+ });
661
+ }
662
+ };
663
+
664
+ // packages/experimental/assistant/src/conversation/conversation.ts
665
+ import { ToolResult, Message as Message3 } from "@dxos/artifact";
666
+ import { createStatic, ObjectId as ObjectId2 } from "@dxos/echo-schema";
667
+ import { invariant as invariant3 } from "@dxos/invariant";
668
+ import { log as log2 } from "@dxos/log";
669
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/conversation/conversation.ts";
670
+ var runLLM = async (params) => {
671
+ let conversationResult = null;
672
+ const history = params.history ?? [];
673
+ const generate = async () => {
674
+ log2("llm generate", {
675
+ tools: params.tools
676
+ }, {
677
+ F: __dxlog_file3,
678
+ L: 43,
679
+ S: void 0,
680
+ C: (f, a) => f(...a)
681
+ });
682
+ const beginTs = Date.now();
683
+ const stream = await params.client.generate({
684
+ model: params.model,
685
+ spaceId: params.spaceId,
686
+ threadId: params.threadId,
687
+ history,
688
+ systemPrompt: params.system,
689
+ tools: params.tools
690
+ });
691
+ for await (const event of stream) {
692
+ params.logger?.(event);
693
+ }
694
+ const messages = await stream.complete();
695
+ const message = messages.at(-1);
696
+ log2("llm result", {
697
+ time: Date.now() - beginTs,
698
+ message
699
+ }, {
700
+ F: __dxlog_file3,
701
+ L: 60,
702
+ S: void 0,
703
+ C: (f, a) => f(...a)
704
+ });
705
+ invariant3(message, void 0, {
706
+ F: __dxlog_file3,
707
+ L: 61,
708
+ S: void 0,
709
+ A: [
710
+ "message",
711
+ ""
712
+ ]
713
+ });
714
+ params.logger?.({
715
+ type: "message",
716
+ message
717
+ });
718
+ history.push(...messages.map((message2) => ({
719
+ ...message2,
720
+ content: message2.content.filter((block) => block.type !== "image")
721
+ })));
722
+ const isToolUse2 = message.content.at(-1)?.type === "tool_use";
723
+ if (isToolUse2) {
724
+ const toolCalls = message.content.filter((block) => block.type === "tool_use");
725
+ invariant3(toolCalls.length === 1, void 0, {
726
+ F: __dxlog_file3,
727
+ L: 72,
728
+ S: void 0,
729
+ A: [
730
+ "toolCalls.length === 1",
731
+ ""
732
+ ]
733
+ });
734
+ const toolCall = toolCalls[0];
735
+ const tool = params.tools.find((tool2) => tool2.name === toolCall.name);
736
+ if (!tool) {
737
+ throw new Error(`Tool not found: ${toolCall.name}`);
738
+ }
739
+ invariant3(tool.execute, void 0, {
740
+ F: __dxlog_file3,
741
+ L: 79,
742
+ S: void 0,
743
+ A: [
744
+ "tool.execute",
745
+ ""
746
+ ]
747
+ });
748
+ const toolResult = await tool.execute(toolCall.input);
749
+ switch (toolResult.kind) {
750
+ case "error": {
751
+ log2.warn("tool error", {
752
+ message: toolResult.message
753
+ }, {
754
+ F: __dxlog_file3,
755
+ L: 83,
756
+ S: void 0,
757
+ C: (f, a) => f(...a)
758
+ });
759
+ const resultMessage = {
760
+ id: ObjectId2.random(),
761
+ spaceId: params.spaceId,
762
+ threadId: params.threadId,
763
+ role: "user",
764
+ content: [
765
+ {
766
+ type: "tool_result",
767
+ toolUseId: toolCall.id,
768
+ content: toolResult.message,
769
+ isError: true
770
+ }
771
+ ]
772
+ };
773
+ params.logger?.({
774
+ type: "message",
775
+ message: resultMessage
776
+ });
777
+ history.push(resultMessage);
778
+ return true;
779
+ }
780
+ case "success": {
781
+ log2("tool success", {
782
+ result: toolResult.result
783
+ }, {
784
+ F: __dxlog_file3,
785
+ L: 105,
786
+ S: void 0,
787
+ C: (f, a) => f(...a)
788
+ });
789
+ const resultMessage = {
790
+ id: ObjectId2.random(),
791
+ spaceId: params.spaceId,
792
+ threadId: params.threadId,
793
+ role: "user",
794
+ content: [
795
+ {
796
+ type: "tool_result",
797
+ toolUseId: toolCall.id,
798
+ content: JSON.stringify(toolResult.result)
799
+ }
800
+ ]
801
+ };
802
+ params.logger?.({
803
+ type: "message",
804
+ message: resultMessage
805
+ });
806
+ history.push(resultMessage);
807
+ return true;
808
+ }
809
+ case "break": {
810
+ log2("tool break", {
811
+ result: toolResult.result
812
+ }, {
813
+ F: __dxlog_file3,
814
+ L: 126,
815
+ S: void 0,
816
+ C: (f, a) => f(...a)
817
+ });
818
+ conversationResult = toolResult.result;
819
+ return false;
820
+ }
821
+ }
822
+ }
823
+ return false;
824
+ };
825
+ while (await generate()) {
826
+ }
827
+ return {
828
+ result: conversationResult,
829
+ history
830
+ };
831
+ };
832
+ var isToolUse = (message) => message.content.at(-1)?.type === "tool_use";
833
+ var runTools = async ({ message, tools, extensions }) => {
834
+ const toolCalls = message.content.filter((block) => block.type === "tool_use");
835
+ invariant3(toolCalls.length === 1, void 0, {
836
+ F: __dxlog_file3,
837
+ L: 164,
838
+ S: void 0,
839
+ A: [
840
+ "toolCalls.length === 1",
841
+ ""
842
+ ]
843
+ });
844
+ const toolCall = toolCalls[0];
845
+ const tool = tools.find((tool2) => tool2.name === toolCall.name);
846
+ if (!tool) {
847
+ throw new Error(`Tool not found: ${toolCall.name}`);
848
+ }
849
+ let toolResult;
850
+ try {
851
+ invariant3(tool.execute, void 0, {
852
+ F: __dxlog_file3,
853
+ L: 173,
854
+ S: void 0,
855
+ A: [
856
+ "tool.execute",
857
+ ""
858
+ ]
859
+ });
860
+ toolResult = await tool.execute(toolCall.input, {
861
+ extensions
862
+ });
863
+ } catch (error) {
864
+ log2("tool error", {
865
+ error
866
+ }, {
867
+ F: __dxlog_file3,
868
+ L: 176,
869
+ S: void 0,
870
+ C: (f, a) => f(...a)
871
+ });
872
+ toolResult = ToolResult.Error(error.message);
873
+ }
874
+ switch (toolResult.kind) {
875
+ case "error": {
876
+ log2("tool error", {
877
+ message: toolResult.message
878
+ }, {
879
+ F: __dxlog_file3,
880
+ L: 182,
881
+ S: void 0,
882
+ C: (f, a) => f(...a)
883
+ });
884
+ const resultMessage = createStatic(Message3, {
885
+ role: "user",
886
+ content: [
887
+ {
888
+ type: "tool_result",
889
+ toolUseId: toolCall.id,
890
+ content: toolResult.message,
891
+ isError: true
892
+ }
893
+ ]
894
+ });
895
+ return {
896
+ type: "continue",
897
+ message: resultMessage
898
+ };
899
+ }
900
+ case "success": {
901
+ log2("tool success", {
902
+ result: toolResult.result
903
+ }, {
904
+ F: __dxlog_file3,
905
+ L: 203,
906
+ S: void 0,
907
+ C: (f, a) => f(...a)
908
+ });
909
+ const resultMessage = createStatic(Message3, {
910
+ role: "user",
911
+ content: [
912
+ {
913
+ type: "tool_result",
914
+ toolUseId: toolCall.id,
915
+ content: typeof toolResult.result === "string" ? toolResult.result : JSON.stringify(toolResult.result)
916
+ }
917
+ ]
918
+ });
919
+ return {
920
+ type: "continue",
921
+ message: resultMessage
922
+ };
923
+ }
924
+ case "break": {
925
+ log2("tool break", {
926
+ result: toolResult.result
927
+ }, {
928
+ F: __dxlog_file3,
929
+ L: 222,
930
+ S: void 0,
931
+ C: (f, a) => f(...a)
932
+ });
933
+ return {
934
+ type: "break",
935
+ result: toolResult.result
936
+ };
937
+ }
938
+ }
939
+ };
36
940
  export {
37
941
  AIServiceClientImpl,
38
942
  EchoDataSource,
39
943
  GenerateRequest,
40
944
  GenerationStream,
41
- ImageSource,
42
945
  LLMModel,
43
- LLMTool,
44
- LLMToolDefinition,
45
- LLMToolResult,
46
- Message,
47
- MessageContentBlock,
48
- MessageFromDb,
49
- MessageImageContentBlock,
50
- MessageRole,
51
- MessageTextContentBlock,
52
- MessageToolResultContentBlock,
53
- MessageToolUseContentBlock,
54
- ObjectId,
55
- ObjectIdBrand,
56
- Space,
57
- SpaceIdSchema,
58
- Thread,
946
+ StreamingParser,
59
947
  ToolTypes,
60
- createUserMessage,
61
- defineTool,
948
+ createArtifactElement,
62
949
  executeQuery,
63
950
  executeQueryPlan,
64
951
  formatInferredRelationshipLabel,
65
952
  formatJsonSchemaForLLM,
66
953
  formatNodeLabel,
67
954
  isReference,
955
+ isToolUse,
68
956
  parseCypherQuery,
69
- runLLM
957
+ runLLM,
958
+ runTools
70
959
  };
71
960
  //# sourceMappingURL=index.mjs.map