@dxos/assistant 0.8.1-main.ba2dec9 → 0.8.1-staging.31c3ee1

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 (75) hide show
  1. package/dist/lib/browser/chunk-PND5VR6D.mjs +2315 -0
  2. package/dist/lib/browser/chunk-PND5VR6D.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +313 -1631
  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 +20 -6
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node/chunk-IBOBRBDF.cjs +2326 -0
  9. package/dist/lib/node/chunk-IBOBRBDF.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +321 -1629
  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 +22 -11
  14. package/dist/lib/node/testing/index.cjs.map +4 -4
  15. package/dist/lib/node-esm/chunk-DCQJOLSR.mjs +2317 -0
  16. package/dist/lib/node-esm/chunk-DCQJOLSR.mjs.map +7 -0
  17. package/dist/lib/node-esm/index.mjs +313 -1631
  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 +20 -6
  21. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  22. package/dist/types/src/ai-service/defs.d.ts +8 -2
  23. package/dist/types/src/ai-service/defs.d.ts.map +1 -1
  24. package/dist/types/src/ai-service/edge-client.d.ts.map +1 -1
  25. package/dist/types/src/ai-service/message-collector.d.ts +16 -16
  26. package/dist/types/src/ai-service/message-collector.d.ts.map +1 -1
  27. package/dist/types/src/ai-service/ollama-client.d.ts +1 -5
  28. package/dist/types/src/ai-service/ollama-client.d.ts.map +1 -1
  29. package/dist/types/src/ai-service/parser.d.ts +8 -8
  30. package/dist/types/src/ai-service/parser.d.ts.map +1 -1
  31. package/dist/types/src/ai-service/types.d.ts +90 -156
  32. package/dist/types/src/ai-service/types.d.ts.map +1 -1
  33. package/dist/types/src/conversation/conversation.d.ts +8 -8
  34. package/dist/types/src/conversation/conversation.d.ts.map +1 -1
  35. package/dist/types/src/index.d.ts +1 -0
  36. package/dist/types/src/index.d.ts.map +1 -1
  37. package/dist/types/src/session/index.d.ts +2 -0
  38. package/dist/types/src/session/index.d.ts.map +1 -0
  39. package/dist/types/src/session/session.d.ts +216 -0
  40. package/dist/types/src/session/session.d.ts.map +1 -0
  41. package/dist/types/src/session/session.test.d.ts +2 -0
  42. package/dist/types/src/session/session.test.d.ts.map +1 -0
  43. package/dist/types/src/testing/index.d.ts +1 -0
  44. package/dist/types/src/testing/index.d.ts.map +1 -1
  45. package/dist/types/src/testing/ollama.d.ts +6 -0
  46. package/dist/types/src/testing/ollama.d.ts.map +1 -0
  47. package/package.json +13 -13
  48. package/src/ai-service/client.test.ts +14 -14
  49. package/src/ai-service/defs.ts +18 -3
  50. package/src/ai-service/edge-client.ts +3 -2
  51. package/src/ai-service/ollama-client.ts +50 -24
  52. package/src/ai-service/parser.ts +4 -2
  53. package/src/ai-service/stream.test.ts +28 -4
  54. package/src/ai-service/types.ts +2 -2
  55. package/src/bin/chat.ts +3 -3
  56. package/src/conversation/conversation.test.ts +3 -3
  57. package/src/conversation/conversation.ts +17 -1
  58. package/src/cypher/query-executor.test.ts +3 -3
  59. package/src/cypher/schema.test.ts +1 -1
  60. package/src/index.ts +1 -0
  61. package/src/session/index.ts +5 -0
  62. package/src/session/session.test.ts +257 -0
  63. package/src/session/session.ts +375 -0
  64. package/src/testing/artifacts.test.ts +6 -6
  65. package/src/testing/index.ts +1 -0
  66. package/src/testing/object-extraction.test.ts +2 -2
  67. package/src/testing/ollama.ts +15 -0
  68. package/src/testing/query.test.ts +5 -5
  69. package/src/testing/test-schema.ts +5 -5
  70. package/dist/lib/browser/chunk-AXUUNA7O.mjs +0 -565
  71. package/dist/lib/browser/chunk-AXUUNA7O.mjs.map +0 -7
  72. package/dist/lib/node/chunk-QPHGQINW.cjs +0 -577
  73. package/dist/lib/node/chunk-QPHGQINW.cjs.map +0 -7
  74. package/dist/lib/node-esm/chunk-GOVFOBIB.mjs +0 -567
  75. package/dist/lib/node-esm/chunk-GOVFOBIB.mjs.map +0 -7
@@ -1,1684 +1,366 @@
1
1
  import {
2
+ AIServiceEdgeClient,
3
+ DEFAULT_EDGE_MODEL,
4
+ DEFAULT_EDGE_MODELS,
5
+ DEFAULT_OLLAMA_ENDPOINT,
6
+ DEFAULT_OLLAMA_MODEL,
7
+ DEFAULT_OLLAMA_MODELS,
2
8
  EchoDataSource,
9
+ GenerateRequest,
10
+ GenerationStreamImpl,
11
+ LLMModel,
12
+ MessageCollector,
13
+ MixedStreamParser,
14
+ OllamaClient,
15
+ StreamTransform,
16
+ ToolTypes,
17
+ createArtifactElement,
18
+ createGenerationStream,
19
+ emitMessageAsEvents,
3
20
  executeQuery,
4
21
  executeQueryPlan,
5
22
  formatInferredRelationshipLabel,
6
23
  formatJsonSchemaForLLM,
7
24
  formatNodeLabel,
8
25
  isReference,
9
- parseCypherQuery
10
- } from "./chunk-AXUUNA7O.mjs";
11
-
12
- // packages/experimental/assistant/src/ai-service/defs.ts
13
- var DEFAULT_LLM_MODEL = "@anthropic/claude-3-5-sonnet-20241022";
14
- var DEFAULT_LLM_MODELS = [
15
- // AI Gateway.
16
- // https://developers.cloudflare.com/ai-gateway
17
- "@anthropic/claude-3-5-haiku-20241022",
18
- "@anthropic/claude-3-5-sonnet-20241022",
19
- "@anthropic/claude-3-7-sonnet-20250219",
20
- // Workers AI.
21
- // https://developers.cloudflare.com/workers-ai/models
22
- "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
23
- "@hf/nousresearch/hermes-2-pro-mistral-7b",
24
- "@ollama/llama-3-1-nemotron-70b-instruct",
25
- "@ollama/llama-3-1-nemotron-mini-4b-instruct",
26
- "@ollama/llama-3-2-3b"
27
- ];
26
+ isToolUse,
27
+ mergeMessageBlock,
28
+ parseCypherQuery,
29
+ runLLM,
30
+ runTools
31
+ } from "./chunk-PND5VR6D.mjs";
28
32
 
29
- // packages/experimental/assistant/src/ai-service/edge-client.ts
33
+ // packages/experimental/assistant/src/session/session.ts
30
34
  import { Schema as S } from "effect";
31
- import { Message } from "@dxos/artifact";
35
+ import { defineTool, Message, ToolResult } from "@dxos/artifact";
36
+ import { Event, synchronized } from "@dxos/async";
37
+ import { createStatic } from "@dxos/echo-schema";
32
38
  import { invariant } from "@dxos/invariant";
33
- import { log as log2 } from "@dxos/log";
34
-
35
- // packages/experimental/assistant/src/ai-service/stream.ts
36
- import { Trigger } from "@dxos/async";
37
39
  import { log } from "@dxos/log";
