@dxos/assistant 0.7.5-staging.e6154f3 → 0.8.1-main.303c73a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +976 -536
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +2 -2
- package/dist/lib/browser/testing/index.mjs.map +1 -1
- package/dist/lib/node/index.cjs +1172 -735
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +22 -22
- package/dist/lib/node/testing/index.cjs.map +2 -2
- package/dist/lib/node-esm/index.mjs +976 -536
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +2 -2
- package/dist/lib/node-esm/testing/index.mjs.map +1 -1
- package/dist/types/src/ai-service/edge-client.d.ts +19 -0
- package/dist/types/src/ai-service/edge-client.d.ts.map +1 -0
- package/dist/types/src/ai-service/index.d.ts +4 -1
- package/dist/types/src/ai-service/index.d.ts.map +1 -1
- package/dist/types/src/ai-service/message-collector.d.ts +88 -0
- package/dist/types/src/ai-service/message-collector.d.ts.map +1 -0
- package/dist/types/src/ai-service/ollama-client.d.ts +43 -0
- package/dist/types/src/ai-service/ollama-client.d.ts.map +1 -0
- package/dist/types/src/ai-service/parser.d.ts +3 -1
- package/dist/types/src/ai-service/parser.d.ts.map +1 -1
- package/dist/types/src/ai-service/service.d.ts +9 -0
- package/dist/types/src/ai-service/service.d.ts.map +1 -0
- package/dist/types/src/ai-service/stream.d.ts +2 -1
- package/dist/types/src/ai-service/stream.d.ts.map +1 -1
- package/dist/types/src/ai-service/testing/mock.d.ts +2 -2
- package/dist/types/src/ai-service/testing/mock.d.ts.map +1 -1
- package/dist/types/src/ai-service/types.d.ts +1 -1
- package/dist/types/src/ai-service/types.d.ts.map +1 -1
- package/dist/types/src/conversation/conversation.d.ts +3 -1
- package/dist/types/src/conversation/conversation.d.ts.map +1 -1
- package/dist/types/src/conversation/types.d.ts +1 -1
- package/dist/types/src/conversation/types.d.ts.map +1 -1
- package/dist/types/src/testing/query-promts.d.ts +1 -1
- package/dist/types/src/testing/query-promts.d.ts.map +1 -1
- package/dist/types/src/testing/test-data.d.ts +1 -1
- package/dist/types/src/testing/test-data.d.ts.map +1 -1
- package/dist/types/src/testing/test-schema.d.ts +1 -1
- package/dist/types/src/testing/test-schema.d.ts.map +1 -1
- package/package.json +17 -14
- package/src/ai-service/client.test.ts +94 -7
- package/src/ai-service/{client.ts → edge-client.ts} +15 -24
- package/src/ai-service/index.ts +4 -1
- package/src/ai-service/message-collector.ts +84 -0
- package/src/ai-service/ollama-client.ts +473 -0
- package/src/ai-service/parser.ts +7 -2
- package/src/ai-service/service.ts +15 -0
- package/src/ai-service/stream.ts +5 -3
- package/src/ai-service/testing/mock.ts +7 -2
- package/src/ai-service/types.ts +1 -1
- package/src/bin/chat.ts +2 -2
- package/src/conversation/conversation.test.ts +3 -3
- package/src/conversation/conversation.ts +3 -2
- package/src/conversation/types.ts +1 -1
- package/src/testing/artifacts.test.ts +2 -2
- package/src/testing/query-promts.ts +1 -1
- package/src/testing/query.test.ts +2 -2
- package/src/testing/test-data.ts +1 -1
- package/src/testing/test-schema.ts +1 -1
- package/dist/types/src/ai-service/client.d.ts +0 -25
- package/dist/types/src/ai-service/client.d.ts.map +0 -1
|
@@ -9,8 +9,25 @@ import {
|
|
|
9
9
|
parseCypherQuery
|
|
10
10
|
} from "./chunk-AXUUNA7O.mjs";
|
|
11
11
|
|
|
12
|
-
// packages/experimental/assistant/src/ai-service/
|
|
13
|
-
|
|
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
|
+
];
|
|
28
|
+
|
|
29
|
+
// packages/experimental/assistant/src/ai-service/edge-client.ts
|
|
30
|
+
import { Schema as S } from "effect";
|
|
14
31
|
import { Message } from "@dxos/artifact";
|
|
15
32
|
import { invariant } from "@dxos/invariant";
|
|
16
33
|
import { log as log2 } from "@dxos/log";
|
|
@@ -270,7 +287,7 @@ var createGenerationStream = (response, controller = new AbortController()) => {
|
|
|
270
287
|
raw: sse.raw
|
|
271
288
|
}, {
|
|
272
289
|
F: __dxlog_file,
|
|
273
|
-
L:
|
|
290
|
+
L: 22,
|
|
274
291
|
S: this,
|
|
275
292
|
C: (f, a) => f(...a)
|
|
276
293
|
});
|
|
@@ -286,7 +303,7 @@ var createGenerationStream = (response, controller = new AbortController()) => {
|
|
|
286
303
|
raw: sse.raw
|
|
287
304
|
}, {
|
|
288
305
|
F: __dxlog_file,
|
|
289
|
-
L:
|
|
306
|
+
L: 38,
|
|
290
307
|
S: this,
|
|
291
308
|
C: (f, a) => f(...a)
|
|
292
309
|
});
|
|
@@ -301,9 +318,9 @@ var createGenerationStream = (response, controller = new AbortController()) => {
|
|
|
301
318
|
}
|
|
302
319
|
}
|
|
303
320
|
};
|
|
304
|
-
return new
|
|
321
|
+
return new GenerationStreamImpl(controller, iterator);
|
|
305
322
|
};
|
|
306
|
-
var
|
|
323
|
+
var GenerationStreamImpl = class {
|
|
307
324
|
constructor(_controller, _getIterator) {
|
|
308
325
|
this._controller = _controller;
|
|
309
326
|
this._getIterator = _getIterator;
|
|
@@ -342,6 +359,7 @@ var GenerationStream = class {
|
|
|
342
359
|
this._done.wake();
|
|
343
360
|
} catch (err) {
|
|
344
361
|
this._done.throw(err);
|
|
362
|
+
throw err;
|
|
345
363
|
}
|
|
346
364
|
};
|
|
347
365
|
return generator.call(this);
|
|
@@ -349,13 +367,13 @@ var GenerationStream = class {
|
|
|
349
367
|
}
|
|
350
368
|
};
|
|
351
369
|
|
|
352
|
-
// packages/experimental/assistant/src/ai-service/client.ts
|
|
353
|
-
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/client.ts";
|
|
354
|
-
var
|
|
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 {
|
|
355
373
|
constructor({ endpoint }) {
|
|
356
374
|
invariant(endpoint, "endpoint is required", {
|
|
357
375
|
F: __dxlog_file2,
|
|
358
|
-
L:
|
|
376
|
+
L: 23,
|
|
359
377
|
S: this,
|
|
360
378
|
A: [
|
|
361
379
|
"endpoint",
|
|
@@ -364,6 +382,10 @@ var AIServiceClientImpl = class {
|
|
|
364
382
|
});
|
|
365
383
|
this._endpoint = endpoint;
|
|
366
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* @deprecated
|
|
387
|
+
*/
|
|
388
|
+
// TODO(burdon): Remove.
|
|
367
389
|
async appendMessages(messages) {
|
|
368
390
|
const url = `${this._endpoint}/message`;
|
|
369
391
|
const res = await fetch(url, {
|
|
@@ -383,15 +405,11 @@ var AIServiceClientImpl = class {
|
|
|
383
405
|
* Process request and open message stream.
|
|
384
406
|
*/
|
|
385
407
|
async exec(request) {
|
|
386
|
-
request = {
|
|
387
|
-
tools: request.tools ?? [],
|
|
388
|
-
...request
|
|
389
|
-
};
|
|
390
408
|
log2.info("requesting", {
|
|
391
409
|
endpoint: this._endpoint
|
|
392
410
|
}, {
|
|
393
411
|
F: __dxlog_file2,
|
|
394
|
-
L:
|
|
412
|
+
L: 50,
|
|
395
413
|
S: this,
|
|
396
414
|
C: (f, a) => f(...a)
|
|
397
415
|
});
|
|
@@ -402,11 +420,15 @@ var AIServiceClientImpl = class {
|
|
|
402
420
|
headers: {
|
|
403
421
|
"Content-Type": "application/json"
|
|
404
422
|
},
|
|
405
|
-
body: JSON.stringify(
|
|
423
|
+
body: JSON.stringify({
|
|
424
|
+
// TODO(dmaretskyi): Errors if tools are not provided.
|
|
425
|
+
tools: request.tools ?? [],
|
|
426
|
+
...request
|
|
427
|
+
})
|
|
406
428
|
});
|
|
407
429
|
invariant(response.body instanceof ReadableStream, void 0, {
|
|
408
430
|
F: __dxlog_file2,
|
|
409
|
-
L:
|
|
431
|
+
L: 63,
|
|
410
432
|
S: this,
|
|
411
433
|
A: [
|
|
412
434
|
"response.body instanceof ReadableStream",
|
|
@@ -417,495 +439,81 @@ var AIServiceClientImpl = class {
|
|
|
417
439
|
}
|
|
418
440
|
};
|
|
419
441
|
|
|
420
|
-
// packages/experimental/assistant/src/ai-service/
|
|
421
|
-
var
|
|
422
|
-
var DEFAULT_LLM_MODELS = [
|
|
423
|
-
// AI Gateway.
|
|
424
|
-
// https://developers.cloudflare.com/ai-gateway
|
|
425
|
-
"@anthropic/claude-3-5-haiku-20241022",
|
|
426
|
-
"@anthropic/claude-3-5-sonnet-20241022",
|
|
427
|
-
"@anthropic/claude-3-7-sonnet-20250219",
|
|
428
|
-
// Workers AI.
|
|
429
|
-
// https://developers.cloudflare.com/workers-ai/models
|
|
430
|
-
"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
|
|
431
|
-
"@hf/nousresearch/hermes-2-pro-mistral-7b",
|
|
432
|
-
"@ollama/llama-3-1-nemotron-70b-instruct",
|
|
433
|
-
"@ollama/llama-3-1-nemotron-mini-4b-instruct",
|
|
434
|
-
"@ollama/llama-3-2-3b"
|
|
435
|
-
];
|
|
436
|
-
|
|
437
|
-
// packages/experimental/assistant/src/ai-service/parser.ts
|
|
438
|
-
import { Message as Message2 } from "@dxos/artifact";
|
|
439
|
-
import { Event } from "@dxos/async";
|
|
440
|
-
import { createStatic } from "@dxos/echo-schema";
|
|
441
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
442
|
-
import { log as log3 } from "@dxos/log";
|
|
443
|
-
import { isNotFalsy, safeParseJson } from "@dxos/util";
|
|
444
|
-
|
|
445
|
-
// packages/experimental/assistant/src/ai-service/transform.ts
|
|
446
|
-
import * as P from "parsimmon";
|
|
447
|
-
var textChunk = P.regexp(/[^<]+/).map((content) => ({
|
|
448
|
-
type: "text",
|
|
449
|
-
content
|
|
450
|
-
}));
|
|
451
|
-
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]) => [
|
|
452
|
-
name,
|
|
453
|
-
value
|
|
454
|
-
]);
|
|
455
|
-
var tagName = P.regexp(/[a-zA-Z_][a-zA-Z0-9_-]*/);
|
|
456
|
-
var selfClosingTag = P.seqMap(
|
|
457
|
-
//
|
|
458
|
-
P.string("<"),
|
|
459
|
-
tagName,
|
|
460
|
-
P.optWhitespace.then(attribute).many(),
|
|
461
|
-
P.optWhitespace.then(P.string("/>")),
|
|
462
|
-
P.optWhitespace,
|
|
463
|
-
(_, tag, attributes, __) => ({
|
|
464
|
-
type: "tag",
|
|
465
|
-
tag,
|
|
466
|
-
attributes: Object.fromEntries(attributes),
|
|
467
|
-
content: [],
|
|
468
|
-
selfClosing: true
|
|
469
|
-
})
|
|
470
|
-
);
|
|
471
|
-
var openTag = P.seqMap(
|
|
472
|
-
//
|
|
473
|
-
P.string("<"),
|
|
474
|
-
tagName,
|
|
475
|
-
P.optWhitespace.then(attribute).many(),
|
|
476
|
-
P.string(">"),
|
|
477
|
-
P.regexp(/[^<]*/),
|
|
478
|
-
(_, tag, attributes, __, content) => ({
|
|
479
|
-
type: "tag",
|
|
480
|
-
tag,
|
|
481
|
-
attributes: Object.fromEntries(attributes),
|
|
482
|
-
content: content.length ? [
|
|
483
|
-
{
|
|
484
|
-
type: "text",
|
|
485
|
-
content
|
|
486
|
-
}
|
|
487
|
-
] : []
|
|
488
|
-
})
|
|
489
|
-
);
|
|
490
|
-
var closeTag = P.seqMap(
|
|
491
|
-
//
|
|
492
|
-
P.string("</"),
|
|
493
|
-
tagName,
|
|
494
|
-
P.string(">"),
|
|
495
|
-
P.optWhitespace,
|
|
496
|
-
(_, tag) => ({
|
|
497
|
-
type: "tag",
|
|
498
|
-
tag,
|
|
499
|
-
content: [],
|
|
500
|
-
closing: true
|
|
501
|
-
})
|
|
502
|
-
);
|
|
503
|
-
var mixedChunk = P.alt(selfClosingTag, openTag, closeTag, textChunk);
|
|
504
|
-
var StreamTransform = class {
|
|
505
|
-
constructor() {
|
|
506
|
-
this._buffer = "";
|
|
507
|
-
}
|
|
508
|
-
transform(chunk) {
|
|
509
|
-
this._buffer += chunk;
|
|
510
|
-
const results = [];
|
|
511
|
-
const parser = mixedChunk.many().skip(P.optWhitespace);
|
|
512
|
-
const result = parser.parse(this._buffer);
|
|
513
|
-
if (result.status) {
|
|
514
|
-
for (const chunk2 of result.value) {
|
|
515
|
-
results.push(chunk2);
|
|
516
|
-
}
|
|
517
|
-
this._buffer = "";
|
|
518
|
-
}
|
|
519
|
-
return results;
|
|
520
|
-
}
|
|
521
|
-
// TODO(burdon): Process remaining buffer?
|
|
522
|
-
flush() {
|
|
523
|
-
const remaining = this._buffer;
|
|
524
|
-
this._buffer = "";
|
|
525
|
-
return remaining.length > 0 ? [
|
|
526
|
-
{
|
|
527
|
-
type: "text",
|
|
528
|
-
content: remaining
|
|
529
|
-
}
|
|
530
|
-
] : [];
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
// packages/experimental/assistant/src/ai-service/parser.ts
|
|
535
|
-
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/parser.ts";
|
|
536
|
-
var MixedStreamParser = class {
|
|
442
|
+
// packages/experimental/assistant/src/ai-service/message-collector.ts
|
|
443
|
+
var MessageCollector = class {
|
|
537
444
|
constructor() {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
*/
|
|
541
|
-
this.message = new Event();
|
|
542
|
-
/**
|
|
543
|
-
* Complete block added to Message.
|
|
544
|
-
*/
|
|
545
|
-
this.block = new Event();
|
|
546
|
-
/**
|
|
547
|
-
* Update partial block (while streaming).
|
|
548
|
-
*/
|
|
549
|
-
this.update = new Event();
|
|
445
|
+
this._messages = [];
|
|
446
|
+
this._pendingMessage = void 0;
|
|
550
447
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
if (messageBlock) {
|
|
554
|
-
if (messageBlock.type === "text" && messageBlock.text.length === 0) {
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
invariant2(this._message, void 0, {
|
|
558
|
-
F: __dxlog_file3,
|
|
559
|
-
L: 46,
|
|
560
|
-
S: this,
|
|
561
|
-
A: [
|
|
562
|
-
"this._message",
|
|
563
|
-
""
|
|
564
|
-
]
|
|
565
|
-
});
|
|
566
|
-
this._message.content.push(messageBlock);
|
|
567
|
-
this.block.emit(messageBlock);
|
|
568
|
-
}
|
|
448
|
+
get messages() {
|
|
449
|
+
return this._messages;
|
|
569
450
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
if (messageBlock) {
|
|
573
|
-
messageBlock.pending = true;
|
|
574
|
-
this.update.emit(messageBlock);
|
|
575
|
-
}
|
|
451
|
+
get pendingMessage() {
|
|
452
|
+
return this._pendingMessage;
|
|
576
453
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
for await (const event of stream) {
|
|
587
|
-
switch (event.type) {
|
|
588
|
-
//
|
|
589
|
-
// Messages.
|
|
590
|
-
//
|
|
591
|
-
case "message_start": {
|
|
592
|
-
if (this._message) {
|
|
593
|
-
log3.warn("unexpected message_start", void 0, {
|
|
594
|
-
F: __dxlog_file3,
|
|
595
|
-
L: 88,
|
|
596
|
-
S: this,
|
|
597
|
-
C: (f, a) => f(...a)
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
this._message = createStatic(Message2, {
|
|
601
|
-
role: "assistant",
|
|
602
|
-
content: []
|
|
603
|
-
});
|
|
604
|
-
this.message.emit(this._message);
|
|
605
|
-
break;
|
|
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;
|
|
606
463
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
S: this,
|
|
613
|
-
C: (f, a) => f(...a)
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
break;
|
|
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;
|
|
617
469
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
C: (f, a) => f(...a)
|
|
625
|
-
});
|
|
626
|
-
} else {
|
|
627
|
-
messagesCollected.push(this._message);
|
|
628
|
-
this._message = void 0;
|
|
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;
|
|
629
476
|
}
|
|
630
|
-
break;
|
|
631
477
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
if (
|
|
637
|
-
|
|
638
|
-
content: contentBlock
|
|
639
|
-
}, {
|
|
640
|
-
F: __dxlog_file3,
|
|
641
|
-
L: 120,
|
|
642
|
-
S: this,
|
|
643
|
-
C: (f, a) => f(...a)
|
|
644
|
-
});
|
|
478
|
+
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;
|
|
645
484
|
}
|
|
646
|
-
contentBlock = event.content;
|
|
647
|
-
break;
|
|
648
485
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
L: 130,
|
|
654
|
-
S: this,
|
|
655
|
-
C: (f, a) => f(...a)
|
|
656
|
-
});
|
|
657
|
-
break;
|
|
658
|
-
}
|
|
659
|
-
switch (event.delta.type) {
|
|
660
|
-
//
|
|
661
|
-
// Text
|
|
662
|
-
//
|
|
663
|
-
case "text_delta": {
|
|
664
|
-
const chunks = transformer.transform(event.delta.text);
|
|
665
|
-
for (const chunk of chunks) {
|
|
666
|
-
switch (streamBlock?.type) {
|
|
667
|
-
//
|
|
668
|
-
// XML Fragment.
|
|
669
|
-
//
|
|
670
|
-
case "tag": {
|
|
671
|
-
if (chunk.type === "tag") {
|
|
672
|
-
if (chunk.selfClosing) {
|
|
673
|
-
streamBlock.content.push(chunk);
|
|
674
|
-
} else if (chunk.closing) {
|
|
675
|
-
if (stack.length > 0) {
|
|
676
|
-
const top = stack.pop();
|
|
677
|
-
invariant2(top && top.type === "tag", void 0, {
|
|
678
|
-
F: __dxlog_file3,
|
|
679
|
-
L: 154,
|
|
680
|
-
S: this,
|
|
681
|
-
A: [
|
|
682
|
-
"top && top.type === 'tag'",
|
|
683
|
-
""
|
|
684
|
-
]
|
|
685
|
-
});
|
|
686
|
-
top.content.push(streamBlock);
|
|
687
|
-
streamBlock = top;
|
|
688
|
-
} else {
|
|
689
|
-
this._emitBlock(contentBlock, streamBlock);
|
|
690
|
-
streamBlock = void 0;
|
|
691
|
-
}
|
|
692
|
-
} else {
|
|
693
|
-
stack.push(streamBlock);
|
|
694
|
-
streamBlock = chunk;
|
|
695
|
-
}
|
|
696
|
-
} else {
|
|
697
|
-
if (streamBlock.content.length === 0) {
|
|
698
|
-
streamBlock.content.push(chunk);
|
|
699
|
-
} else {
|
|
700
|
-
const last = streamBlock.content.at(-1);
|
|
701
|
-
invariant2(last, void 0, {
|
|
702
|
-
F: __dxlog_file3,
|
|
703
|
-
L: 171,
|
|
704
|
-
S: this,
|
|
705
|
-
A: [
|
|
706
|
-
"last",
|
|
707
|
-
""
|
|
708
|
-
]
|
|
709
|
-
});
|
|
710
|
-
if (last.type === "text") {
|
|
711
|
-
last.content += chunk.content;
|
|
712
|
-
} else {
|
|
713
|
-
streamBlock.content.push(chunk);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
break;
|
|
718
|
-
}
|
|
719
|
-
//
|
|
720
|
-
// Text Fragment.
|
|
721
|
-
//
|
|
722
|
-
case "text": {
|
|
723
|
-
if (chunk.type === "tag") {
|
|
724
|
-
this._emitBlock(contentBlock, streamBlock);
|
|
725
|
-
if (chunk.selfClosing) {
|
|
726
|
-
this._emitBlock(contentBlock, chunk);
|
|
727
|
-
streamBlock = void 0;
|
|
728
|
-
} else {
|
|
729
|
-
streamBlock = chunk;
|
|
730
|
-
}
|
|
731
|
-
} else {
|
|
732
|
-
streamBlock.content += chunk.content;
|
|
733
|
-
}
|
|
734
|
-
break;
|
|
735
|
-
}
|
|
736
|
-
//
|
|
737
|
-
// No current chunk.
|
|
738
|
-
//
|
|
739
|
-
default: {
|
|
740
|
-
if (chunk.type === "tag" && chunk.selfClosing) {
|
|
741
|
-
this._emitBlock(contentBlock, chunk);
|
|
742
|
-
} else {
|
|
743
|
-
streamBlock = chunk;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
break;
|
|
749
|
-
}
|
|
750
|
-
//
|
|
751
|
-
// JSON
|
|
752
|
-
//
|
|
753
|
-
case "input_json_delta": {
|
|
754
|
-
if (!contentBlock) {
|
|
755
|
-
log3.warn("unexpected input_json_delta", {
|
|
756
|
-
contentBlock
|
|
757
|
-
}, {
|
|
758
|
-
F: __dxlog_file3,
|
|
759
|
-
L: 221,
|
|
760
|
-
S: this,
|
|
761
|
-
C: (f, a) => f(...a)
|
|
762
|
-
});
|
|
763
|
-
break;
|
|
764
|
-
}
|
|
765
|
-
if (streamBlock?.type === "json") {
|
|
766
|
-
streamBlock.content += event.delta.partial_json;
|
|
767
|
-
} else {
|
|
768
|
-
streamBlock = {
|
|
769
|
-
type: "json",
|
|
770
|
-
content: event.delta.partial_json
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
break;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
break;
|
|
777
|
-
}
|
|
778
|
-
case "content_block_stop": {
|
|
779
|
-
if (!contentBlock) {
|
|
780
|
-
log3.warn("unexpected content_block_stop", void 0, {
|
|
781
|
-
F: __dxlog_file3,
|
|
782
|
-
L: 238,
|
|
783
|
-
S: this,
|
|
784
|
-
C: (f, a) => f(...a)
|
|
785
|
-
});
|
|
786
|
-
break;
|
|
787
|
-
}
|
|
788
|
-
this._emitBlock(contentBlock, streamBlock);
|
|
789
|
-
contentBlock = void 0;
|
|
790
|
-
streamBlock = void 0;
|
|
791
|
-
break;
|
|
486
|
+
break;
|
|
487
|
+
case "message_delta":
|
|
488
|
+
if (this._pendingMessage && event.delta) {
|
|
489
|
+
Object.assign(this._pendingMessage, event.delta);
|
|
792
490
|
}
|
|
793
|
-
|
|
794
|
-
if (contentBlock && streamBlock) {
|
|
795
|
-
this._emitUpdate(contentBlock, streamBlock);
|
|
796
|
-
}
|
|
491
|
+
break;
|
|
797
492
|
}
|
|
798
|
-
return messagesCollected;
|
|
799
493
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
log3("mergeMessageBlock", {
|
|
803
|
-
contentBlock,
|
|
804
|
-
streamBlock
|
|
805
|
-
}, {
|
|
806
|
-
F: __dxlog_file3,
|
|
807
|
-
L: 265,
|
|
808
|
-
S: void 0,
|
|
809
|
-
C: (f, a) => f(...a)
|
|
810
|
-
});
|
|
811
|
-
switch (streamBlock.type) {
|
|
812
|
-
//
|
|
813
|
-
// Text
|
|
814
|
-
//
|
|
815
|
-
case "text": {
|
|
816
|
-
return {
|
|
817
|
-
...contentBlock,
|
|
818
|
-
type: "text",
|
|
819
|
-
text: streamBlock.content
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
//
|
|
823
|
-
// XML
|
|
824
|
-
//
|
|
825
|
-
case "tag": {
|
|
826
|
-
switch (streamBlock.tag) {
|
|
827
|
-
case "cot": {
|
|
828
|
-
const content = streamBlock.content.map((block) => {
|
|
829
|
-
switch (block.type) {
|
|
830
|
-
case "text":
|
|
831
|
-
return block.content;
|
|
832
|
-
default:
|
|
833
|
-
return null;
|
|
834
|
-
}
|
|
835
|
-
}).filter(isNotFalsy).join("\n");
|
|
836
|
-
return {
|
|
837
|
-
...contentBlock,
|
|
838
|
-
type: "text",
|
|
839
|
-
disposition: "cot",
|
|
840
|
-
text: content
|
|
841
|
-
};
|
|
842
|
-
}
|
|
843
|
-
case "artifact": {
|
|
844
|
-
const { attributes } = streamBlock;
|
|
845
|
-
return {
|
|
846
|
-
type: "json",
|
|
847
|
-
disposition: "artifact",
|
|
848
|
-
json: JSON.stringify(attributes)
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
case "suggest": {
|
|
852
|
-
if (streamBlock.content.length === 1 && streamBlock.content[0].type === "text") {
|
|
853
|
-
return {
|
|
854
|
-
type: "json",
|
|
855
|
-
disposition: "suggest",
|
|
856
|
-
json: JSON.stringify({
|
|
857
|
-
text: streamBlock.content[0].content
|
|
858
|
-
})
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
break;
|
|
862
|
-
}
|
|
863
|
-
case "select": {
|
|
864
|
-
return {
|
|
865
|
-
type: "json",
|
|
866
|
-
disposition: "select",
|
|
867
|
-
json: JSON.stringify({
|
|
868
|
-
options: streamBlock.content.flatMap((content) => content.type === "tag" && content.content.length === 1 && content.content[0].type === "text" ? [
|
|
869
|
-
content.content[0].content
|
|
870
|
-
] : [])
|
|
871
|
-
})
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
case "tool-list": {
|
|
875
|
-
return {
|
|
876
|
-
type: "json",
|
|
877
|
-
disposition: "tool_list",
|
|
878
|
-
json: JSON.stringify({})
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
break;
|
|
883
|
-
}
|
|
884
|
-
//
|
|
885
|
-
// JSON
|
|
886
|
-
//
|
|
887
|
-
case "json": {
|
|
888
|
-
switch (contentBlock.type) {
|
|
889
|
-
case "tool_use": {
|
|
890
|
-
return {
|
|
891
|
-
...contentBlock,
|
|
892
|
-
input: safeParseJson(streamBlock.content) ?? {}
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
return {
|
|
897
|
-
...contentBlock,
|
|
898
|
-
type: "json",
|
|
899
|
-
disposition: "artifact",
|
|
900
|
-
json: streamBlock.content
|
|
901
|
-
};
|
|
902
|
-
}
|
|
494
|
+
pushMessage(message) {
|
|
495
|
+
this._messages.push(message);
|
|
903
496
|
}
|
|
904
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";
|
|
905
513
|
|
|
906
514
|
// packages/experimental/assistant/src/ai-service/types.ts
|
|
907
|
-
import { Schema as S2 } from "
|
|
908
|
-
import { Tool, Message as
|
|
515
|
+
import { Schema as S2 } from "effect";
|
|
516
|
+
import { Tool, Message as Message2, SpaceIdSchema } from "@dxos/artifact";
|
|
909
517
|
import { ObjectId } from "@dxos/echo-schema";
|
|
910
518
|
var createArtifactElement = (id) => `<artifact id=${id} />`;
|
|
911
519
|
var LLMModel = S2.Literal(...DEFAULT_LLM_MODELS);
|
|
@@ -933,27 +541,27 @@ var GenerateRequest = S2.Struct({
|
|
|
933
541
|
* History of messages to include in the context window.
|
|
934
542
|
*/
|
|
935
543
|
// TODO(burdon): Rename messages.
|
|
936
|
-
history: S2.optional(S2.Array(
|
|
544
|
+
history: S2.optional(S2.Array(Message2)),
|
|
937
545
|
/**
|
|
938
546
|
* Current request.
|
|
939
547
|
*/
|
|
940
|
-
prompt: S2.optional(
|
|
548
|
+
prompt: S2.optional(Message2)
|
|
941
549
|
});
|
|
942
550
|
|
|
943
551
|
// packages/experimental/assistant/src/conversation/conversation.ts
|
|
944
|
-
import { ToolResult, Message as
|
|
945
|
-
import { createStatic
|
|
946
|
-
import { invariant as
|
|
947
|
-
import { log as
|
|
948
|
-
var
|
|
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";
|
|
949
557
|
var runLLM = async (params) => {
|
|
950
558
|
let conversationResult = null;
|
|
951
559
|
const history = params.history ?? [];
|
|
952
560
|
const generate = async () => {
|
|
953
|
-
|
|
561
|
+
log3("llm generate", {
|
|
954
562
|
tools: params.tools
|
|
955
563
|
}, {
|
|
956
|
-
F:
|
|
564
|
+
F: __dxlog_file3,
|
|
957
565
|
L: 44,
|
|
958
566
|
S: void 0,
|
|
959
567
|
C: (f, a) => f(...a)
|
|
@@ -973,8 +581,8 @@ var runLLM = async (params) => {
|
|
|
973
581
|
await stream.complete();
|
|
974
582
|
const messages = [];
|
|
975
583
|
const message = messages.at(-1);
|
|
976
|
-
|
|
977
|
-
F:
|
|
584
|
+
invariant2(message, void 0, {
|
|
585
|
+
F: __dxlog_file3,
|
|
978
586
|
L: 63,
|
|
979
587
|
S: void 0,
|
|
980
588
|
A: [
|
|
@@ -982,17 +590,17 @@ var runLLM = async (params) => {
|
|
|
982
590
|
""
|
|
983
591
|
]
|
|
984
592
|
});
|
|
985
|
-
|
|
593
|
+
log3("llm result", {
|
|
986
594
|
time: Date.now() - beginTs,
|
|
987
595
|
message
|
|
988
596
|
}, {
|
|
989
|
-
F:
|
|
597
|
+
F: __dxlog_file3,
|
|
990
598
|
L: 65,
|
|
991
599
|
S: void 0,
|
|
992
600
|
C: (f, a) => f(...a)
|
|
993
601
|
});
|
|
994
|
-
|
|
995
|
-
F:
|
|
602
|
+
invariant2(message, void 0, {
|
|
603
|
+
F: __dxlog_file3,
|
|
996
604
|
L: 66,
|
|
997
605
|
S: void 0,
|
|
998
606
|
A: [
|
|
@@ -1011,8 +619,8 @@ var runLLM = async (params) => {
|
|
|
1011
619
|
const isToolUse2 = message.content.at(-1)?.type === "tool_use";
|
|
1012
620
|
if (isToolUse2) {
|
|
1013
621
|
const toolCalls = message.content.filter((block) => block.type === "tool_use");
|
|
1014
|
-
|
|
1015
|
-
F:
|
|
622
|
+
invariant2(toolCalls.length === 1, void 0, {
|
|
623
|
+
F: __dxlog_file3,
|
|
1016
624
|
L: 77,
|
|
1017
625
|
S: void 0,
|
|
1018
626
|
A: [
|
|
@@ -1025,8 +633,8 @@ var runLLM = async (params) => {
|
|
|
1025
633
|
if (!tool) {
|
|
1026
634
|
throw new Error(`Tool not found: ${toolCall.name}`);
|
|
1027
635
|
}
|
|
1028
|
-
|
|
1029
|
-
F:
|
|
636
|
+
invariant2(tool.execute, void 0, {
|
|
637
|
+
F: __dxlog_file3,
|
|
1030
638
|
L: 84,
|
|
1031
639
|
S: void 0,
|
|
1032
640
|
A: [
|
|
@@ -1037,10 +645,10 @@ var runLLM = async (params) => {
|
|
|
1037
645
|
const toolResult = await tool.execute(toolCall.input);
|
|
1038
646
|
switch (toolResult.kind) {
|
|
1039
647
|
case "error": {
|
|
1040
|
-
|
|
648
|
+
log3.warn("tool error", {
|
|
1041
649
|
message: toolResult.message
|
|
1042
650
|
}, {
|
|
1043
|
-
F:
|
|
651
|
+
F: __dxlog_file3,
|
|
1044
652
|
L: 88,
|
|
1045
653
|
S: void 0,
|
|
1046
654
|
C: (f, a) => f(...a)
|
|
@@ -1067,10 +675,10 @@ var runLLM = async (params) => {
|
|
|
1067
675
|
return true;
|
|
1068
676
|
}
|
|
1069
677
|
case "success": {
|
|
1070
|
-
|
|
678
|
+
log3("tool success", {
|
|
1071
679
|
result: toolResult.result
|
|
1072
680
|
}, {
|
|
1073
|
-
F:
|
|
681
|
+
F: __dxlog_file3,
|
|
1074
682
|
L: 110,
|
|
1075
683
|
S: void 0,
|
|
1076
684
|
C: (f, a) => f(...a)
|
|
@@ -1096,10 +704,10 @@ var runLLM = async (params) => {
|
|
|
1096
704
|
return true;
|
|
1097
705
|
}
|
|
1098
706
|
case "break": {
|
|
1099
|
-
|
|
707
|
+
log3("tool break", {
|
|
1100
708
|
result: toolResult.result
|
|
1101
709
|
}, {
|
|
1102
|
-
F:
|
|
710
|
+
F: __dxlog_file3,
|
|
1103
711
|
L: 131,
|
|
1104
712
|
S: void 0,
|
|
1105
713
|
C: (f, a) => f(...a)
|
|
@@ -1118,10 +726,10 @@ var runLLM = async (params) => {
|
|
|
1118
726
|
history
|
|
1119
727
|
};
|
|
1120
728
|
};
|
|
1121
|
-
var isToolUse = (message) => {
|
|
729
|
+
var isToolUse = (message, { onlyToolNames } = {}) => {
|
|
1122
730
|
const block = message.content.at(-1);
|
|
1123
|
-
|
|
1124
|
-
F:
|
|
731
|
+
invariant2(block, void 0, {
|
|
732
|
+
F: __dxlog_file3,
|
|
1125
733
|
L: 152,
|
|
1126
734
|
S: void 0,
|
|
1127
735
|
A: [
|
|
@@ -1129,12 +737,12 @@ var isToolUse = (message) => {
|
|
|
1129
737
|
""
|
|
1130
738
|
]
|
|
1131
739
|
});
|
|
1132
|
-
return block.type === "tool_use";
|
|
740
|
+
return block.type === "tool_use" && (!onlyToolNames || onlyToolNames.includes(block.name));
|
|
1133
741
|
};
|
|
1134
742
|
var runTools = async ({ message, tools, extensions }) => {
|
|
1135
743
|
const toolCalls = message.content.filter((block) => block.type === "tool_use");
|
|
1136
|
-
|
|
1137
|
-
F:
|
|
744
|
+
invariant2(toolCalls.length === 1, void 0, {
|
|
745
|
+
F: __dxlog_file3,
|
|
1138
746
|
L: 174,
|
|
1139
747
|
S: void 0,
|
|
1140
748
|
A: [
|
|
@@ -1149,8 +757,8 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1149
757
|
}
|
|
1150
758
|
let toolResult;
|
|
1151
759
|
try {
|
|
1152
|
-
|
|
1153
|
-
F:
|
|
760
|
+
invariant2(tool.execute, void 0, {
|
|
761
|
+
F: __dxlog_file3,
|
|
1154
762
|
L: 183,
|
|
1155
763
|
S: void 0,
|
|
1156
764
|
A: [
|
|
@@ -1162,10 +770,10 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1162
770
|
extensions
|
|
1163
771
|
});
|
|
1164
772
|
} catch (error) {
|
|
1165
|
-
|
|
773
|
+
log3("tool error", {
|
|
1166
774
|
error
|
|
1167
775
|
}, {
|
|
1168
|
-
F:
|
|
776
|
+
F: __dxlog_file3,
|
|
1169
777
|
L: 186,
|
|
1170
778
|
S: void 0,
|
|
1171
779
|
C: (f, a) => f(...a)
|
|
@@ -1174,15 +782,15 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1174
782
|
}
|
|
1175
783
|
switch (toolResult.kind) {
|
|
1176
784
|
case "error": {
|
|
1177
|
-
|
|
785
|
+
log3("tool error", {
|
|
1178
786
|
message: toolResult.message
|
|
1179
787
|
}, {
|
|
1180
|
-
F:
|
|
788
|
+
F: __dxlog_file3,
|
|
1181
789
|
L: 192,
|
|
1182
790
|
S: void 0,
|
|
1183
791
|
C: (f, a) => f(...a)
|
|
1184
792
|
});
|
|
1185
|
-
const resultMessage =
|
|
793
|
+
const resultMessage = createStatic(Message3, {
|
|
1186
794
|
role: "user",
|
|
1187
795
|
content: [
|
|
1188
796
|
{
|
|
@@ -1199,22 +807,23 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1199
807
|
};
|
|
1200
808
|
}
|
|
1201
809
|
case "success": {
|
|
1202
|
-
|
|
810
|
+
log3("tool success", {
|
|
1203
811
|
result: toolResult.result
|
|
1204
812
|
}, {
|
|
1205
|
-
F:
|
|
813
|
+
F: __dxlog_file3,
|
|
1206
814
|
L: 213,
|
|
1207
815
|
S: void 0,
|
|
1208
816
|
C: (f, a) => f(...a)
|
|
1209
817
|
});
|
|
1210
|
-
const resultMessage =
|
|
818
|
+
const resultMessage = createStatic(Message3, {
|
|
1211
819
|
role: "user",
|
|
1212
820
|
content: [
|
|
1213
821
|
{
|
|
1214
822
|
type: "tool_result",
|
|
1215
823
|
toolUseId: toolCall.id,
|
|
1216
824
|
content: typeof toolResult.result === "string" ? toolResult.result : JSON.stringify(toolResult.result) ?? ""
|
|
1217
|
-
}
|
|
825
|
+
},
|
|
826
|
+
...toolResult.extractContentBlocks ?? []
|
|
1218
827
|
]
|
|
1219
828
|
});
|
|
1220
829
|
return {
|
|
@@ -1223,11 +832,11 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1223
832
|
};
|
|
1224
833
|
}
|
|
1225
834
|
case "break": {
|
|
1226
|
-
|
|
835
|
+
log3("tool break", {
|
|
1227
836
|
result: toolResult.result
|
|
1228
837
|
}, {
|
|
1229
|
-
F:
|
|
1230
|
-
L:
|
|
838
|
+
F: __dxlog_file3,
|
|
839
|
+
L: 234,
|
|
1231
840
|
S: void 0,
|
|
1232
841
|
C: (f, a) => f(...a)
|
|
1233
842
|
});
|
|
@@ -1238,19 +847,850 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1238
847
|
}
|
|
1239
848
|
}
|
|
1240
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])
|
|
897
|
+
];
|
|
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();
|
|
1014
|
+
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
|
|
1041
|
+
}, {
|
|
1042
|
+
F: __dxlog_file4,
|
|
1043
|
+
L: 256,
|
|
1044
|
+
S: this,
|
|
1045
|
+
C: (f, a) => f(...a)
|
|
1046
|
+
});
|
|
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 ?? [])
|
|
1131
|
+
});
|
|
1132
|
+
switch (result.type) {
|
|
1133
|
+
case "continue": {
|
|
1134
|
+
collector.pushMessage(result.message);
|
|
1135
|
+
yield* emitMessageAsEvents(result.message);
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
case "break": {
|
|
1139
|
+
throw new Error("Tool use breaks not allowed");
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}.bind(this));
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
controller.abort();
|
|
1146
|
+
throw error;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
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 = "";
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
|
|
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";
|
|
1213
|
+
|
|
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
|
+
};
|
|
1302
|
+
|
|
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
|
+
}
|
|
1677
|
+
};
|
|
1241
1678
|
export {
|
|
1242
|
-
|
|
1679
|
+
AIServiceEdgeClient,
|
|
1243
1680
|
DEFAULT_LLM_MODEL,
|
|
1244
1681
|
DEFAULT_LLM_MODELS,
|
|
1245
1682
|
EchoDataSource,
|
|
1246
1683
|
GenerateRequest,
|
|
1247
|
-
|
|
1684
|
+
GenerationStreamImpl,
|
|
1248
1685
|
LLMModel,
|
|
1686
|
+
MessageCollector,
|
|
1249
1687
|
MixedStreamParser,
|
|
1688
|
+
OllamaClient,
|
|
1250
1689
|
StreamTransform,
|
|
1251
1690
|
ToolTypes,
|
|
1252
1691
|
createArtifactElement,
|
|
1253
1692
|
createGenerationStream,
|
|
1693
|
+
emitMessageAsEvents,
|
|
1254
1694
|
executeQuery,
|
|
1255
1695
|
executeQueryPlan,
|
|
1256
1696
|
formatInferredRelationshipLabel,
|