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