38
-
39
- // packages/experimental/assistant/src/ai-service/util/sse.ts
40
- async function* iterSSEMessages(response, controller) {
41
- if (!response.body) {
42
- controller.abort();
43
- throw new Error("Attempted to iterate over a response with no body");
44
- }
45
- const sseDecoder = new SSEDecoder();
46
- const lineDecoder = new LineDecoder();
47
- const iter = readableStreamAsyncIterable(response.body);
48
- for await (const sseChunk of iterSSEChunks(iter)) {
49
- for (const line of lineDecoder.decode(sseChunk)) {
50
- const sse = sseDecoder.decode(line);
51
- if (sse) {
52
- yield sse;
53
- }
54
- }
55
- }
56
- for (const line of lineDecoder.flush()) {
57
- const sse = sseDecoder.decode(line);
58
- if (sse) {
59
- yield sse;
60
- }
61
- }
62
- }
63
- async function* iterSSEChunks(iterator) {
64
- let data = new Uint8Array();
65
- for await (const chunk of iterator) {
66
- if (chunk == null) {
67
- continue;
68
- }
69
- const binaryChunk = chunk instanceof ArrayBuffer ? new Uint8Array(chunk) : typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk;
70
- const newData = new Uint8Array(data.length + binaryChunk.length);
71
- newData.set(data);
72
- newData.set(binaryChunk, data.length);
73
- data = newData;
74
- let patternIndex;
75
- while ((patternIndex = findDoubleNewlineIndex(data)) !== -1) {
76
- yield data.slice(0, patternIndex);
77
- data = data.slice(patternIndex);
78
- }
79
- }
80
- if (data.length > 0) {
81
- yield data;
82
- }
40
+ function _ts_decorate(decorators, target, key, desc) {
41
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
42
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
43
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
44
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
83
45
  }
84
- var findDoubleNewlineIndex = (buffer) => {
85
- const newline = 10;
86
- const carriage = 13;
87
- for (let i = 0; i < buffer.length - 2; i++) {
88
- if (buffer[i] === newline && buffer[i + 1] === newline) {
89
- return i + 2;
90
- }
91
- if (buffer[i] === carriage && buffer[i + 1] === carriage) {
92
- return i + 2;
93
- }
94
- if (buffer[i] === carriage && buffer[i + 1] === newline && i + 3 < buffer.length && buffer[i + 2] === carriage && buffer[i + 3] === newline) {
95
- return i + 4;
96
- }
97
- }
98
- return -1;
99
- };
100
- var SSEDecoder = class {
101
- constructor() {
102
- this.event = null;
103
- this.data = [];
104
- this.chunks = [];
105
- }
106
- decode(line) {
107
- if (line.endsWith("\r")) {
108
- line = line.substring(0, line.length - 1);
109
- }
110
- if (!line) {
111
- if (!this.event && !this.data.length) {
112
- return null;
113
- }
114
- const sse = {
115
- event: this.event,
116
- data: this.data.join("\n"),
117
- raw: this.chunks
118
- };
119
- this.event = null;
120
- this.data = [];
121
- this.chunks = [];
122
- return sse;
123
- }
124
- this.chunks.push(line);
125
- if (line.startsWith(":")) {
126
- return null;
127
- }
128
- let [fieldname, _, value] = partition(line, ":");
129
- if (value.startsWith(" ")) {
130
- value = value.substring(1);
131
- }
132
- if (fieldname === "event") {
133
- this.event = value;
134
- } else if (fieldname === "data") {
135
- this.data.push(value);
136
- }
137
- return null;
138
- }
139
- };
140
- var partition = (str, delimiter) => {
141
- const index = str.indexOf(delimiter);
142
- if (index !== -1) {
143
- return [
144
- str.substring(0, index),
145
- delimiter,
146
- str.substring(index + delimiter.length)
147
- ];
148
- }
149
- return [
150
- str,
151
- "",
152
- ""
153
- ];
154
- };
155
- function readableStreamAsyncIterable(stream) {
156
- if (stream[Symbol.asyncIterator]) {
157
- return stream;
158
- }
159
- const reader = stream.getReader();
160
- return {
161
- next: async () => {
162
- try {
163
- const result = await reader.read();
164
- if (result?.done) {
165
- reader.releaseLock();
166
- }
167
- return result;
168
- } catch (e) {
169
- reader.releaseLock();
170
- throw e;
171
- }
172
- },
173
- return: async () => {
174
- const cancelPromise = reader.cancel();
175
- reader.releaseLock();
176
- await cancelPromise;
177
- return {
178
- done: true,
179
- value: void 0
180
- };
181
- },
182
- [Symbol.asyncIterator]() {
183
- return this;
184
- }
185
- };
186
- }
187
- var LineDecoder = class _LineDecoder {
188
- static {
189
- // prettier-ignore
190
- this.NEWLINE_CHARS = /* @__PURE__ */ new Set([
191
- "\n",
192
- "\r"
193
- ]);
194
- }
195
- static {
196
- this.NEWLINE_REGEXP = /\r\n|[\n\r]/g;
197
- }
198
- constructor() {
199
- this.buffer = [];
200
- this.trailingCR = false;
201
- }
202
- decode(chunk) {
203
- let text = this.decodeText(chunk);
204
- if (this.trailingCR) {
205
- text = "\r" + text;
206
- this.trailingCR = false;
207
- }
208
- if (text.endsWith("\r")) {
209
- this.trailingCR = true;
210
- text = text.slice(0, -1);
211
- }
212
- if (!text) {
213
- return [];
214
- }
215
- const trailingNewline = _LineDecoder.NEWLINE_CHARS.has(text[text.length - 1] || "");
216
- let lines = text.split(_LineDecoder.NEWLINE_REGEXP);
217
- if (trailingNewline) {
218
- lines.pop();
219
- }
220
- if (lines.length === 1 && !trailingNewline) {
221
- this.buffer.push(lines[0]);
222
- return [];
223
- }
224
- if (this.buffer.length > 0) {
225
- lines = [
226
- this.buffer.join("") + lines[0],
227
- ...lines.slice(1)
228
- ];
229
- this.buffer = [];
230
- }
231
- if (!trailingNewline) {
232
- this.buffer = [
233
- lines.pop() || ""
234
- ];
235
- }
236
- return lines;
237
- }
238
- decodeText(bytes) {
239
- if (bytes == null) {
240
- return "";
241
- }
242
- if (typeof bytes === "string") {
243
- return bytes;
244
- }
245
- if (typeof Buffer !== "undefined") {
246
- if (bytes instanceof Buffer) {
247
- return bytes.toString();
248
- }
249
- if (bytes instanceof Uint8Array) {
250
- return Buffer.from(bytes).toString();
251
- }
252
- 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.`);
253
- }
254
- if (typeof TextDecoder !== "undefined") {
255
- if (bytes instanceof Uint8Array || bytes instanceof ArrayBuffer) {
256
- this.textDecoder ??= new TextDecoder("utf8");
257
- return this.textDecoder.decode(bytes);
258
- }
259
- throw new Error(`Unexpected: received non-Uint8Array/ArrayBuffer (${bytes.constructor.name}) in a web platform. Please report this error.`);
260
- }
261
- throw new Error("Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.");
262
- }
263
- flush() {
264
- if (!this.buffer.length && !this.trailingCR) {
265
- return [];
266
- }
267
- const lines = [
268
- this.buffer.join("")
269
- ];
270
- this.buffer = [];
271
- this.trailingCR = false;
272
- return lines;
46
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/session/session.ts";
47
+ var AISession = class _AISession {
48
+ constructor(_options) {
49
+ this._options = _options;
50
+ this._parser = new MixedStreamParser();
51
+ this._pending = [];
52
+ this._history = [];
53
+ this.message = this._parser.message;
54
+ this.block = this._parser.block;
55
+ this.update = this._parser.update;
56
+ this.streamEvent = this._parser.streamEvent;
57
+ this.userMessage = new Event();
58
+ this._parser.message.on((message) => {
59
+ this._pending.push(message);
60
+ });
273
61
  }
274
- };
275
-
276
- // packages/experimental/assistant/src/ai-service/stream.ts
277
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/stream.ts";
278
- var createGenerationStream = (response, controller = new AbortController()) => {
279
- const iterator = async function* () {
280
- for await (const sse of iterSSEMessages(response, controller)) {
281
- if (sse.event === "completion") {
282
- try {
283
- yield JSON.parse(sse.data);
284
- } catch (err) {
285
- log.error("could not parse message into JSON:", {
286
- data: sse.data,
287
- raw: sse.raw
288
- }, {
289
- F: __dxlog_file,
290
- L: 22,
291
- S: this,
292
- C: (f, a) => f(...a)
62
+ async run(options) {
63
+ const systemTools = [
64
+ defineTool("system", {
65
+ name: "query_artifact_definitions",
66
+ description: "Query the available artifact definitions",
67
+ schema: S.Struct({}),
68
+ execute: async () => {
69
+ return ToolResult.Success({
70
+ artifactDefinitions: options.artifacts.map((artifact) => ({
71
+ id: artifact.id,
72
+ name: artifact.name,
73
+ description: artifact.description
74
+ }))
293
75
  });
294
- throw err;
295
- }
296
- }
297
- 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") {
298
- try {
299
- yield JSON.parse(sse.data);
300
- } catch (err) {
301
- log.error("could not parse message into JSON:", {
302
- data: sse.data,
303
- raw: sse.raw
304
- }, {
305
- F: __dxlog_file,
306
- L: 38,
307
- S: this,
308
- C: (f, a) => f(...a)
309
- });
310
- throw err;
311
- }
312
- }
313
- if (sse.event === "ping") {
314
- continue;
315
- }
316
- if (sse.event === "error") {
317
- throw new Error(`Message generation error: ${sse.data}`);
318
- }
319
- }
320
- };
321
- return new GenerationStreamImpl(controller, iterator);
322
- };
323
- var GenerationStreamImpl = class {
324
- constructor(_controller, _getIterator) {
325
- this._controller = _controller;
326
- this._getIterator = _getIterator;
327
- this._done = new Trigger();
328
- this._iterator = void 0;
329
- }
330
- /**
331
- * Returns an iterator over the stream.
332
- */
333
- [Symbol.asyncIterator]() {
334
- return this._createIterator();
335
- }
336
- /**
337
- * Aborts the stream.
338
- */
339
- abort() {
340
- this._controller.abort();
341
- }
342
- /**
343
- * Returns the complete message list generated by the AI service.
344
- */
345
- async complete() {
346
- await this._done.wait();
347
- }
348
- /**
349
- * Creates an iterator over the stream.
350
- */
351
- _createIterator() {
352
- const self = this;
353
- return this._iterator ??= (() => {
354
- const generator = async function* () {
355
- try {
356
- for await (const event of self._getIterator()) {
357
- yield event;
358
- }
359
- this._done.wake();
360
- } catch (err) {
361
- this._done.throw(err);
362
- throw err;
363
76
  }
364
- };
365
- return generator.call(this);
366
- })();
367
- }
368
- };
369
-
370
- // packages/experimental/assistant/src/ai-service/edge-client.ts
371
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/edge-client.ts";
372
- var AIServiceEdgeClient = class {
373
- constructor({ endpoint }) {
374
- invariant(endpoint, "endpoint is required", {
375
- F: __dxlog_file2,
376
- L: 23,
377
- S: this,
378
- A: [
379
- "endpoint",
380
- "'endpoint is required'"
381
- ]
382
- });
383
- this._endpoint = endpoint;
384
- }
385
- /**
386
- * @deprecated
387
- */
388
- // TODO(burdon): Remove.
389
- async appendMessages(messages) {
390
- const url = `${this._endpoint}/message`;
391
- const res = await fetch(url, {
392
- method: "POST",
393
- headers: {
394
- "Content-Type": "application/json"
395
- },
396
- body: JSON.stringify({
397
- messages: await S.encodePromise(S.Array(Message))(messages)
398
77
  })
399
- });
400
- if (!res.ok) {
401
- throw new Error(`${await res.text()} [${url}]`);
402
- }
403
- }
404
- /**
405
- * Process request and open message stream.
406
- */
407
- async exec(request) {
408
- log2.info("requesting", {
409
- endpoint: this._endpoint
410
- }, {
411
- F: __dxlog_file2,
412
- L: 50,
413
- S: this,
414
- C: (f, a) => f(...a)
415
- });
416
- const controller = new AbortController();
417
- const response = await fetch(`${this._endpoint}/generate`, {
418
- signal: controller.signal,
419
- method: "POST",
420
- headers: {
421
- "Content-Type": "application/json"
422
- },
423
- body: JSON.stringify({
424
- // TODO(dmaretskyi): Errors if tools are not provided.
425
- tools: request.tools ?? [],
426
- ...request
427
- })
428
- });
429
- invariant(response.body instanceof ReadableStream, void 0, {
430
- F: __dxlog_file2,
431
- L: 63,
432
- S: this,
433
- A: [
434
- "response.body instanceof ReadableStream",
435
- ""
436
- ]
437
- });
438
- return createGenerationStream(response, controller);
439
- }
440
- };
441
-
442
- // packages/experimental/assistant/src/ai-service/message-collector.ts
443
- var MessageCollector = class {
444
- constructor() {
445
- this._messages = [];
446
- this._pendingMessage = void 0;
447
- }
448
- get messages() {
449
- return this._messages;
450
- }
451
- get pendingMessage() {
452
- return this._pendingMessage;
453
- }
454
- push(event) {
455
- switch (event.type) {
456
- case "message_start":
457
- this._pendingMessage = event.message;
458
- break;
459
- case "message_stop":
460
- if (this._pendingMessage) {
461
- this._messages.push(this._pendingMessage);
462
- this._pendingMessage = void 0;
463
- }
464
- break;
465
- case "content_block_start":
466
- if (this._pendingMessage) {
467
- this._pendingMessage.content = this._pendingMessage.content || [];
468
- this._pendingMessage.content[event.index] = event.content;
469
- }
470
- break;
471
- case "content_block_delta":
472
- if (this._pendingMessage && this._pendingMessage.content) {
473
- const block = this._pendingMessage.content[event.index];
474
- if (block && event.delta.type === "text_delta" && block.type === "text") {
475
- block.text = (block.text || "") + event.delta.text;
78
+ ];
79
+ switch (this._options.operationModel) {
80
+ case "planning":
81
+ systemTools.push(defineTool("system", {
82
+ name: "create_plan",
83
+ description: "Create a plan. Make sure that each step is independent and can be executed solely on the data returned by the previous step. The steps only share the data that is specified in the plan.",
84
+ schema: S.Struct({
85
+ goal: S.String.annotations({
86
+ description: "The goal that the plan will achieve."
87
+ }),
88
+ steps: S.Array(S.Struct({
89
+ action: S.String.annotations({
90
+ description: "Complete, detailed action to perform."
91
+ }),
92
+ input: S.String.annotations({
93
+ description: "The required input data for this step. First step must not require any data."
94
+ }),
95
+ output: S.String.annotations({
96
+ description: "The output data from this step. This is passed to the next step as input."
97
+ }),
98
+ requiredArtifactIds: S.Array(S.String).annotations({
99
+ description: "The ids of the artifacts required to perform this step.",
100
+ examples: [
101
+ [
102
+ "artifact:dxos.org/example/Test"
103
+ ]
104
+ ]
105
+ })
106
+ })).annotations({
107
+ description: "Steps"
108
+ })
109
+ }),
110
+ execute: async ({ goal, steps }) => {
111
+ const missingArtifactIds = steps.flatMap((step) => step.requiredArtifactIds).filter((artifactId) => !options.artifacts.some((artifact) => artifact.id === artifactId));
112
+ if (missingArtifactIds.length > 0) {
113
+ return ToolResult.Error(`One or more artifact ids are invalid: ${missingArtifactIds.join(", ")}`);
114
+ }
115
+ const stepResults = [];
116
+ for (const step of steps) {
117
+ log("executing step", {
118
+ action: step.action
119
+ }, {
120
+ F: __dxlog_file,
121
+ L: 160,
122
+ S: this,
123
+ C: (f, a) => f(...a)
124
+ });
125
+ const session = new _AISession({
126
+ operationModel: "immediate"
127
+ });
128
+ session.message.on((ev) => this.message.emit(ev));
129
+ session.block.on((ev) => this.block.emit(ev));
130
+ session.update.on((ev) => this.update.emit(ev));
131
+ session.streamEvent.on((ev) => this.streamEvent.emit(ev));
132
+ session.userMessage.on((ev) => this.userMessage.emit(ev));
133
+ const messages = await session.run({
134
+ client: options.client,
135
+ artifacts: options.artifacts,
136
+ tools: options.tools,
137
+ history: options.history,
138
+ extensions: options.extensions,
139
+ generationOptions: options.generationOptions,
140
+ requiredArtifactIds: step.requiredArtifactIds.slice(),
141
+ prompt: `
142
+ You are an agent that executes the subtask in a plan.
143
+
144
+ While you know the plan's goal only focus on your subtask
145
+ Use the data from the previous step to complete your task.
146
+ Return all of the data from "output" in your last message.
147
+
148
+ Current step (that you need to complete):
149
+ ${JSON.stringify({
150
+ goal,
151
+ input: step.input,
152
+ output: step.output,
153
+ yourTask: step.action
154
+ }, null, 2)}
155
+
156
+ Previous step results:
157
+ ${JSON.stringify(stepResults.at(-1))}.
158
+ `
159
+ });
160
+ const result = messages.at(-1);
161
+ stepResults.push(result);
162
+ }
163
+ return ToolResult.Success({
164
+ stepResults
165
+ });
476
166
  }
477
- }
167
+ }));
478
168
  break;
479
- case "content_block_stop":
480
- if (this._pendingMessage && this._pendingMessage.content) {
481
- const block = this._pendingMessage.content[event.index];
482
- if (block) {
483
- block.pending = false;
169
+ case "immediate":
170
+ systemTools.push(defineTool("system", {
171
+ name: "require_artifact_definitions",
172
+ description: "Require the use of specific artifact definitions. This will allow the model to interact with artifact definitions and use their tools.",
173
+ schema: S.Struct({
174
+ artifactDefinitionIds: S.Array(S.String).annotations({
175
+ description: "The ids of the artifact definitions to require",
176
+ examples: [
177
+ [
178
+ "artifact:dxos.org/example/Test"
179
+ ]
180
+ ]
181
+ })
182
+ }),
183
+ execute: async ({ artifactDefinitionIds }) => {
184
+ const missingArtifactDefinitionIds = artifactDefinitionIds.filter((artifactId) => !options.artifacts.some((artifact) => artifact.id === artifactId));
185
+ if (missingArtifactDefinitionIds.length > 0) {
186
+ return ToolResult.Error(`One or more artifact definition ids are invalid: ${missingArtifactDefinitionIds.join(", ")}`);
187
+ }
188
+ for (const artifactDefinitionId of artifactDefinitionIds) {
189
+ requiredArtifactIds.add(artifactDefinitionId);
190
+ }
191
+ return ToolResult.Success({});
484
192
  }
485
- }
486
- break;
487
- case "message_delta":
488
- if (this._pendingMessage && event.delta) {
489
- Object.assign(this._pendingMessage, event.delta);
490
- }
193
+ }));
491
194
  break;
492
195
  }
493
- }
494
- pushMessage(message) {
495
- this._messages.push(message);
496
- }
497
- };
498
- function* emitMessageAsEvents(message) {
499
- yield {
500
- type: "message_start",
501
- message
502
- };
503
- yield {
504
- type: "message_stop"
505
- };
506
- }
507
-
508
- // packages/experimental/assistant/src/ai-service/ollama-client.ts
509
- import { defineTool, ToolResult as ToolResult2 } from "@dxos/artifact";
510
- import { ObjectId as ObjectId3, S as S3 } from "@dxos/echo-schema";
511
- import { invariant as invariant3 } from "@dxos/invariant";
512
- import { log as log4 } from "@dxos/log";
513
-
514
- // packages/experimental/assistant/src/ai-service/types.ts
515
- import { Schema as S2 } from "effect";
516
- import { Tool, Message as Message2, SpaceIdSchema } from "@dxos/artifact";
517
- import { ObjectId } from "@dxos/echo-schema";
518
- var createArtifactElement = (id) => `<artifact id=${id} />`;
519
- var LLMModel = S2.Literal(...DEFAULT_LLM_MODELS);
520
- var ToolTypes = Object.freeze({
521
- // TODO(dmaretskyi): Not implemented yet.
522
- // DatabaseQuery: 'database_query',
523
- TextToImage: "text_to_image"
524
- });
525
- var GenerateRequest = S2.Struct({
526
- spaceId: S2.optional(SpaceIdSchema),
527
- threadId: S2.optional(ObjectId),
528
- /**
529
- * Preferred model or system default.
530
- */
531
- model: S2.optional(LLMModel),
532
- /**
533
- * Tools available for the LLM.
534
- */
535
- tools: S2.optional(S2.Array(Tool).pipe(S2.mutable)),
536
- /**
537
- * System instructions to the LLM.
538
- */
539
- systemPrompt: S2.optional(S2.String),
540
- /**
541
- * History of messages to include in the context window.
542
- */
543
- // TODO(burdon): Rename messages.
544
- history: S2.optional(S2.Array(Message2)),
545
- /**
546
- * Current request.
547
- */
548
- prompt: S2.optional(Message2)
549
- });
550
-
551
- // packages/experimental/assistant/src/conversation/conversation.ts
552
- import { ToolResult, Message as Message3 } from "@dxos/artifact";
553
- import { createStatic, ObjectId as ObjectId2 } from "@dxos/echo-schema";
554
- import { invariant as invariant2 } from "@dxos/invariant";
555
- import { log as log3 } from "@dxos/log";
556
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/conversation/conversation.ts";
557
- var runLLM = async (params) => {
558
- let conversationResult = null;
559
- const history = params.history ?? [];
560
- const generate = async () => {
561
- log3("llm generate", {
562
- tools: params.tools
563
- }, {
564
- F: __dxlog_file3,
565
- L: 44,
566
- S: void 0,
567
- C: (f, a) => f(...a)
568
- });
569
- const beginTs = Date.now();
570
- const stream = await params.client.exec({
571
- model: params.model,
572
- spaceId: params.spaceId,
573
- threadId: params.threadId,
574
- history,
575
- systemPrompt: params.system,
576
- tools: params.tools
577
- });
578
- for await (const event of stream) {
579
- params.logger?.(event);
580
- }
581
- await stream.complete();
582
- const messages = [];
583
- const message = messages.at(-1);
584
- invariant2(message, void 0, {
585
- F: __dxlog_file3,
586
- L: 63,
587
- S: void 0,
588
- A: [
589
- "message",
590
- ""
591
- ]
592
- });
593
- log3("llm result", {
594
- time: Date.now() - beginTs,
595
- message
596
- }, {
597
- F: __dxlog_file3,
598
- L: 65,
599
- S: void 0,
600
- C: (f, a) => f(...a)
601
- });
602
- invariant2(message, void 0, {
603
- F: __dxlog_file3,
604
- L: 66,
605
- S: void 0,
606
- A: [
607
- "message",
608
- ""
609
- ]
610
- });
611
- params.logger?.({
612
- type: "message",
613
- message
614
- });
615
- history.push(...messages.map((message2) => ({
616
- ...message2,
617
- content: message2.content.filter((block) => block.type !== "image")
618
- })));
619
- const isToolUse2 = message.content.at(-1)?.type === "tool_use";
620
- if (isToolUse2) {
621
- const toolCalls = message.content.filter((block) => block.type === "tool_use");
622
- invariant2(toolCalls.length === 1, void 0, {
623
- F: __dxlog_file3,
624
- L: 77,
625
- S: void 0,
626
- A: [
627
- "toolCalls.length === 1",
628
- ""
629
- ]
630
- });
631
- const toolCall = toolCalls[0];
632
- const tool = params.tools.find((tool2) => tool2.name === toolCall.name);
633
- if (!tool) {
634
- throw new Error(`Tool not found: ${toolCall.name}`);
635
- }
636
- invariant2(tool.execute, void 0, {
637
- F: __dxlog_file3,
638
- L: 84,
639
- S: void 0,
640
- A: [
641
- "tool.execute",
642
- ""
643
- ]
644
- });
645
- const toolResult = await tool.execute(toolCall.input);
646
- switch (toolResult.kind) {
647
- case "error": {
648
- log3.warn("tool error", {
649
- message: toolResult.message
650
- }, {
651
- F: __dxlog_file3,
652
- L: 88,
653
- S: void 0,
654
- C: (f, a) => f(...a)
655
- });
656
- const resultMessage = {
657
- id: ObjectId2.random(),
658
- spaceId: params.spaceId,
659
- threadId: params.threadId,
660
- role: "user",
661
- content: [
662
- {
663
- type: "tool_result",
664
- toolUseId: toolCall.id,
665
- content: toolResult.message,
666
- isError: true
667
- }
668
- ]
669
- };
670
- params.logger?.({
671
- type: "message",
672
- message: resultMessage
673
- });
674
- history.push(resultMessage);
675
- return true;
676
- }
677
- case "success": {
678
- log3("tool success", {
679
- result: toolResult.result
680
- }, {
681
- F: __dxlog_file3,
682
- L: 110,
683
- S: void 0,
684
- C: (f, a) => f(...a)
685
- });
686
- const resultMessage = {
687
- id: ObjectId2.random(),
688
- spaceId: params.spaceId,
689
- threadId: params.threadId,
690
- role: "user",
691
- content: [
692
- {
693
- type: "tool_result",
694
- toolUseId: toolCall.id,
695
- content: JSON.stringify(toolResult.result)
696
- }
697
- ]
698
- };
699
- params.logger?.({
700
- type: "message",
701
- message: resultMessage
702
- });
703
- history.push(resultMessage);
704
- return true;
705
- }
706
- case "break": {
707
- log3("tool break", {
708
- result: toolResult.result
709
- }, {
710
- F: __dxlog_file3,
711
- L: 131,
712
- S: void 0,
713
- C: (f, a) => f(...a)
714
- });
715
- conversationResult = toolResult.result;
716
- return false;
717
- }
718
- }
719
- }
720
- return false;
721
- };
722
- while (await generate()) {
723
- }
724
- return {
725
- result: conversationResult,
726
- history
727
- };
728
- };
729
- var isToolUse = (message, { onlyToolNames } = {}) => {
730
- const block = message.content.at(-1);
731
- invariant2(block, void 0, {
732
- F: __dxlog_file3,
733
- L: 152,
734
- S: void 0,
735
- A: [
736
- "block",
737
- ""
738
- ]
739
- });
740
- return block.type === "tool_use" && (!onlyToolNames || onlyToolNames.includes(block.name));
741
- };
742
- var runTools = async ({ message, tools, extensions }) => {
743
- const toolCalls = message.content.filter((block) => block.type === "tool_use");
744
- invariant2(toolCalls.length === 1, void 0, {
745
- F: __dxlog_file3,
746
- L: 174,
747
- S: void 0,
748
- A: [
749
- "toolCalls.length === 1",
750
- ""
751
- ]
752
- });
753
- const toolCall = toolCalls[0];
754
- const tool = tools.find((tool2) => tool2.name === toolCall.name);
755
- if (!tool) {
756
- throw new Error(`Tool not found: ${toolCall.name}`);
757
- }
758
- let toolResult;
759
- try {
760
- invariant2(tool.execute, void 0, {
761
- F: __dxlog_file3,
762
- L: 183,
763
- S: void 0,
764
- A: [
765
- "tool.execute",
766
- ""
767
- ]
768
- });
769
- toolResult = await tool.execute(toolCall.input, {
770
- extensions
771
- });
772
- } catch (error) {
773
- log3("tool error", {
774
- error
775
- }, {
776
- F: __dxlog_file3,
777
- L: 186,
778
- S: void 0,
779
- C: (f, a) => f(...a)
780
- });
781
- toolResult = ToolResult.Error(error.message);
782
- }
783
- switch (toolResult.kind) {
784
- case "error": {
785
- log3("tool error", {
786
- message: toolResult.message
787
- }, {
788
- F: __dxlog_file3,
789
- L: 192,
790
- S: void 0,
791
- C: (f, a) => f(...a)
792
- });
793
- const resultMessage = createStatic(Message3, {
196
+ this._history = [
197
+ ...options.history
198
+ ];
199
+ this._pending = [
200
+ createStatic(Message, {
794
201
  role: "user",
795
202
  content: [
796
203
  {
797
- type: "tool_result",
798
- toolUseId: toolCall.id,
799
- content: toolResult.message,
800
- isError: true
204
+ type: "text",
205
+ text: options.prompt
801
206
  }
802
207
  ]
803
- });
804
- return {
805
- type: "continue",
806
- message: resultMessage
807
- };
808
- }
809
- case "success": {
810
- log3("tool success", {
811
- result: toolResult.result
812
- }, {
813
- F: __dxlog_file3,
814
- L: 213,
815
- S: void 0,
816
- C: (f, a) => f(...a)
817
- });
818
- const resultMessage = createStatic(Message3, {
819
- role: "user",
820
- content: [
821
- {
822
- type: "tool_result",
823
- toolUseId: toolCall.id,
824
- content: typeof toolResult.result === "string" ? toolResult.result : JSON.stringify(toolResult.result) ?? ""
825
- },
826
- ...toolResult.extractContentBlocks ?? []
827
- ]
828
- });
829
- return {
830
- type: "continue",
831
- message: resultMessage
832
- };
833
- }
834
- case "break": {
835
- log3("tool break", {
836
- result: toolResult.result
837
- }, {
838
- F: __dxlog_file3,
839
- L: 234,
840
- S: void 0,
841
- C: (f, a) => f(...a)
842
- });
843
- return {
844
- type: "break",
845
- result: toolResult.result
846
- };
847
- }
848
- }
849
- };
850
-
851
- // packages/experimental/assistant/src/ai-service/ollama-client.ts
852
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/ollama-client.ts";
853
- var DEFAULT_OLLAMA_URL = "http://localhost:11434";
854
- var DEFAULT_OLLAMA_MODEL = "llama3.2:1b";
855
- var OllamaClient = class _OllamaClient {
856
- /**
857
- * Create a test client with small local model and no temperature for predictable results.
858
- */
859
- static createClient(options) {
860
- return new _OllamaClient({
861
- tools: options?.tools,
862
- overrides: {
863
- model: DEFAULT_OLLAMA_MODEL,
864
- temperature: 0
865
- }
866
- });
867
- }
868
- /**
869
- * Check if Ollama server is running and accessible.
870
- * @returns Promise that resolves to true if Ollama is running, false otherwise.
871
- */
872
- static async isRunning() {
873
- try {
874
- const response = await fetch(`${DEFAULT_OLLAMA_URL}/api/version`, {
875
- method: "GET",
876
- headers: {
877
- "Content-Type": "application/json"
878
- },
879
- signal: AbortSignal.timeout(100)
880
- });
881
- return response.ok;
882
- } catch (error) {
883
- return false;
884
- }
885
- }
886
- constructor({ endpoint, tools, overrides, maxToolInvocations }) {
887
- this._endpoint = endpoint ?? DEFAULT_OLLAMA_URL;
888
- this._tools = tools ?? [];
889
- this._modelOverride = overrides?.model;
890
- this._temperatureOverride = overrides?.temperature;
891
- this._maxToolInvocations = maxToolInvocations ?? 3;
892
- }
893
- _getEmbededTools(clientTools) {
894
- return [
895
- ...this._tools,
896
- ...clientTools.filter((tool) => tool.type !== void 0 && WELL_KNOWN_TOOLS[tool.type]).map((tool) => WELL_KNOWN_TOOLS[tool.type])
208
+ })
897
209
  ];
898
- }
899
- async *_generateStream(request, signal) {
900
- invariant3(request.prompt === void 0, "Prompt must be in history", {
901
- F: __dxlog_file4,
902
- L: 99,
903
- S: this,
904
- A: [
905
- "request.prompt === undefined",
906
- "'Prompt must be in history'"
907
- ]
908
- });
909
- const ollamaRequest = {
910
- model: this._modelOverride ?? request.model ?? "llama3",
911
- messages: [],
912
- stream: true,
913
- options: {
914
- temperature: this._temperatureOverride ?? 0.7
915
- }
916
- };
917
- if (request.systemPrompt) {
918
- ollamaRequest.messages.push({
919
- role: "system",
920
- content: request.systemPrompt
921
- });
922
- }
923
- if (request.history?.length) {
924
- for (const message of request.history) {
925
- const role = message.role === "assistant" ? "assistant" : "user";
926
- if (message.content.length === 0) {
927
- continue;
928
- }
929
- for (const content of message.content) {
930
- switch (content.type) {
931
- case "text": {
932
- ollamaRequest.messages.push({
933
- role,
934
- content: content.text
935
- });
936
- break;
937
- }
938
- case "tool_use": {
939
- ollamaRequest.messages.push({
940
- role: "assistant",
941
- content: "",
942
- tool_calls: [
943
- {
944
- function: {
945
- name: content.name,
946
- arguments: content.input
947
- }
948
- }
949
- ]
950
- });
951
- break;
952
- }
953
- case "tool_result": {
954
- ollamaRequest.messages.push({
955
- role: "tool",
956
- content: JSON.stringify({
957
- type: "tool_result",
958
- toolUseId: content.toolUseId,
959
- content: content.content
960
- })
961
- });
962
- break;
963
- }
964
- }
965
- }
966
- }
967
- }
968
- if (request.tools?.length || this._tools.length) {
969
- const allTools = [
970
- ...request.tools ?? [],
971
- ...this._tools
972
- ];
973
- const withWellKnownTools = allTools.map((tool) => tool.type !== void 0 && WELL_KNOWN_TOOLS[tool.type] ? WELL_KNOWN_TOOLS[tool.type] : tool);
974
- if (withWellKnownTools.length > 0) {
975
- ollamaRequest.tools = withWellKnownTools.map((tool) => ({
976
- type: "function",
977
- function: {
978
- name: tool.name,
979
- description: tool.description,
980
- parameters: {
981
- type: "object",
982
- properties: tool.parameters?.properties,
983
- required: tool.parameters?.required
984
- }
985
- }
986
- }));
987
- }
988
- }
989
- log4.info("generate request", {
990
- history: request.history,
991
- prompt: request.prompt,
992
- ollamaRequest
993
- }, {
994
- F: __dxlog_file4,
995
- L: 193,
996
- S: this,
997
- C: (f, a) => f(...a)
998
- });
999
- const response = await fetch(`${this._endpoint}/api/chat`, {
1000
- method: "POST",
1001
- headers: {
1002
- "Content-Type": "application/json"
1003
- },
1004
- body: JSON.stringify(ollamaRequest),
1005
- signal
1006
- });
1007
- if (!response.ok) {
1008
- throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
1009
- }
1010
- if (!response.body) {
1011
- throw new Error("Ollama API returned no response body");
1012
- }
1013
- const reader = response.body.pipeThrough(new TextDecoderStream()).pipeThrough(new OllamaDecoderStream()).getReader();
210
+ this.userMessage.emit(this._pending.at(-1));
211
+ this._stream = void 0;
212
+ let error;
213
+ const requiredArtifactIds = new Set(options.requiredArtifactIds ?? []);
1014
214
  try {
1015
- const messageId = ObjectId3.random();
1016
- yield {
1017
- type: "message_start",
1018
- message: {
1019
- id: messageId,
1020
- role: "assistant",
1021
- content: []
1022
- }
1023
- };
1024
- const textBlock = {
1025
- type: "text",
1026
- text: ""
1027
- };
1028
- let currentBlockIndex = 0;
1029
- yield {
1030
- type: "content_block_start",
1031
- index: currentBlockIndex,
1032
- content: textBlock
1033
- };
1034
- while (true) {
1035
- const { done, value: data } = await reader.read();
1036
- if (done) {
1037
- break;
1038
- }
1039
- log4.info("ollama data", {
1040
- data
215
+ let more = false;
216
+ do {
217
+ const tools = [
218
+ ...systemTools,
219
+ ...options.tools,
220
+ ...options.artifacts.filter((artifact) => requiredArtifactIds.has(artifact.id)).flatMap((artifact) => artifact.tools)
221
+ ];
222
+ log("request", {
223
+ pending: this._pending.length,
224
+ history: this._history.length,
225
+ tools: tools.map((tool) => tool.name)
1041
226
  }, {
1042
- F: __dxlog_file4,
1043
- L: 256,
227
+ F: __dxlog_file,
228
+ L: 264,
1044
229
  S: this,
1045
230
  C: (f, a) => f(...a)
1046
231
  });
1047
- if (data.message?.content) {
1048
- yield {
1049
- type: "content_block_delta",
1050
- index: 0,
1051
- delta: {
1052
- type: "text_delta",
1053
- text: data.message.content
1054
- }
1055
- };
1056
- }
1057
- if (data.message?.tool_calls && data.message?.tool_calls?.length > 0) {
1058
- if (data.message.tool_calls.length > 1) {
1059
- log4.warn("Ollama returned multiple tool calls. Only the first one will be used.", void 0, {
1060
- F: __dxlog_file4,
1061
- L: 273,
1062
- S: this,
1063
- C: (f, a) => f(...a)
1064
- });
1065
- }
1066
- yield {
1067
- type: "content_block_stop",
1068
- index: currentBlockIndex++
1069
- };
1070
- yield {
1071
- type: "content_block_start",
1072
- index: currentBlockIndex,
1073
- content: {
1074
- type: "tool_use",
1075
- id: ObjectId3.random(),
1076
- name: data.message.tool_calls[0].function.name,
1077
- input: sanitizeToolArguments(data.message.tool_calls[0].function.arguments)
1078
- }
1079
- };
1080
- yield {
1081
- type: "message_delta",
1082
- delta: {
1083
- stopReason: "tool_use"
1084
- }
1085
- };
1086
- }
1087
- }
1088
- yield {
1089
- type: "content_block_stop",
1090
- index: currentBlockIndex
1091
- };
1092
- yield {
1093
- type: "message_stop"
1094
- };
1095
- } finally {
1096
- reader.releaseLock();
1097
- }
1098
- }
1099
- async exec(request) {
1100
- const controller = new AbortController();
1101
- try {
1102
- return new GenerationStreamImpl(controller, async function* () {
1103
- const collector = new MessageCollector();
1104
- let toolInvocations = 0;
1105
- while (true) {
1106
- for await (const event of this._generateStream({
1107
- ...request,
1108
- history: [
1109
- ...request.history ?? [],
1110
- ...request.prompt ? [
1111
- request.prompt
1112
- ] : [],
1113
- ...collector.messages
1114
- ],
1115
- prompt: void 0
1116
- }, controller.signal)) {
1117
- collector.push(event);
1118
- yield event;
1119
- }
1120
- if (collector.messages.length > 0 && !isToolUse(collector.messages.at(-1), {
1121
- onlyToolNames: this._getEmbededTools(request.tools ?? []).map((tool) => tool.name)
1122
- })) {
1123
- break;
1124
- }
1125
- if (++toolInvocations > this._maxToolInvocations) {
1126
- throw new Error("Maximum number of tool invocations reached");
1127
- }
1128
- const result = await runTools({
1129
- message: collector.messages.at(-1),
1130
- tools: this._getEmbededTools(request.tools ?? [])
232
+ this._stream = await options.client.exec({
233
+ ...options.generationOptions ?? {},
234
+ // TODO(burdon): Rename messages or separate history/message.
235
+ history: [
236
+ ...this._history,
237
+ ...this._pending
238
+ ],
239
+ tools,
240
+ systemPrompt: options.systemPrompt ?? createBaseInstructions({
241
+ availableArtifacts: Array.from(requiredArtifactIds),
242
+ operationModel: this._options.operationModel
243
+ })
244
+ });
245
+ await this._parser.parse(this._stream);
246
+ await this._stream.complete();
247
+ log("response", {
248
+ pending: this._pending
249
+ }, {
250
+ F: __dxlog_file,
251
+ L: 289,
252
+ S: this,
253
+ C: (f, a) => f(...a)
254
+ });
255
+ more = false;
256
+ const message = this._pending.at(-1);
257
+ invariant(message, void 0, {
258
+ F: __dxlog_file,
259
+ L: 294,
260
+ S: this,
261
+ A: [
262
+ "message",
263
+ ""
264
+ ]
265
+ });
266
+ if (isToolUse(message)) {
267
+ log("tool request...", void 0, {
268
+ F: __dxlog_file,
269
+ L: 296,
270
+ S: this,
271
+ C: (f, a) => f(...a)
1131
272
  });
1132
- switch (result.type) {
273
+ const response = await runTools({
274
+ message: this._pending.at(-1),
275
+ tools,
276
+ extensions: options.extensions ?? {}
277
+ });
278
+ log("tool response", {
279
+ response
280
+ }, {
281
+ F: __dxlog_file,
282
+ L: 303,
283
+ S: this,
284
+ C: (f, a) => f(...a)
285
+ });
286
+ switch (response.type) {
1133
287
  case "continue": {
1134
- collector.pushMessage(result.message);
1135
- yield* emitMessageAsEvents(result.message);
288
+ this._pending = [
289
+ ...this._pending,
290
+ response.message
291
+ ];
292
+ this.userMessage.emit(response.message);
293
+ more = true;
1136
294
  break;
1137
295
  }
1138
- case "break": {
1139
- throw new Error("Tool use breaks not allowed");
1140
- }
1141
296
  }
1142
297
  }
1143
- }.bind(this));
1144
- } catch (error) {
1145
- controller.abort();
298
+ } while (more);
299
+ } catch (err) {
300
+ log.catch(err, void 0, {
301
+ F: __dxlog_file,
302
+ L: 315,
303
+ S: this,
304
+ C: (f, a) => f(...a)
305
+ });
306
+ if (err instanceof Error && err.message.includes("Overloaded")) {
307
+ error = new AIServiecOverloadedError("AI service overloaded", {
308
+ cause: err
309
+ });
310
+ } else {
311
+ error = new Error("AI service error", {
312
+ cause: err
313
+ });
314
+ }
315
+ } finally {
316
+ this._stream = void 0;
317
+ }
318
+ if (error) {
1146
319
  throw error;
1147
320
  }
321
+ return this._pending;
1148
322
  }
1149
- };
1150
- var sanitizeToolArguments = (args) => {
1151
- if (args.type === "object" && typeof args.properties === "object" && Array.isArray(args.required)) {
1152
- return args.properties;
1153
- }
1154
- return args;
1155
- };
1156
- var SAMPLE_IMAGE_URL = "https://images.nightcafe.studio/jobs/BNmcRhHCM1JRKoUtqSei/BNmcRhHCM1JRKoUtqSei--1--5b9rv.jpg";
1157
- var WELL_KNOWN_TOOLS = {
1158
- [ToolTypes.TextToImage]: defineTool("system", {
1159
- name: "text-to-image",
1160
- description: "Generate an image from a text prompt",
1161
- schema: S3.Struct({
1162
- prompt: S3.String.annotations({
1163
- description: "The text prompt describing the image to generate"
1164
- })
1165
- }),
1166
- execute: async ({ prompt }) => {
1167
- const image = await fetch(SAMPLE_IMAGE_URL).then(async (res) => Buffer.from(await res.arrayBuffer()));
1168
- const imageBase64 = image.toString("base64");
1169
- const id = ObjectId3.random();
1170
- return ToolResult2.Success(`The generation was successful. Image ID: ${id} Prompt: ${prompt}`, [
1171
- {
1172
- type: "image",
1173
- id,
1174
- source: {
1175
- type: "base64",
1176
- mediaType: "image/jpeg",
1177
- data: imageBase64
1178
- }
1179
- }
1180
- ]);
1181
- }
1182
- })
1183
- };
1184
- var OllamaDecoderStream = class extends TransformStream {
1185
- constructor() {
1186
- super({
1187
- transform: (chunk, controller) => {
1188
- this._buffer += chunk;
1189
- const lines = this._buffer.split("\n");
1190
- this._buffer = lines.pop() || "";
1191
- for (const line of lines) {
1192
- controller.enqueue(JSON.parse(line.trim()));
1193
- }
1194
- },
1195
- flush: (controller) => {
1196
- this._buffer = this._buffer.trim();
1197
- if (this._buffer.length > 0) {
1198
- controller.enqueue(JSON.parse(this._buffer));
1199
- }
1200
- }
1201
- });
1202
- this._buffer = "";
323
+ abort() {
324
+ this._stream?.abort();
1203
325
  }
1204
326
  };
327
+ _ts_decorate([
328
+ synchronized
329
+ ], AISession.prototype, "run", null);
330
+ var createBaseInstructions = ({ availableArtifacts, operationModel }) => `
331
+ You are a friendly, advanced AI assistant capable of creating and managing artifacts from available data and tools.
332
+ Your task is to process user commands and questions and decide how best to respond.
333
+ In some cases, you will need to create or reference data objects called artifacts.
1205
334
 
1206
- // packages/experimental/assistant/src/ai-service/parser.ts
1207
- import { Message as Message4 } from "@dxos/artifact";
1208
- import { Event } from "@dxos/async";
1209
- import { createStatic as createStatic2 } from "@dxos/echo-schema";
1210
- import { invariant as invariant4 } from "@dxos/invariant";
1211
- import { log as log5 } from "@dxos/log";
1212
- import { isNotFalsy, safeParseJson } from "@dxos/util";
335
+ Follow these guidelines carefully:
1213
336
 
1214
- // packages/experimental/assistant/src/ai-service/transform.ts
1215
- import * as P from "parsimmon";
1216
- var textChunk = P.regexp(/[^<]+/).map((content) => ({
1217
- type: "text",
1218
- content
1219
- }));
1220
- var attribute = P.seq(P.regexp(/[a-zA-Z_][a-zA-Z0-9_-]*/), P.string("=").trim(P.optWhitespace), P.alt(P.regexp(/"[^"]*"/).map((s) => s.slice(1, -1)), P.regexp(/'[^']*'/).map((s) => s.slice(1, -1)))).map(([name, _, value]) => [
1221
- name,
1222
- value
1223
- ]);
1224
- var tagName = P.regexp(/[a-zA-Z_][a-zA-Z0-9_-]*/);
1225
- var selfClosingTag = P.seqMap(
1226
- //
1227
- P.string("<"),
1228
- tagName,
1229
- P.optWhitespace.then(attribute).many(),
1230
- P.optWhitespace.then(P.string("/>")),
1231
- P.optWhitespace,
1232
- (_, tag, attributes, __) => ({
1233
- type: "tag",
1234
- tag,
1235
- attributes: Object.fromEntries(attributes),
1236
- content: [],
1237
- selfClosing: true
1238
- })
1239
- );
1240
- var openTag = P.seqMap(
1241
- //
1242
- P.string("<"),
1243
- tagName,
1244
- P.optWhitespace.then(attribute).many(),
1245
- P.string(">"),
1246
- P.regexp(/[^<]*/),
1247
- (_, tag, attributes, __, content) => ({
1248
- type: "tag",
1249
- tag,
1250
- attributes: Object.fromEntries(attributes),
1251
- content: content.length ? [
1252
- {
1253
- type: "text",
1254
- content
1255
- }
1256
- ] : []
1257
- })
1258
- );
1259
- var closeTag = P.seqMap(
1260
- //
1261
- P.string("</"),
1262
- tagName,
1263
- P.string(">"),
1264
- P.optWhitespace,
1265
- (_, tag) => ({
1266
- type: "tag",
1267
- tag,
1268
- content: [],
1269
- closing: true
1270
- })
1271
- );
1272
- var mixedChunk = P.alt(selfClosingTag, openTag, closeTag, textChunk);
1273
- var StreamTransform = class {
1274
- constructor() {
1275
- this._buffer = "";
1276
- }
1277
- transform(chunk) {
1278
- this._buffer += chunk;
1279
- const results = [];
1280
- const parser = mixedChunk.many().skip(P.optWhitespace);
1281
- const result = parser.parse(this._buffer);
1282
- if (result.status) {
1283
- for (const chunk2 of result.value) {
1284
- results.push(chunk2);
1285
- }
1286
- this._buffer = "";
1287
- }
1288
- return results;
1289
- }
1290
- // TODO(burdon): Process remaining buffer?
1291
- flush() {
1292
- const remaining = this._buffer;
1293
- this._buffer = "";
1294
- return remaining.length > 0 ? [
1295
- {
1296
- type: "text",
1297
- content: remaining
1298
- }
1299
- ] : [];
1300
- }
1301
- };
337
+ Decision-making:
1302
338
 
1303
- // packages/experimental/assistant/src/ai-service/parser.ts
1304
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/parser.ts";
1305
- var MixedStreamParser = class {
1306
- constructor() {
1307
- /**
1308
- * New message.
1309
- */
1310
- this.message = new Event();
1311
- /**
1312
- * Complete block added to Message.
1313
- */
1314
- this.block = new Event();
1315
- /**
1316
- * Update partial block (while streaming).
1317
- */
1318
- this.update = new Event();
1319
- this.streamEvent = new Event();
1320
- }
1321
- _emitBlock(contentBlock, streamBlock) {
1322
- const messageBlock = streamBlock ? mergeMessageBlock(contentBlock, streamBlock) : contentBlock;
1323
- if (messageBlock) {
1324
- if (messageBlock.type === "text" && messageBlock.text.length === 0) {
1325
- return;
1326
- }
1327
- invariant4(this._message, void 0, {
1328
- F: __dxlog_file5,
1329
- L: 49,
1330
- S: this,
1331
- A: [
1332
- "this._message",
1333
- ""
1334
- ]
1335
- });
1336
- this._message.content.push(messageBlock);
1337
- this.block.emit(messageBlock);
1338
- }
1339
- }
1340
- _emitUpdate(contentBlock, streamBlock) {
1341
- const messageBlock = mergeMessageBlock(contentBlock, streamBlock);
1342
- if (messageBlock) {
1343
- messageBlock.pending = true;
1344
- this.update.emit(messageBlock);
1345
- }
1346
- }
1347
- /**
1348
- * Parse stream until end.
1349
- */
1350
- async parse(stream) {
1351
- const transformer = new StreamTransform();
1352
- let contentBlock;
1353
- let streamBlock;
1354
- const stack = [];
1355
- const messagesCollected = [];
1356
- for await (const event of stream) {
1357
- this.streamEvent.emit(event);
1358
- switch (event.type) {
1359
- //
1360
- // Messages.
1361
- //
1362
- case "message_start": {
1363
- if (this._message) {
1364
- log5.warn("unexpected message_start", void 0, {
1365
- F: __dxlog_file5,
1366
- L: 93,
1367
- S: this,
1368
- C: (f, a) => f(...a)
1369
- });
1370
- }
1371
- this._message = createStatic2(Message4, {
1372
- role: event.message.role,
1373
- content: [
1374
- ...event.message.content
1375
- ]
1376
- });
1377
- this.message.emit(this._message);
1378
- break;
1379
- }
1380
- case "message_delta": {
1381
- if (!this._message) {
1382
- log5.warn("unexpected message_delta", void 0, {
1383
- F: __dxlog_file5,
1384
- L: 103,
1385
- S: this,
1386
- C: (f, a) => f(...a)
1387
- });
1388
- }
1389
- break;
1390
- }
1391
- case "message_stop": {
1392
- if (!this._message) {
1393
- log5.warn("unexpected message_stop", void 0, {
1394
- F: __dxlog_file5,
1395
- L: 111,
1396
- S: this,
1397
- C: (f, a) => f(...a)
1398
- });
1399
- } else {
1400
- messagesCollected.push(this._message);
1401
- this._message = void 0;
1402
- }
1403
- break;
1404
- }
1405
- //
1406
- // Content blocks.
1407
- //
1408
- case "content_block_start": {
1409
- if (contentBlock) {
1410
- log5.warn("unexpected content_block_start", {
1411
- content: contentBlock
1412
- }, {
1413
- F: __dxlog_file5,
1414
- L: 125,
1415
- S: this,
1416
- C: (f, a) => f(...a)
1417
- });
1418
- }
1419
- contentBlock = event.content;
1420
- break;
1421
- }
1422
- case "content_block_delta": {
1423
- if (!contentBlock) {
1424
- log5.warn("unexpected content_block_delta", void 0, {
1425
- F: __dxlog_file5,
1426
- L: 135,
1427
- S: this,
1428
- C: (f, a) => f(...a)
1429
- });
1430
- break;
1431
- }
1432
- switch (event.delta.type) {
1433
- //
1434
- // Text
1435
- //
1436
- case "text_delta": {
1437
- const chunks = transformer.transform(event.delta.text);
1438
- for (const chunk of chunks) {
1439
- switch (streamBlock?.type) {
1440
- //
1441
- // XML Fragment.
1442
- //
1443
- case "tag": {
1444
- if (chunk.type === "tag") {
1445
- if (chunk.selfClosing) {
1446
- streamBlock.content.push(chunk);
1447
- } else if (chunk.closing) {
1448
- if (stack.length > 0) {
1449
- const top = stack.pop();
1450
- invariant4(top && top.type === "tag", void 0, {
1451
- F: __dxlog_file5,
1452
- L: 159,
1453
- S: this,
1454
- A: [
1455
- "top && top.type === 'tag'",
1456
- ""
1457
- ]
1458
- });
1459
- top.content.push(streamBlock);
1460
- streamBlock = top;
1461
- } else {
1462
- this._emitBlock(contentBlock, streamBlock);
1463
- streamBlock = void 0;
1464
- }
1465
- } else {
1466
- stack.push(streamBlock);
1467
- streamBlock = chunk;
1468
- }
1469
- } else {
1470
- if (streamBlock.content.length === 0) {
1471
- streamBlock.content.push(chunk);
1472
- } else {
1473
- const last = streamBlock.content.at(-1);
1474
- invariant4(last, void 0, {
1475
- F: __dxlog_file5,
1476
- L: 176,
1477
- S: this,
1478
- A: [
1479
- "last",
1480
- ""
1481
- ]
1482
- });
1483
- if (last.type === "text") {
1484
- last.content += chunk.content;
1485
- } else {
1486
- streamBlock.content.push(chunk);
1487
- }
1488
- }
1489
- }
1490
- break;
1491
- }
1492
- //
1493
- // Text Fragment.
1494
- //
1495
- case "text": {
1496
- if (chunk.type === "tag") {
1497
- this._emitBlock(contentBlock, streamBlock);
1498
- if (chunk.selfClosing) {
1499
- this._emitBlock(contentBlock, chunk);
1500
- streamBlock = void 0;
1501
- } else {
1502
- streamBlock = chunk;
1503
- }
1504
- } else {
1505
- streamBlock.content += chunk.content;
1506
- }
1507
- break;
1508
- }
1509
- //
1510
- // No current chunk.
1511
- //
1512
- default: {
1513
- if (chunk.type === "tag" && chunk.selfClosing) {
1514
- this._emitBlock(contentBlock, chunk);
1515
- } else {
1516
- streamBlock = chunk;
1517
- }
1518
- }
1519
- }
1520
- }
1521
- break;
1522
- }
1523
- //
1524
- // JSON
1525
- //
1526
- case "input_json_delta": {
1527
- if (!contentBlock) {
1528
- log5.warn("unexpected input_json_delta", {
1529
- contentBlock
1530
- }, {
1531
- F: __dxlog_file5,
1532
- L: 226,
1533
- S: this,
1534
- C: (f, a) => f(...a)
1535
- });
1536
- break;
1537
- }
1538
- if (streamBlock?.type === "json") {
1539
- streamBlock.content += event.delta.partial_json;
1540
- } else {
1541
- streamBlock = {
1542
- type: "json",
1543
- content: event.delta.partial_json
1544
- };
1545
- }
1546
- break;
1547
- }
1548
- }
1549
- break;
1550
- }
1551
- case "content_block_stop": {
1552
- if (!contentBlock) {
1553
- log5.warn("unexpected content_block_stop", void 0, {
1554
- F: __dxlog_file5,
1555
- L: 243,
1556
- S: this,
1557
- C: (f, a) => f(...a)
1558
- });
1559
- break;
1560
- }
1561
- this._emitBlock(contentBlock, streamBlock);
1562
- contentBlock = void 0;
1563
- streamBlock = void 0;
1564
- break;
1565
- }
1566
- }
1567
- if (contentBlock && streamBlock) {
1568
- this._emitUpdate(contentBlock, streamBlock);
1569
- }
1570
- }
1571
- return messagesCollected;
1572
- }
1573
- };
1574
- var mergeMessageBlock = (contentBlock, streamBlock) => {
1575
- log5("mergeMessageBlock", {
1576
- contentBlock,
1577
- streamBlock
1578
- }, {
1579
- F: __dxlog_file5,
1580
- L: 270,
1581
- S: void 0,
1582
- C: (f, a) => f(...a)
1583
- });
1584
- switch (streamBlock.type) {
1585
- //
1586
- // Text
1587
- //
1588
- case "text": {
1589
- return {
1590
- ...contentBlock,
1591
- type: "text",
1592
- text: streamBlock.content
1593
- };
1594
- }
1595
- //
1596
- // XML
1597
- //
1598
- case "tag": {
1599
- switch (streamBlock.tag) {
1600
- case "cot": {
1601
- const content = streamBlock.content.map((block) => {
1602
- switch (block.type) {
1603
- case "text":
1604
- return block.content;
1605
- default:
1606
- return null;
1607
- }
1608
- }).filter(isNotFalsy).join("\n");
1609
- return {
1610
- ...contentBlock,
1611
- type: "text",
1612
- disposition: "cot",
1613
- text: content
1614
- };
1615
- }
1616
- case "artifact": {
1617
- const { attributes } = streamBlock;
1618
- return {
1619
- type: "json",
1620
- disposition: "artifact",
1621
- json: JSON.stringify(attributes)
1622
- };
1623
- }
1624
- case "suggest": {
1625
- if (streamBlock.content.length === 1 && streamBlock.content[0].type === "text") {
1626
- return {
1627
- type: "json",
1628
- disposition: "suggest",
1629
- json: JSON.stringify({
1630
- text: streamBlock.content[0].content
1631
- })
1632
- };
1633
- }
1634
- break;
1635
- }
1636
- case "select": {
1637
- return {
1638
- type: "json",
1639
- disposition: "select",
1640
- json: JSON.stringify({
1641
- options: streamBlock.content.flatMap((content) => content.type === "tag" && content.content.length === 1 && content.content[0].type === "text" ? [
1642
- content.content[0].content
1643
- ] : [])
1644
- })
1645
- };
1646
- }
1647
- case "tool-list": {
1648
- return {
1649
- type: "json",
1650
- disposition: "tool_list",
1651
- json: JSON.stringify({})
1652
- };
1653
- }
1654
- }
1655
- break;
1656
- }
1657
- //
1658
- // JSON
1659
- //
1660
- case "json": {
1661
- switch (contentBlock.type) {
1662
- case "tool_use": {
1663
- return {
1664
- ...contentBlock,
1665
- input: safeParseJson(streamBlock.content) ?? {}
1666
- };
1667
- }
1668
- }
1669
- return {
1670
- ...contentBlock,
1671
- type: "json",
1672
- disposition: "artifact",
1673
- json: streamBlock.content
1674
- };
1675
- }
1676
- }
339
+ - Analyze the structure and type of the content in the user's message.
340
+ - Can you complete the task using the available artifacts?
341
+ - If you can't complete the task using the available artifacts, query the list of available artifacts using the appropriate tool.
342
+ - Identify which artifacts are relevant to the user's request.
343
+ ${operationModel === "planning" ? `
344
+ - Break down the user's request into a step-by-step plan that you will use to execute the user's request, the plan items should include artifacts which will be used to perform the actions.
345
+ ` : `
346
+ - Are the required artifacts already available?
347
+ - If not, select which artifact(s) will be the most relevant and require them using the require_artifacts tool.
348
+ - The require'd artifact tools will be available for use after require.
349
+ `}
350
+
351
+ ${availableArtifacts.length > 0 ? `Artifacts already in context: ${availableArtifacts.join("\n")}` : ""}
352
+ `;
353
+ var AIServiecOverloadedError = class extends S.TaggedError()("AIServiecOverloadedError", {}) {
1677
354
  };
1678
355
  export {
1679
356
  AIServiceEdgeClient,
1680
- DEFAULT_LLM_MODEL,
1681
- DEFAULT_LLM_MODELS,
357
+ AIServiecOverloadedError,
358
+ AISession,
359
+ DEFAULT_EDGE_MODEL,
360
+ DEFAULT_EDGE_MODELS,
361
+ DEFAULT_OLLAMA_ENDPOINT,
362
+ DEFAULT_OLLAMA_MODEL,
363
+ DEFAULT_OLLAMA_MODELS,
1682
364
  EchoDataSource,
1683
365
  GenerateRequest,
1684
366
  GenerationStreamImpl,