@dxos/assistant 0.8.0 → 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
|
@@ -10,8 +10,25 @@ import {
|
|
|
10
10
|
parseCypherQuery
|
|
11
11
|
} from "./chunk-GOVFOBIB.mjs";
|
|
12
12
|
|
|
13
|
-
// packages/experimental/assistant/src/ai-service/
|
|
14
|
-
|
|
13
|
+
// packages/experimental/assistant/src/ai-service/defs.ts
|
|
14
|
+
var DEFAULT_LLM_MODEL = "@anthropic/claude-3-5-sonnet-20241022";
|
|
15
|
+
var DEFAULT_LLM_MODELS = [
|
|
16
|
+
// AI Gateway.
|
|
17
|
+
// https://developers.cloudflare.com/ai-gateway
|
|
18
|
+
"@anthropic/claude-3-5-haiku-20241022",
|
|
19
|
+
"@anthropic/claude-3-5-sonnet-20241022",
|
|
20
|
+
"@anthropic/claude-3-7-sonnet-20250219",
|
|
21
|
+
// Workers AI.
|
|
22
|
+
// https://developers.cloudflare.com/workers-ai/models
|
|
23
|
+
"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
|
|
24
|
+
"@hf/nousresearch/hermes-2-pro-mistral-7b",
|
|
25
|
+
"@ollama/llama-3-1-nemotron-70b-instruct",
|
|
26
|
+
"@ollama/llama-3-1-nemotron-mini-4b-instruct",
|
|
27
|
+
"@ollama/llama-3-2-3b"
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// packages/experimental/assistant/src/ai-service/edge-client.ts
|
|
31
|
+
import { Schema as S } from "effect";
|
|
15
32
|
import { Message } from "@dxos/artifact";
|
|
16
33
|
import { invariant } from "@dxos/invariant";
|
|
17
34
|
import { log as log2 } from "@dxos/log";
|
|
@@ -271,7 +288,7 @@ var createGenerationStream = (response, controller = new AbortController()) => {
|
|
|
271
288
|
raw: sse.raw
|
|
272
289
|
}, {
|
|
273
290
|
F: __dxlog_file,
|
|
274
|
-
L:
|
|
291
|
+
L: 22,
|
|
275
292
|
S: this,
|
|
276
293
|
C: (f, a) => f(...a)
|
|
277
294
|
});
|
|
@@ -287,7 +304,7 @@ var createGenerationStream = (response, controller = new AbortController()) => {
|
|
|
287
304
|
raw: sse.raw
|
|
288
305
|
}, {
|
|
289
306
|
F: __dxlog_file,
|
|
290
|
-
L:
|
|
307
|
+
L: 38,
|
|
291
308
|
S: this,
|
|
292
309
|
C: (f, a) => f(...a)
|
|
293
310
|
});
|
|
@@ -302,9 +319,9 @@ var createGenerationStream = (response, controller = new AbortController()) => {
|
|
|
302
319
|
}
|
|
303
320
|
}
|
|
304
321
|
};
|
|
305
|
-
return new
|
|
322
|
+
return new GenerationStreamImpl(controller, iterator);
|
|
306
323
|
};
|
|
307
|
-
var
|
|
324
|
+
var GenerationStreamImpl = class {
|
|
308
325
|
constructor(_controller, _getIterator) {
|
|
309
326
|
this._controller = _controller;
|
|
310
327
|
this._getIterator = _getIterator;
|
|
@@ -343,6 +360,7 @@ var GenerationStream = class {
|
|
|
343
360
|
this._done.wake();
|
|
344
361
|
} catch (err) {
|
|
345
362
|
this._done.throw(err);
|
|
363
|
+
throw err;
|
|
346
364
|
}
|
|
347
365
|
};
|
|
348
366
|
return generator.call(this);
|
|
@@ -350,13 +368,13 @@ var GenerationStream = class {
|
|
|
350
368
|
}
|
|
351
369
|
};
|
|
352
370
|
|
|
353
|
-
// packages/experimental/assistant/src/ai-service/client.ts
|
|
354
|
-
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/client.ts";
|
|
355
|
-
var
|
|
371
|
+
// packages/experimental/assistant/src/ai-service/edge-client.ts
|
|
372
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/edge-client.ts";
|
|
373
|
+
var AIServiceEdgeClient = class {
|
|
356
374
|
constructor({ endpoint }) {
|
|
357
375
|
invariant(endpoint, "endpoint is required", {
|
|
358
376
|
F: __dxlog_file2,
|
|
359
|
-
L:
|
|
377
|
+
L: 23,
|
|
360
378
|
S: this,
|
|
361
379
|
A: [
|
|
362
380
|
"endpoint",
|
|
@@ -365,6 +383,10 @@ var AIServiceClientImpl = class {
|
|
|
365
383
|
});
|
|
366
384
|
this._endpoint = endpoint;
|
|
367
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* @deprecated
|
|
388
|
+
*/
|
|
389
|
+
// TODO(burdon): Remove.
|
|
368
390
|
async appendMessages(messages) {
|
|
369
391
|
const url = `${this._endpoint}/message`;
|
|
370
392
|
const res = await fetch(url, {
|
|
@@ -384,15 +406,11 @@ var AIServiceClientImpl = class {
|
|
|
384
406
|
* Process request and open message stream.
|
|
385
407
|
*/
|
|
386
408
|
async exec(request) {
|
|
387
|
-
request = {
|
|
388
|
-
tools: request.tools ?? [],
|
|
389
|
-
...request
|
|
390
|
-
};
|
|
391
409
|
log2.info("requesting", {
|
|
392
410
|
endpoint: this._endpoint
|
|
393
411
|
}, {
|
|
394
412
|
F: __dxlog_file2,
|
|
395
|
-
L:
|
|
413
|
+
L: 50,
|
|
396
414
|
S: this,
|
|
397
415
|
C: (f, a) => f(...a)
|
|
398
416
|
});
|
|
@@ -403,11 +421,15 @@ var AIServiceClientImpl = class {
|
|
|
403
421
|
headers: {
|
|
404
422
|
"Content-Type": "application/json"
|
|
405
423
|
},
|
|
406
|
-
body: JSON.stringify(
|
|
424
|
+
body: JSON.stringify({
|
|
425
|
+
// TODO(dmaretskyi): Errors if tools are not provided.
|
|
426
|
+
tools: request.tools ?? [],
|
|
427
|
+
...request
|
|
428
|
+
})
|
|
407
429
|
});
|
|
408
430
|
invariant(response.body instanceof ReadableStream, void 0, {
|
|
409
431
|
F: __dxlog_file2,
|
|
410
|
-
L:
|
|
432
|
+
L: 63,
|
|
411
433
|
S: this,
|
|
412
434
|
A: [
|
|
413
435
|
"response.body instanceof ReadableStream",
|
|
@@ -418,495 +440,81 @@ var AIServiceClientImpl = class {
|
|
|
418
440
|
}
|
|
419
441
|
};
|
|
420
442
|
|
|
421
|
-
// packages/experimental/assistant/src/ai-service/
|
|
422
|
-
var
|
|
423
|
-
var DEFAULT_LLM_MODELS = [
|
|
424
|
-
// AI Gateway.
|
|
425
|
-
// https://developers.cloudflare.com/ai-gateway
|
|
426
|
-
"@anthropic/claude-3-5-haiku-20241022",
|
|
427
|
-
"@anthropic/claude-3-5-sonnet-20241022",
|
|
428
|
-
"@anthropic/claude-3-7-sonnet-20250219",
|
|
429
|
-
// Workers AI.
|
|
430
|
-
// https://developers.cloudflare.com/workers-ai/models
|
|
431
|
-
"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
|
|
432
|
-
"@hf/nousresearch/hermes-2-pro-mistral-7b",
|
|
433
|
-
"@ollama/llama-3-1-nemotron-70b-instruct",
|
|
434
|
-
"@ollama/llama-3-1-nemotron-mini-4b-instruct",
|
|
435
|
-
"@ollama/llama-3-2-3b"
|
|
436
|
-
];
|
|
437
|
-
|
|
438
|
-
// packages/experimental/assistant/src/ai-service/parser.ts
|
|
439
|
-
import { Message as Message2 } from "@dxos/artifact";
|
|
440
|
-
import { Event } from "@dxos/async";
|
|
441
|
-
import { createStatic } from "@dxos/echo-schema";
|
|
442
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
443
|
-
import { log as log3 } from "@dxos/log";
|
|
444
|
-
import { isNotFalsy, safeParseJson } from "@dxos/util";
|
|
445
|
-
|
|
446
|
-
// packages/experimental/assistant/src/ai-service/transform.ts
|
|
447
|
-
import * as P from "parsimmon";
|
|
448
|
-
var textChunk = P.regexp(/[^<]+/).map((content) => ({
|
|
449
|
-
type: "text",
|
|
450
|
-
content
|
|
451
|
-
}));
|
|
452
|
-
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]) => [
|
|
453
|
-
name,
|
|
454
|
-
value
|
|
455
|
-
]);
|
|
456
|
-
var tagName = P.regexp(/[a-zA-Z_][a-zA-Z0-9_-]*/);
|
|
457
|
-
var selfClosingTag = P.seqMap(
|
|
458
|
-
//
|
|
459
|
-
P.string("<"),
|
|
460
|
-
tagName,
|
|
461
|
-
P.optWhitespace.then(attribute).many(),
|
|
462
|
-
P.optWhitespace.then(P.string("/>")),
|
|
463
|
-
P.optWhitespace,
|
|
464
|
-
(_, tag, attributes, __) => ({
|
|
465
|
-
type: "tag",
|
|
466
|
-
tag,
|
|
467
|
-
attributes: Object.fromEntries(attributes),
|
|
468
|
-
content: [],
|
|
469
|
-
selfClosing: true
|
|
470
|
-
})
|
|
471
|
-
);
|
|
472
|
-
var openTag = P.seqMap(
|
|
473
|
-
//
|
|
474
|
-
P.string("<"),
|
|
475
|
-
tagName,
|
|
476
|
-
P.optWhitespace.then(attribute).many(),
|
|
477
|
-
P.string(">"),
|
|
478
|
-
P.regexp(/[^<]*/),
|
|
479
|
-
(_, tag, attributes, __, content) => ({
|
|
480
|
-
type: "tag",
|
|
481
|
-
tag,
|
|
482
|
-
attributes: Object.fromEntries(attributes),
|
|
483
|
-
content: content.length ? [
|
|
484
|
-
{
|
|
485
|
-
type: "text",
|
|
486
|
-
content
|
|
487
|
-
}
|
|
488
|
-
] : []
|
|
489
|
-
})
|
|
490
|
-
);
|
|
491
|
-
var closeTag = P.seqMap(
|
|
492
|
-
//
|
|
493
|
-
P.string("</"),
|
|
494
|
-
tagName,
|
|
495
|
-
P.string(">"),
|
|
496
|
-
P.optWhitespace,
|
|
497
|
-
(_, tag) => ({
|
|
498
|
-
type: "tag",
|
|
499
|
-
tag,
|
|
500
|
-
content: [],
|
|
501
|
-
closing: true
|
|
502
|
-
})
|
|
503
|
-
);
|
|
504
|
-
var mixedChunk = P.alt(selfClosingTag, openTag, closeTag, textChunk);
|
|
505
|
-
var StreamTransform = class {
|
|
506
|
-
constructor() {
|
|
507
|
-
this._buffer = "";
|
|
508
|
-
}
|
|
509
|
-
transform(chunk) {
|
|
510
|
-
this._buffer += chunk;
|
|
511
|
-
const results = [];
|
|
512
|
-
const parser = mixedChunk.many().skip(P.optWhitespace);
|
|
513
|
-
const result = parser.parse(this._buffer);
|
|
514
|
-
if (result.status) {
|
|
515
|
-
for (const chunk2 of result.value) {
|
|
516
|
-
results.push(chunk2);
|
|
517
|
-
}
|
|
518
|
-
this._buffer = "";
|
|
519
|
-
}
|
|
520
|
-
return results;
|
|
521
|
-
}
|
|
522
|
-
// TODO(burdon): Process remaining buffer?
|
|
523
|
-
flush() {
|
|
524
|
-
const remaining = this._buffer;
|
|
525
|
-
this._buffer = "";
|
|
526
|
-
return remaining.length > 0 ? [
|
|
527
|
-
{
|
|
528
|
-
type: "text",
|
|
529
|
-
content: remaining
|
|
530
|
-
}
|
|
531
|
-
] : [];
|
|
532
|
-
}
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
// packages/experimental/assistant/src/ai-service/parser.ts
|
|
536
|
-
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/parser.ts";
|
|
537
|
-
var MixedStreamParser = class {
|
|
443
|
+
// packages/experimental/assistant/src/ai-service/message-collector.ts
|
|
444
|
+
var MessageCollector = class {
|
|
538
445
|
constructor() {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
*/
|
|
542
|
-
this.message = new Event();
|
|
543
|
-
/**
|
|
544
|
-
* Complete block added to Message.
|
|
545
|
-
*/
|
|
546
|
-
this.block = new Event();
|
|
547
|
-
/**
|
|
548
|
-
* Update partial block (while streaming).
|
|
549
|
-
*/
|
|
550
|
-
this.update = new Event();
|
|
446
|
+
this._messages = [];
|
|
447
|
+
this._pendingMessage = void 0;
|
|
551
448
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
if (messageBlock) {
|
|
555
|
-
if (messageBlock.type === "text" && messageBlock.text.length === 0) {
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
invariant2(this._message, void 0, {
|
|
559
|
-
F: __dxlog_file3,
|
|
560
|
-
L: 46,
|
|
561
|
-
S: this,
|
|
562
|
-
A: [
|
|
563
|
-
"this._message",
|
|
564
|
-
""
|
|
565
|
-
]
|
|
566
|
-
});
|
|
567
|
-
this._message.content.push(messageBlock);
|
|
568
|
-
this.block.emit(messageBlock);
|
|
569
|
-
}
|
|
449
|
+
get messages() {
|
|
450
|
+
return this._messages;
|
|
570
451
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if (messageBlock) {
|
|
574
|
-
messageBlock.pending = true;
|
|
575
|
-
this.update.emit(messageBlock);
|
|
576
|
-
}
|
|
452
|
+
get pendingMessage() {
|
|
453
|
+
return this._pendingMessage;
|
|
577
454
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
for await (const event of stream) {
|
|
588
|
-
switch (event.type) {
|
|
589
|
-
//
|
|
590
|
-
// Messages.
|
|
591
|
-
//
|
|
592
|
-
case "message_start": {
|
|
593
|
-
if (this._message) {
|
|
594
|
-
log3.warn("unexpected message_start", void 0, {
|
|
595
|
-
F: __dxlog_file3,
|
|
596
|
-
L: 88,
|
|
597
|
-
S: this,
|
|
598
|
-
C: (f, a) => f(...a)
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
this._message = createStatic(Message2, {
|
|
602
|
-
role: "assistant",
|
|
603
|
-
content: []
|
|
604
|
-
});
|
|
605
|
-
this.message.emit(this._message);
|
|
606
|
-
break;
|
|
455
|
+
push(event) {
|
|
456
|
+
switch (event.type) {
|
|
457
|
+
case "message_start":
|
|
458
|
+
this._pendingMessage = event.message;
|
|
459
|
+
break;
|
|
460
|
+
case "message_stop":
|
|
461
|
+
if (this._pendingMessage) {
|
|
462
|
+
this._messages.push(this._pendingMessage);
|
|
463
|
+
this._pendingMessage = void 0;
|
|
607
464
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
S: this,
|
|
614
|
-
C: (f, a) => f(...a)
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
break;
|
|
465
|
+
break;
|
|
466
|
+
case "content_block_start":
|
|
467
|
+
if (this._pendingMessage) {
|
|
468
|
+
this._pendingMessage.content = this._pendingMessage.content || [];
|
|
469
|
+
this._pendingMessage.content[event.index] = event.content;
|
|
618
470
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
C: (f, a) => f(...a)
|
|
626
|
-
});
|
|
627
|
-
} else {
|
|
628
|
-
messagesCollected.push(this._message);
|
|
629
|
-
this._message = void 0;
|
|
471
|
+
break;
|
|
472
|
+
case "content_block_delta":
|
|
473
|
+
if (this._pendingMessage && this._pendingMessage.content) {
|
|
474
|
+
const block = this._pendingMessage.content[event.index];
|
|
475
|
+
if (block && event.delta.type === "text_delta" && block.type === "text") {
|
|
476
|
+
block.text = (block.text || "") + event.delta.text;
|
|
630
477
|
}
|
|
631
|
-
break;
|
|
632
478
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
if (
|
|
638
|
-
|
|
639
|
-
content: contentBlock
|
|
640
|
-
}, {
|
|
641
|
-
F: __dxlog_file3,
|
|
642
|
-
L: 120,
|
|
643
|
-
S: this,
|
|
644
|
-
C: (f, a) => f(...a)
|
|
645
|
-
});
|
|
479
|
+
break;
|
|
480
|
+
case "content_block_stop":
|
|
481
|
+
if (this._pendingMessage && this._pendingMessage.content) {
|
|
482
|
+
const block = this._pendingMessage.content[event.index];
|
|
483
|
+
if (block) {
|
|
484
|
+
block.pending = false;
|
|
646
485
|
}
|
|
647
|
-
contentBlock = event.content;
|
|
648
|
-
break;
|
|
649
486
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
L: 130,
|
|
655
|
-
S: this,
|
|
656
|
-
C: (f, a) => f(...a)
|
|
657
|
-
});
|
|
658
|
-
break;
|
|
659
|
-
}
|
|
660
|
-
switch (event.delta.type) {
|
|
661
|
-
//
|
|
662
|
-
// Text
|
|
663
|
-
//
|
|
664
|
-
case "text_delta": {
|
|
665
|
-
const chunks = transformer.transform(event.delta.text);
|
|
666
|
-
for (const chunk of chunks) {
|
|
667
|
-
switch (streamBlock?.type) {
|
|
668
|
-
//
|
|
669
|
-
// XML Fragment.
|
|
670
|
-
//
|
|
671
|
-
case "tag": {
|
|
672
|
-
if (chunk.type === "tag") {
|
|
673
|
-
if (chunk.selfClosing) {
|
|
674
|
-
streamBlock.content.push(chunk);
|
|
675
|
-
} else if (chunk.closing) {
|
|
676
|
-
if (stack.length > 0) {
|
|
677
|
-
const top = stack.pop();
|
|
678
|
-
invariant2(top && top.type === "tag", void 0, {
|
|
679
|
-
F: __dxlog_file3,
|
|
680
|
-
L: 154,
|
|
681
|
-
S: this,
|
|
682
|
-
A: [
|
|
683
|
-
"top && top.type === 'tag'",
|
|
684
|
-
""
|
|
685
|
-
]
|
|
686
|
-
});
|
|
687
|
-
top.content.push(streamBlock);
|
|
688
|
-
streamBlock = top;
|
|
689
|
-
} else {
|
|
690
|
-
this._emitBlock(contentBlock, streamBlock);
|
|
691
|
-
streamBlock = void 0;
|
|
692
|
-
}
|
|
693
|
-
} else {
|
|
694
|
-
stack.push(streamBlock);
|
|
695
|
-
streamBlock = chunk;
|
|
696
|
-
}
|
|
697
|
-
} else {
|
|
698
|
-
if (streamBlock.content.length === 0) {
|
|
699
|
-
streamBlock.content.push(chunk);
|
|
700
|
-
} else {
|
|
701
|
-
const last = streamBlock.content.at(-1);
|
|
702
|
-
invariant2(last, void 0, {
|
|
703
|
-
F: __dxlog_file3,
|
|
704
|
-
L: 171,
|
|
705
|
-
S: this,
|
|
706
|
-
A: [
|
|
707
|
-
"last",
|
|
708
|
-
""
|
|
709
|
-
]
|
|
710
|
-
});
|
|
711
|
-
if (last.type === "text") {
|
|
712
|
-
last.content += chunk.content;
|
|
713
|
-
} else {
|
|
714
|
-
streamBlock.content.push(chunk);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
break;
|
|
719
|
-
}
|
|
720
|
-
//
|
|
721
|
-
// Text Fragment.
|
|
722
|
-
//
|
|
723
|
-
case "text": {
|
|
724
|
-
if (chunk.type === "tag") {
|
|
725
|
-
this._emitBlock(contentBlock, streamBlock);
|
|
726
|
-
if (chunk.selfClosing) {
|
|
727
|
-
this._emitBlock(contentBlock, chunk);
|
|
728
|
-
streamBlock = void 0;
|
|
729
|
-
} else {
|
|
730
|
-
streamBlock = chunk;
|
|
731
|
-
}
|
|
732
|
-
} else {
|
|
733
|
-
streamBlock.content += chunk.content;
|
|
734
|
-
}
|
|
735
|
-
break;
|
|
736
|
-
}
|
|
737
|
-
//
|
|
738
|
-
// No current chunk.
|
|
739
|
-
//
|
|
740
|
-
default: {
|
|
741
|
-
if (chunk.type === "tag" && chunk.selfClosing) {
|
|
742
|
-
this._emitBlock(contentBlock, chunk);
|
|
743
|
-
} else {
|
|
744
|
-
streamBlock = chunk;
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
break;
|
|
750
|
-
}
|
|
751
|
-
//
|
|
752
|
-
// JSON
|
|
753
|
-
//
|
|
754
|
-
case "input_json_delta": {
|
|
755
|
-
if (!contentBlock) {
|
|
756
|
-
log3.warn("unexpected input_json_delta", {
|
|
757
|
-
contentBlock
|
|
758
|
-
}, {
|
|
759
|
-
F: __dxlog_file3,
|
|
760
|
-
L: 221,
|
|
761
|
-
S: this,
|
|
762
|
-
C: (f, a) => f(...a)
|
|
763
|
-
});
|
|
764
|
-
break;
|
|
765
|
-
}
|
|
766
|
-
if (streamBlock?.type === "json") {
|
|
767
|
-
streamBlock.content += event.delta.partial_json;
|
|
768
|
-
} else {
|
|
769
|
-
streamBlock = {
|
|
770
|
-
type: "json",
|
|
771
|
-
content: event.delta.partial_json
|
|
772
|
-
};
|
|
773
|
-
}
|
|
774
|
-
break;
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
break;
|
|
778
|
-
}
|
|
779
|
-
case "content_block_stop": {
|
|
780
|
-
if (!contentBlock) {
|
|
781
|
-
log3.warn("unexpected content_block_stop", void 0, {
|
|
782
|
-
F: __dxlog_file3,
|
|
783
|
-
L: 238,
|
|
784
|
-
S: this,
|
|
785
|
-
C: (f, a) => f(...a)
|
|
786
|
-
});
|
|
787
|
-
break;
|
|
788
|
-
}
|
|
789
|
-
this._emitBlock(contentBlock, streamBlock);
|
|
790
|
-
contentBlock = void 0;
|
|
791
|
-
streamBlock = void 0;
|
|
792
|
-
break;
|
|
487
|
+
break;
|
|
488
|
+
case "message_delta":
|
|
489
|
+
if (this._pendingMessage && event.delta) {
|
|
490
|
+
Object.assign(this._pendingMessage, event.delta);
|
|
793
491
|
}
|
|
794
|
-
|
|
795
|
-
if (contentBlock && streamBlock) {
|
|
796
|
-
this._emitUpdate(contentBlock, streamBlock);
|
|
797
|
-
}
|
|
492
|
+
break;
|
|
798
493
|
}
|
|
799
|
-
return messagesCollected;
|
|
800
494
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
log3("mergeMessageBlock", {
|
|
804
|
-
contentBlock,
|
|
805
|
-
streamBlock
|
|
806
|
-
}, {
|
|
807
|
-
F: __dxlog_file3,
|
|
808
|
-
L: 265,
|
|
809
|
-
S: void 0,
|
|
810
|
-
C: (f, a) => f(...a)
|
|
811
|
-
});
|
|
812
|
-
switch (streamBlock.type) {
|
|
813
|
-
//
|
|
814
|
-
// Text
|
|
815
|
-
//
|
|
816
|
-
case "text": {
|
|
817
|
-
return {
|
|
818
|
-
...contentBlock,
|
|
819
|
-
type: "text",
|
|
820
|
-
text: streamBlock.content
|
|
821
|
-
};
|
|
822
|
-
}
|
|
823
|
-
//
|
|
824
|
-
// XML
|
|
825
|
-
//
|
|
826
|
-
case "tag": {
|
|
827
|
-
switch (streamBlock.tag) {
|
|
828
|
-
case "cot": {
|
|
829
|
-
const content = streamBlock.content.map((block) => {
|
|
830
|
-
switch (block.type) {
|
|
831
|
-
case "text":
|
|
832
|
-
return block.content;
|
|
833
|
-
default:
|
|
834
|
-
return null;
|
|
835
|
-
}
|
|
836
|
-
}).filter(isNotFalsy).join("\n");
|
|
837
|
-
return {
|
|
838
|
-
...contentBlock,
|
|
839
|
-
type: "text",
|
|
840
|
-
disposition: "cot",
|
|
841
|
-
text: content
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
case "artifact": {
|
|
845
|
-
const { attributes } = streamBlock;
|
|
846
|
-
return {
|
|
847
|
-
type: "json",
|
|
848
|
-
disposition: "artifact",
|
|
849
|
-
json: JSON.stringify(attributes)
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
case "suggest": {
|
|
853
|
-
if (streamBlock.content.length === 1 && streamBlock.content[0].type === "text") {
|
|
854
|
-
return {
|
|
855
|
-
type: "json",
|
|
856
|
-
disposition: "suggest",
|
|
857
|
-
json: JSON.stringify({
|
|
858
|
-
text: streamBlock.content[0].content
|
|
859
|
-
})
|
|
860
|
-
};
|
|
861
|
-
}
|
|
862
|
-
break;
|
|
863
|
-
}
|
|
864
|
-
case "select": {
|
|
865
|
-
return {
|
|
866
|
-
type: "json",
|
|
867
|
-
disposition: "select",
|
|
868
|
-
json: JSON.stringify({
|
|
869
|
-
options: streamBlock.content.flatMap((content) => content.type === "tag" && content.content.length === 1 && content.content[0].type === "text" ? [
|
|
870
|
-
content.content[0].content
|
|
871
|
-
] : [])
|
|
872
|
-
})
|
|
873
|
-
};
|
|
874
|
-
}
|
|
875
|
-
case "tool-list": {
|
|
876
|
-
return {
|
|
877
|
-
type: "json",
|
|
878
|
-
disposition: "tool_list",
|
|
879
|
-
json: JSON.stringify({})
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
break;
|
|
884
|
-
}
|
|
885
|
-
//
|
|
886
|
-
// JSON
|
|
887
|
-
//
|
|
888
|
-
case "json": {
|
|
889
|
-
switch (contentBlock.type) {
|
|
890
|
-
case "tool_use": {
|
|
891
|
-
return {
|
|
892
|
-
...contentBlock,
|
|
893
|
-
input: safeParseJson(streamBlock.content) ?? {}
|
|
894
|
-
};
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
return {
|
|
898
|
-
...contentBlock,
|
|
899
|
-
type: "json",
|
|
900
|
-
disposition: "artifact",
|
|
901
|
-
json: streamBlock.content
|
|
902
|
-
};
|
|
903
|
-
}
|
|
495
|
+
pushMessage(message) {
|
|
496
|
+
this._messages.push(message);
|
|
904
497
|
}
|
|
905
498
|
};
|
|
499
|
+
function* emitMessageAsEvents(message) {
|
|
500
|
+
yield {
|
|
501
|
+
type: "message_start",
|
|
502
|
+
message
|
|
503
|
+
};
|
|
504
|
+
yield {
|
|
505
|
+
type: "message_stop"
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// packages/experimental/assistant/src/ai-service/ollama-client.ts
|
|
510
|
+
import { defineTool, ToolResult as ToolResult2 } from "@dxos/artifact";
|
|
511
|
+
import { ObjectId as ObjectId3, S as S3 } from "@dxos/echo-schema";
|
|
512
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
513
|
+
import { log as log4 } from "@dxos/log";
|
|
906
514
|
|
|
907
515
|
// packages/experimental/assistant/src/ai-service/types.ts
|
|
908
|
-
import { Schema as S2 } from "
|
|
909
|
-
import { Tool, Message as
|
|
516
|
+
import { Schema as S2 } from "effect";
|
|
517
|
+
import { Tool, Message as Message2, SpaceIdSchema } from "@dxos/artifact";
|
|
910
518
|
import { ObjectId } from "@dxos/echo-schema";
|
|
911
519
|
var createArtifactElement = (id) => `<artifact id=${id} />`;
|
|
912
520
|
var LLMModel = S2.Literal(...DEFAULT_LLM_MODELS);
|
|
@@ -934,27 +542,27 @@ var GenerateRequest = S2.Struct({
|
|
|
934
542
|
* History of messages to include in the context window.
|
|
935
543
|
*/
|
|
936
544
|
// TODO(burdon): Rename messages.
|
|
937
|
-
history: S2.optional(S2.Array(
|
|
545
|
+
history: S2.optional(S2.Array(Message2)),
|
|
938
546
|
/**
|
|
939
547
|
* Current request.
|
|
940
548
|
*/
|
|
941
|
-
prompt: S2.optional(
|
|
549
|
+
prompt: S2.optional(Message2)
|
|
942
550
|
});
|
|
943
551
|
|
|
944
552
|
// packages/experimental/assistant/src/conversation/conversation.ts
|
|
945
|
-
import { ToolResult, Message as
|
|
946
|
-
import { createStatic
|
|
947
|
-
import { invariant as
|
|
948
|
-
import { log as
|
|
949
|
-
var
|
|
553
|
+
import { ToolResult, Message as Message3 } from "@dxos/artifact";
|
|
554
|
+
import { createStatic, ObjectId as ObjectId2 } from "@dxos/echo-schema";
|
|
555
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
556
|
+
import { log as log3 } from "@dxos/log";
|
|
557
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/conversation/conversation.ts";
|
|
950
558
|
var runLLM = async (params) => {
|
|
951
559
|
let conversationResult = null;
|
|
952
560
|
const history = params.history ?? [];
|
|
953
561
|
const generate = async () => {
|
|
954
|
-
|
|
562
|
+
log3("llm generate", {
|
|
955
563
|
tools: params.tools
|
|
956
564
|
}, {
|
|
957
|
-
F:
|
|
565
|
+
F: __dxlog_file3,
|
|
958
566
|
L: 44,
|
|
959
567
|
S: void 0,
|
|
960
568
|
C: (f, a) => f(...a)
|
|
@@ -974,8 +582,8 @@ var runLLM = async (params) => {
|
|
|
974
582
|
await stream.complete();
|
|
975
583
|
const messages = [];
|
|
976
584
|
const message = messages.at(-1);
|
|
977
|
-
|
|
978
|
-
F:
|
|
585
|
+
invariant2(message, void 0, {
|
|
586
|
+
F: __dxlog_file3,
|
|
979
587
|
L: 63,
|
|
980
588
|
S: void 0,
|
|
981
589
|
A: [
|
|
@@ -983,17 +591,17 @@ var runLLM = async (params) => {
|
|
|
983
591
|
""
|
|
984
592
|
]
|
|
985
593
|
});
|
|
986
|
-
|
|
594
|
+
log3("llm result", {
|
|
987
595
|
time: Date.now() - beginTs,
|
|
988
596
|
message
|
|
989
597
|
}, {
|
|
990
|
-
F:
|
|
598
|
+
F: __dxlog_file3,
|
|
991
599
|
L: 65,
|
|
992
600
|
S: void 0,
|
|
993
601
|
C: (f, a) => f(...a)
|
|
994
602
|
});
|
|
995
|
-
|
|
996
|
-
F:
|
|
603
|
+
invariant2(message, void 0, {
|
|
604
|
+
F: __dxlog_file3,
|
|
997
605
|
L: 66,
|
|
998
606
|
S: void 0,
|
|
999
607
|
A: [
|
|
@@ -1012,8 +620,8 @@ var runLLM = async (params) => {
|
|
|
1012
620
|
const isToolUse2 = message.content.at(-1)?.type === "tool_use";
|
|
1013
621
|
if (isToolUse2) {
|
|
1014
622
|
const toolCalls = message.content.filter((block) => block.type === "tool_use");
|
|
1015
|
-
|
|
1016
|
-
F:
|
|
623
|
+
invariant2(toolCalls.length === 1, void 0, {
|
|
624
|
+
F: __dxlog_file3,
|
|
1017
625
|
L: 77,
|
|
1018
626
|
S: void 0,
|
|
1019
627
|
A: [
|
|
@@ -1026,8 +634,8 @@ var runLLM = async (params) => {
|
|
|
1026
634
|
if (!tool) {
|
|
1027
635
|
throw new Error(`Tool not found: ${toolCall.name}`);
|
|
1028
636
|
}
|
|
1029
|
-
|
|
1030
|
-
F:
|
|
637
|
+
invariant2(tool.execute, void 0, {
|
|
638
|
+
F: __dxlog_file3,
|
|
1031
639
|
L: 84,
|
|
1032
640
|
S: void 0,
|
|
1033
641
|
A: [
|
|
@@ -1038,10 +646,10 @@ var runLLM = async (params) => {
|
|
|
1038
646
|
const toolResult = await tool.execute(toolCall.input);
|
|
1039
647
|
switch (toolResult.kind) {
|
|
1040
648
|
case "error": {
|
|
1041
|
-
|
|
649
|
+
log3.warn("tool error", {
|
|
1042
650
|
message: toolResult.message
|
|
1043
651
|
}, {
|
|
1044
|
-
F:
|
|
652
|
+
F: __dxlog_file3,
|
|
1045
653
|
L: 88,
|
|
1046
654
|
S: void 0,
|
|
1047
655
|
C: (f, a) => f(...a)
|
|
@@ -1068,10 +676,10 @@ var runLLM = async (params) => {
|
|
|
1068
676
|
return true;
|
|
1069
677
|
}
|
|
1070
678
|
case "success": {
|
|
1071
|
-
|
|
679
|
+
log3("tool success", {
|
|
1072
680
|
result: toolResult.result
|
|
1073
681
|
}, {
|
|
1074
|
-
F:
|
|
682
|
+
F: __dxlog_file3,
|
|
1075
683
|
L: 110,
|
|
1076
684
|
S: void 0,
|
|
1077
685
|
C: (f, a) => f(...a)
|
|
@@ -1097,10 +705,10 @@ var runLLM = async (params) => {
|
|
|
1097
705
|
return true;
|
|
1098
706
|
}
|
|
1099
707
|
case "break": {
|
|
1100
|
-
|
|
708
|
+
log3("tool break", {
|
|
1101
709
|
result: toolResult.result
|
|
1102
710
|
}, {
|
|
1103
|
-
F:
|
|
711
|
+
F: __dxlog_file3,
|
|
1104
712
|
L: 131,
|
|
1105
713
|
S: void 0,
|
|
1106
714
|
C: (f, a) => f(...a)
|
|
@@ -1119,10 +727,10 @@ var runLLM = async (params) => {
|
|
|
1119
727
|
history
|
|
1120
728
|
};
|
|
1121
729
|
};
|
|
1122
|
-
var isToolUse = (message) => {
|
|
730
|
+
var isToolUse = (message, { onlyToolNames } = {}) => {
|
|
1123
731
|
const block = message.content.at(-1);
|
|
1124
|
-
|
|
1125
|
-
F:
|
|
732
|
+
invariant2(block, void 0, {
|
|
733
|
+
F: __dxlog_file3,
|
|
1126
734
|
L: 152,
|
|
1127
735
|
S: void 0,
|
|
1128
736
|
A: [
|
|
@@ -1130,12 +738,12 @@ var isToolUse = (message) => {
|
|
|
1130
738
|
""
|
|
1131
739
|
]
|
|
1132
740
|
});
|
|
1133
|
-
return block.type === "tool_use";
|
|
741
|
+
return block.type === "tool_use" && (!onlyToolNames || onlyToolNames.includes(block.name));
|
|
1134
742
|
};
|
|
1135
743
|
var runTools = async ({ message, tools, extensions }) => {
|
|
1136
744
|
const toolCalls = message.content.filter((block) => block.type === "tool_use");
|
|
1137
|
-
|
|
1138
|
-
F:
|
|
745
|
+
invariant2(toolCalls.length === 1, void 0, {
|
|
746
|
+
F: __dxlog_file3,
|
|
1139
747
|
L: 174,
|
|
1140
748
|
S: void 0,
|
|
1141
749
|
A: [
|
|
@@ -1150,8 +758,8 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1150
758
|
}
|
|
1151
759
|
let toolResult;
|
|
1152
760
|
try {
|
|
1153
|
-
|
|
1154
|
-
F:
|
|
761
|
+
invariant2(tool.execute, void 0, {
|
|
762
|
+
F: __dxlog_file3,
|
|
1155
763
|
L: 183,
|
|
1156
764
|
S: void 0,
|
|
1157
765
|
A: [
|
|
@@ -1163,10 +771,10 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1163
771
|
extensions
|
|
1164
772
|
});
|
|
1165
773
|
} catch (error) {
|
|
1166
|
-
|
|
774
|
+
log3("tool error", {
|
|
1167
775
|
error
|
|
1168
776
|
}, {
|
|
1169
|
-
F:
|
|
777
|
+
F: __dxlog_file3,
|
|
1170
778
|
L: 186,
|
|
1171
779
|
S: void 0,
|
|
1172
780
|
C: (f, a) => f(...a)
|
|
@@ -1175,15 +783,15 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1175
783
|
}
|
|
1176
784
|
switch (toolResult.kind) {
|
|
1177
785
|
case "error": {
|
|
1178
|
-
|
|
786
|
+
log3("tool error", {
|
|
1179
787
|
message: toolResult.message
|
|
1180
788
|
}, {
|
|
1181
|
-
F:
|
|
789
|
+
F: __dxlog_file3,
|
|
1182
790
|
L: 192,
|
|
1183
791
|
S: void 0,
|
|
1184
792
|
C: (f, a) => f(...a)
|
|
1185
793
|
});
|
|
1186
|
-
const resultMessage =
|
|
794
|
+
const resultMessage = createStatic(Message3, {
|
|
1187
795
|
role: "user",
|
|
1188
796
|
content: [
|
|
1189
797
|
{
|
|
@@ -1200,22 +808,23 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1200
808
|
};
|
|
1201
809
|
}
|
|
1202
810
|
case "success": {
|
|
1203
|
-
|
|
811
|
+
log3("tool success", {
|
|
1204
812
|
result: toolResult.result
|
|
1205
813
|
}, {
|
|
1206
|
-
F:
|
|
814
|
+
F: __dxlog_file3,
|
|
1207
815
|
L: 213,
|
|
1208
816
|
S: void 0,
|
|
1209
817
|
C: (f, a) => f(...a)
|
|
1210
818
|
});
|
|
1211
|
-
const resultMessage =
|
|
819
|
+
const resultMessage = createStatic(Message3, {
|
|
1212
820
|
role: "user",
|
|
1213
821
|
content: [
|
|
1214
822
|
{
|
|
1215
823
|
type: "tool_result",
|
|
1216
824
|
toolUseId: toolCall.id,
|
|
1217
825
|
content: typeof toolResult.result === "string" ? toolResult.result : JSON.stringify(toolResult.result) ?? ""
|
|
1218
|
-
}
|
|
826
|
+
},
|
|
827
|
+
...toolResult.extractContentBlocks ?? []
|
|
1219
828
|
]
|
|
1220
829
|
});
|
|
1221
830
|
return {
|
|
@@ -1224,11 +833,11 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1224
833
|
};
|
|
1225
834
|
}
|
|
1226
835
|
case "break": {
|
|
1227
|
-
|
|
836
|
+
log3("tool break", {
|
|
1228
837
|
result: toolResult.result
|
|
1229
838
|
}, {
|
|
1230
|
-
F:
|
|
1231
|
-
L:
|
|
839
|
+
F: __dxlog_file3,
|
|
840
|
+
L: 234,
|
|
1232
841
|
S: void 0,
|
|
1233
842
|
C: (f, a) => f(...a)
|
|
1234
843
|
});
|
|
@@ -1239,19 +848,850 @@ var runTools = async ({ message, tools, extensions }) => {
|
|
|
1239
848
|
}
|
|
1240
849
|
}
|
|
1241
850
|
};
|
|
851
|
+
|
|
852
|
+
// packages/experimental/assistant/src/ai-service/ollama-client.ts
|
|
853
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/ollama-client.ts";
|
|
854
|
+
var DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
855
|
+
var DEFAULT_OLLAMA_MODEL = "llama3.2:1b";
|
|
856
|
+
var OllamaClient = class _OllamaClient {
|
|
857
|
+
/**
|
|
858
|
+
* Create a test client with small local model and no temperature for predictable results.
|
|
859
|
+
*/
|
|
860
|
+
static createClient(options) {
|
|
861
|
+
return new _OllamaClient({
|
|
862
|
+
tools: options?.tools,
|
|
863
|
+
overrides: {
|
|
864
|
+
model: DEFAULT_OLLAMA_MODEL,
|
|
865
|
+
temperature: 0
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Check if Ollama server is running and accessible.
|
|
871
|
+
* @returns Promise that resolves to true if Ollama is running, false otherwise.
|
|
872
|
+
*/
|
|
873
|
+
static async isRunning() {
|
|
874
|
+
try {
|
|
875
|
+
const response = await fetch(`${DEFAULT_OLLAMA_URL}/api/version`, {
|
|
876
|
+
method: "GET",
|
|
877
|
+
headers: {
|
|
878
|
+
"Content-Type": "application/json"
|
|
879
|
+
},
|
|
880
|
+
signal: AbortSignal.timeout(100)
|
|
881
|
+
});
|
|
882
|
+
return response.ok;
|
|
883
|
+
} catch (error) {
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
constructor({ endpoint, tools, overrides, maxToolInvocations }) {
|
|
888
|
+
this._endpoint = endpoint ?? DEFAULT_OLLAMA_URL;
|
|
889
|
+
this._tools = tools ?? [];
|
|
890
|
+
this._modelOverride = overrides?.model;
|
|
891
|
+
this._temperatureOverride = overrides?.temperature;
|
|
892
|
+
this._maxToolInvocations = maxToolInvocations ?? 3;
|
|
893
|
+
}
|
|
894
|
+
_getEmbededTools(clientTools) {
|
|
895
|
+
return [
|
|
896
|
+
...this._tools,
|
|
897
|
+
...clientTools.filter((tool) => tool.type !== void 0 && WELL_KNOWN_TOOLS[tool.type]).map((tool) => WELL_KNOWN_TOOLS[tool.type])
|
|
898
|
+
];
|
|
899
|
+
}
|
|
900
|
+
async *_generateStream(request, signal) {
|
|
901
|
+
invariant3(request.prompt === void 0, "Prompt must be in history", {
|
|
902
|
+
F: __dxlog_file4,
|
|
903
|
+
L: 99,
|
|
904
|
+
S: this,
|
|
905
|
+
A: [
|
|
906
|
+
"request.prompt === undefined",
|
|
907
|
+
"'Prompt must be in history'"
|
|
908
|
+
]
|
|
909
|
+
});
|
|
910
|
+
const ollamaRequest = {
|
|
911
|
+
model: this._modelOverride ?? request.model ?? "llama3",
|
|
912
|
+
messages: [],
|
|
913
|
+
stream: true,
|
|
914
|
+
options: {
|
|
915
|
+
temperature: this._temperatureOverride ?? 0.7
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
if (request.systemPrompt) {
|
|
919
|
+
ollamaRequest.messages.push({
|
|
920
|
+
role: "system",
|
|
921
|
+
content: request.systemPrompt
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
if (request.history?.length) {
|
|
925
|
+
for (const message of request.history) {
|
|
926
|
+
const role = message.role === "assistant" ? "assistant" : "user";
|
|
927
|
+
if (message.content.length === 0) {
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
for (const content of message.content) {
|
|
931
|
+
switch (content.type) {
|
|
932
|
+
case "text": {
|
|
933
|
+
ollamaRequest.messages.push({
|
|
934
|
+
role,
|
|
935
|
+
content: content.text
|
|
936
|
+
});
|
|
937
|
+
break;
|
|
938
|
+
}
|
|
939
|
+
case "tool_use": {
|
|
940
|
+
ollamaRequest.messages.push({
|
|
941
|
+
role: "assistant",
|
|
942
|
+
content: "",
|
|
943
|
+
tool_calls: [
|
|
944
|
+
{
|
|
945
|
+
function: {
|
|
946
|
+
name: content.name,
|
|
947
|
+
arguments: content.input
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
]
|
|
951
|
+
});
|
|
952
|
+
break;
|
|
953
|
+
}
|
|
954
|
+
case "tool_result": {
|
|
955
|
+
ollamaRequest.messages.push({
|
|
956
|
+
role: "tool",
|
|
957
|
+
content: JSON.stringify({
|
|
958
|
+
type: "tool_result",
|
|
959
|
+
toolUseId: content.toolUseId,
|
|
960
|
+
content: content.content
|
|
961
|
+
})
|
|
962
|
+
});
|
|
963
|
+
break;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if (request.tools?.length || this._tools.length) {
|
|
970
|
+
const allTools = [
|
|
971
|
+
...request.tools ?? [],
|
|
972
|
+
...this._tools
|
|
973
|
+
];
|
|
974
|
+
const withWellKnownTools = allTools.map((tool) => tool.type !== void 0 && WELL_KNOWN_TOOLS[tool.type] ? WELL_KNOWN_TOOLS[tool.type] : tool);
|
|
975
|
+
if (withWellKnownTools.length > 0) {
|
|
976
|
+
ollamaRequest.tools = withWellKnownTools.map((tool) => ({
|
|
977
|
+
type: "function",
|
|
978
|
+
function: {
|
|
979
|
+
name: tool.name,
|
|
980
|
+
description: tool.description,
|
|
981
|
+
parameters: {
|
|
982
|
+
type: "object",
|
|
983
|
+
properties: tool.parameters?.properties,
|
|
984
|
+
required: tool.parameters?.required
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}));
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
log4.info("generate request", {
|
|
991
|
+
history: request.history,
|
|
992
|
+
prompt: request.prompt,
|
|
993
|
+
ollamaRequest
|
|
994
|
+
}, {
|
|
995
|
+
F: __dxlog_file4,
|
|
996
|
+
L: 193,
|
|
997
|
+
S: this,
|
|
998
|
+
C: (f, a) => f(...a)
|
|
999
|
+
});
|
|
1000
|
+
const response = await fetch(`${this._endpoint}/api/chat`, {
|
|
1001
|
+
method: "POST",
|
|
1002
|
+
headers: {
|
|
1003
|
+
"Content-Type": "application/json"
|
|
1004
|
+
},
|
|
1005
|
+
body: JSON.stringify(ollamaRequest),
|
|
1006
|
+
signal
|
|
1007
|
+
});
|
|
1008
|
+
if (!response.ok) {
|
|
1009
|
+
throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
|
|
1010
|
+
}
|
|
1011
|
+
if (!response.body) {
|
|
1012
|
+
throw new Error("Ollama API returned no response body");
|
|
1013
|
+
}
|
|
1014
|
+
const reader = response.body.pipeThrough(new TextDecoderStream()).pipeThrough(new OllamaDecoderStream()).getReader();
|
|
1015
|
+
try {
|
|
1016
|
+
const messageId = ObjectId3.random();
|
|
1017
|
+
yield {
|
|
1018
|
+
type: "message_start",
|
|
1019
|
+
message: {
|
|
1020
|
+
id: messageId,
|
|
1021
|
+
role: "assistant",
|
|
1022
|
+
content: []
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
const textBlock = {
|
|
1026
|
+
type: "text",
|
|
1027
|
+
text: ""
|
|
1028
|
+
};
|
|
1029
|
+
let currentBlockIndex = 0;
|
|
1030
|
+
yield {
|
|
1031
|
+
type: "content_block_start",
|
|
1032
|
+
index: currentBlockIndex,
|
|
1033
|
+
content: textBlock
|
|
1034
|
+
};
|
|
1035
|
+
while (true) {
|
|
1036
|
+
const { done, value: data } = await reader.read();
|
|
1037
|
+
if (done) {
|
|
1038
|
+
break;
|
|
1039
|
+
}
|
|
1040
|
+
log4.info("ollama data", {
|
|
1041
|
+
data
|
|
1042
|
+
}, {
|
|
1043
|
+
F: __dxlog_file4,
|
|
1044
|
+
L: 256,
|
|
1045
|
+
S: this,
|
|
1046
|
+
C: (f, a) => f(...a)
|
|
1047
|
+
});
|
|
1048
|
+
if (data.message?.content) {
|
|
1049
|
+
yield {
|
|
1050
|
+
type: "content_block_delta",
|
|
1051
|
+
index: 0,
|
|
1052
|
+
delta: {
|
|
1053
|
+
type: "text_delta",
|
|
1054
|
+
text: data.message.content
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
if (data.message?.tool_calls && data.message?.tool_calls?.length > 0) {
|
|
1059
|
+
if (data.message.tool_calls.length > 1) {
|
|
1060
|
+
log4.warn("Ollama returned multiple tool calls. Only the first one will be used.", void 0, {
|
|
1061
|
+
F: __dxlog_file4,
|
|
1062
|
+
L: 273,
|
|
1063
|
+
S: this,
|
|
1064
|
+
C: (f, a) => f(...a)
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
yield {
|
|
1068
|
+
type: "content_block_stop",
|
|
1069
|
+
index: currentBlockIndex++
|
|
1070
|
+
};
|
|
1071
|
+
yield {
|
|
1072
|
+
type: "content_block_start",
|
|
1073
|
+
index: currentBlockIndex,
|
|
1074
|
+
content: {
|
|
1075
|
+
type: "tool_use",
|
|
1076
|
+
id: ObjectId3.random(),
|
|
1077
|
+
name: data.message.tool_calls[0].function.name,
|
|
1078
|
+
input: sanitizeToolArguments(data.message.tool_calls[0].function.arguments)
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
yield {
|
|
1082
|
+
type: "message_delta",
|
|
1083
|
+
delta: {
|
|
1084
|
+
stopReason: "tool_use"
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
yield {
|
|
1090
|
+
type: "content_block_stop",
|
|
1091
|
+
index: currentBlockIndex
|
|
1092
|
+
};
|
|
1093
|
+
yield {
|
|
1094
|
+
type: "message_stop"
|
|
1095
|
+
};
|
|
1096
|
+
} finally {
|
|
1097
|
+
reader.releaseLock();
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
async exec(request) {
|
|
1101
|
+
const controller = new AbortController();
|
|
1102
|
+
try {
|
|
1103
|
+
return new GenerationStreamImpl(controller, async function* () {
|
|
1104
|
+
const collector = new MessageCollector();
|
|
1105
|
+
let toolInvocations = 0;
|
|
1106
|
+
while (true) {
|
|
1107
|
+
for await (const event of this._generateStream({
|
|
1108
|
+
...request,
|
|
1109
|
+
history: [
|
|
1110
|
+
...request.history ?? [],
|
|
1111
|
+
...request.prompt ? [
|
|
1112
|
+
request.prompt
|
|
1113
|
+
] : [],
|
|
1114
|
+
...collector.messages
|
|
1115
|
+
],
|
|
1116
|
+
prompt: void 0
|
|
1117
|
+
}, controller.signal)) {
|
|
1118
|
+
collector.push(event);
|
|
1119
|
+
yield event;
|
|
1120
|
+
}
|
|
1121
|
+
if (collector.messages.length > 0 && !isToolUse(collector.messages.at(-1), {
|
|
1122
|
+
onlyToolNames: this._getEmbededTools(request.tools ?? []).map((tool) => tool.name)
|
|
1123
|
+
})) {
|
|
1124
|
+
break;
|
|
1125
|
+
}
|
|
1126
|
+
if (++toolInvocations > this._maxToolInvocations) {
|
|
1127
|
+
throw new Error("Maximum number of tool invocations reached");
|
|
1128
|
+
}
|
|
1129
|
+
const result = await runTools({
|
|
1130
|
+
message: collector.messages.at(-1),
|
|
1131
|
+
tools: this._getEmbededTools(request.tools ?? [])
|
|
1132
|
+
});
|
|
1133
|
+
switch (result.type) {
|
|
1134
|
+
case "continue": {
|
|
1135
|
+
collector.pushMessage(result.message);
|
|
1136
|
+
yield* emitMessageAsEvents(result.message);
|
|
1137
|
+
break;
|
|
1138
|
+
}
|
|
1139
|
+
case "break": {
|
|
1140
|
+
throw new Error("Tool use breaks not allowed");
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}.bind(this));
|
|
1145
|
+
} catch (error) {
|
|
1146
|
+
controller.abort();
|
|
1147
|
+
throw error;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
var sanitizeToolArguments = (args) => {
|
|
1152
|
+
if (args.type === "object" && typeof args.properties === "object" && Array.isArray(args.required)) {
|
|
1153
|
+
return args.properties;
|
|
1154
|
+
}
|
|
1155
|
+
return args;
|
|
1156
|
+
};
|
|
1157
|
+
var SAMPLE_IMAGE_URL = "https://images.nightcafe.studio/jobs/BNmcRhHCM1JRKoUtqSei/BNmcRhHCM1JRKoUtqSei--1--5b9rv.jpg";
|
|
1158
|
+
var WELL_KNOWN_TOOLS = {
|
|
1159
|
+
[ToolTypes.TextToImage]: defineTool("system", {
|
|
1160
|
+
name: "text-to-image",
|
|
1161
|
+
description: "Generate an image from a text prompt",
|
|
1162
|
+
schema: S3.Struct({
|
|
1163
|
+
prompt: S3.String.annotations({
|
|
1164
|
+
description: "The text prompt describing the image to generate"
|
|
1165
|
+
})
|
|
1166
|
+
}),
|
|
1167
|
+
execute: async ({ prompt }) => {
|
|
1168
|
+
const image = await fetch(SAMPLE_IMAGE_URL).then(async (res) => Buffer.from(await res.arrayBuffer()));
|
|
1169
|
+
const imageBase64 = image.toString("base64");
|
|
1170
|
+
const id = ObjectId3.random();
|
|
1171
|
+
return ToolResult2.Success(`The generation was successful. Image ID: ${id} Prompt: ${prompt}`, [
|
|
1172
|
+
{
|
|
1173
|
+
type: "image",
|
|
1174
|
+
id,
|
|
1175
|
+
source: {
|
|
1176
|
+
type: "base64",
|
|
1177
|
+
mediaType: "image/jpeg",
|
|
1178
|
+
data: imageBase64
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
]);
|
|
1182
|
+
}
|
|
1183
|
+
})
|
|
1184
|
+
};
|
|
1185
|
+
var OllamaDecoderStream = class extends TransformStream {
|
|
1186
|
+
constructor() {
|
|
1187
|
+
super({
|
|
1188
|
+
transform: (chunk, controller) => {
|
|
1189
|
+
this._buffer += chunk;
|
|
1190
|
+
const lines = this._buffer.split("\n");
|
|
1191
|
+
this._buffer = lines.pop() || "";
|
|
1192
|
+
for (const line of lines) {
|
|
1193
|
+
controller.enqueue(JSON.parse(line.trim()));
|
|
1194
|
+
}
|
|
1195
|
+
},
|
|
1196
|
+
flush: (controller) => {
|
|
1197
|
+
this._buffer = this._buffer.trim();
|
|
1198
|
+
if (this._buffer.length > 0) {
|
|
1199
|
+
controller.enqueue(JSON.parse(this._buffer));
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
this._buffer = "";
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
|
|
1207
|
+
// packages/experimental/assistant/src/ai-service/parser.ts
|
|
1208
|
+
import { Message as Message4 } from "@dxos/artifact";
|
|
1209
|
+
import { Event } from "@dxos/async";
|
|
1210
|
+
import { createStatic as createStatic2 } from "@dxos/echo-schema";
|
|
1211
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
1212
|
+
import { log as log5 } from "@dxos/log";
|
|
1213
|
+
import { isNotFalsy, safeParseJson } from "@dxos/util";
|
|
1214
|
+
|
|
1215
|
+
// packages/experimental/assistant/src/ai-service/transform.ts
|
|
1216
|
+
import * as P from "parsimmon";
|
|
1217
|
+
var textChunk = P.regexp(/[^<]+/).map((content) => ({
|
|
1218
|
+
type: "text",
|
|
1219
|
+
content
|
|
1220
|
+
}));
|
|
1221
|
+
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]) => [
|
|
1222
|
+
name,
|
|
1223
|
+
value
|
|
1224
|
+
]);
|
|
1225
|
+
var tagName = P.regexp(/[a-zA-Z_][a-zA-Z0-9_-]*/);
|
|
1226
|
+
var selfClosingTag = P.seqMap(
|
|
1227
|
+
//
|
|
1228
|
+
P.string("<"),
|
|
1229
|
+
tagName,
|
|
1230
|
+
P.optWhitespace.then(attribute).many(),
|
|
1231
|
+
P.optWhitespace.then(P.string("/>")),
|
|
1232
|
+
P.optWhitespace,
|
|
1233
|
+
(_, tag, attributes, __) => ({
|
|
1234
|
+
type: "tag",
|
|
1235
|
+
tag,
|
|
1236
|
+
attributes: Object.fromEntries(attributes),
|
|
1237
|
+
content: [],
|
|
1238
|
+
selfClosing: true
|
|
1239
|
+
})
|
|
1240
|
+
);
|
|
1241
|
+
var openTag = P.seqMap(
|
|
1242
|
+
//
|
|
1243
|
+
P.string("<"),
|
|
1244
|
+
tagName,
|
|
1245
|
+
P.optWhitespace.then(attribute).many(),
|
|
1246
|
+
P.string(">"),
|
|
1247
|
+
P.regexp(/[^<]*/),
|
|
1248
|
+
(_, tag, attributes, __, content) => ({
|
|
1249
|
+
type: "tag",
|
|
1250
|
+
tag,
|
|
1251
|
+
attributes: Object.fromEntries(attributes),
|
|
1252
|
+
content: content.length ? [
|
|
1253
|
+
{
|
|
1254
|
+
type: "text",
|
|
1255
|
+
content
|
|
1256
|
+
}
|
|
1257
|
+
] : []
|
|
1258
|
+
})
|
|
1259
|
+
);
|
|
1260
|
+
var closeTag = P.seqMap(
|
|
1261
|
+
//
|
|
1262
|
+
P.string("</"),
|
|
1263
|
+
tagName,
|
|
1264
|
+
P.string(">"),
|
|
1265
|
+
P.optWhitespace,
|
|
1266
|
+
(_, tag) => ({
|
|
1267
|
+
type: "tag",
|
|
1268
|
+
tag,
|
|
1269
|
+
content: [],
|
|
1270
|
+
closing: true
|
|
1271
|
+
})
|
|
1272
|
+
);
|
|
1273
|
+
var mixedChunk = P.alt(selfClosingTag, openTag, closeTag, textChunk);
|
|
1274
|
+
var StreamTransform = class {
|
|
1275
|
+
constructor() {
|
|
1276
|
+
this._buffer = "";
|
|
1277
|
+
}
|
|
1278
|
+
transform(chunk) {
|
|
1279
|
+
this._buffer += chunk;
|
|
1280
|
+
const results = [];
|
|
1281
|
+
const parser = mixedChunk.many().skip(P.optWhitespace);
|
|
1282
|
+
const result = parser.parse(this._buffer);
|
|
1283
|
+
if (result.status) {
|
|
1284
|
+
for (const chunk2 of result.value) {
|
|
1285
|
+
results.push(chunk2);
|
|
1286
|
+
}
|
|
1287
|
+
this._buffer = "";
|
|
1288
|
+
}
|
|
1289
|
+
return results;
|
|
1290
|
+
}
|
|
1291
|
+
// TODO(burdon): Process remaining buffer?
|
|
1292
|
+
flush() {
|
|
1293
|
+
const remaining = this._buffer;
|
|
1294
|
+
this._buffer = "";
|
|
1295
|
+
return remaining.length > 0 ? [
|
|
1296
|
+
{
|
|
1297
|
+
type: "text",
|
|
1298
|
+
content: remaining
|
|
1299
|
+
}
|
|
1300
|
+
] : [];
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
|
|
1304
|
+
// packages/experimental/assistant/src/ai-service/parser.ts
|
|
1305
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/experimental/assistant/src/ai-service/parser.ts";
|
|
1306
|
+
var MixedStreamParser = class {
|
|
1307
|
+
constructor() {
|
|
1308
|
+
/**
|
|
1309
|
+
* New message.
|
|
1310
|
+
*/
|
|
1311
|
+
this.message = new Event();
|
|
1312
|
+
/**
|
|
1313
|
+
* Complete block added to Message.
|
|
1314
|
+
*/
|
|
1315
|
+
this.block = new Event();
|
|
1316
|
+
/**
|
|
1317
|
+
* Update partial block (while streaming).
|
|
1318
|
+
*/
|
|
1319
|
+
this.update = new Event();
|
|
1320
|
+
this.streamEvent = new Event();
|
|
1321
|
+
}
|
|
1322
|
+
_emitBlock(contentBlock, streamBlock) {
|
|
1323
|
+
const messageBlock = streamBlock ? mergeMessageBlock(contentBlock, streamBlock) : contentBlock;
|
|
1324
|
+
if (messageBlock) {
|
|
1325
|
+
if (messageBlock.type === "text" && messageBlock.text.length === 0) {
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
invariant4(this._message, void 0, {
|
|
1329
|
+
F: __dxlog_file5,
|
|
1330
|
+
L: 49,
|
|
1331
|
+
S: this,
|
|
1332
|
+
A: [
|
|
1333
|
+
"this._message",
|
|
1334
|
+
""
|
|
1335
|
+
]
|
|
1336
|
+
});
|
|
1337
|
+
this._message.content.push(messageBlock);
|
|
1338
|
+
this.block.emit(messageBlock);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
_emitUpdate(contentBlock, streamBlock) {
|
|
1342
|
+
const messageBlock = mergeMessageBlock(contentBlock, streamBlock);
|
|
1343
|
+
if (messageBlock) {
|
|
1344
|
+
messageBlock.pending = true;
|
|
1345
|
+
this.update.emit(messageBlock);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* Parse stream until end.
|
|
1350
|
+
*/
|
|
1351
|
+
async parse(stream) {
|
|
1352
|
+
const transformer = new StreamTransform();
|
|
1353
|
+
let contentBlock;
|
|
1354
|
+
let streamBlock;
|
|
1355
|
+
const stack = [];
|
|
1356
|
+
const messagesCollected = [];
|
|
1357
|
+
for await (const event of stream) {
|
|
1358
|
+
this.streamEvent.emit(event);
|
|
1359
|
+
switch (event.type) {
|
|
1360
|
+
//
|
|
1361
|
+
// Messages.
|
|
1362
|
+
//
|
|
1363
|
+
case "message_start": {
|
|
1364
|
+
if (this._message) {
|
|
1365
|
+
log5.warn("unexpected message_start", void 0, {
|
|
1366
|
+
F: __dxlog_file5,
|
|
1367
|
+
L: 93,
|
|
1368
|
+
S: this,
|
|
1369
|
+
C: (f, a) => f(...a)
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
this._message = createStatic2(Message4, {
|
|
1373
|
+
role: event.message.role,
|
|
1374
|
+
content: [
|
|
1375
|
+
...event.message.content
|
|
1376
|
+
]
|
|
1377
|
+
});
|
|
1378
|
+
this.message.emit(this._message);
|
|
1379
|
+
break;
|
|
1380
|
+
}
|
|
1381
|
+
case "message_delta": {
|
|
1382
|
+
if (!this._message) {
|
|
1383
|
+
log5.warn("unexpected message_delta", void 0, {
|
|
1384
|
+
F: __dxlog_file5,
|
|
1385
|
+
L: 103,
|
|
1386
|
+
S: this,
|
|
1387
|
+
C: (f, a) => f(...a)
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
break;
|
|
1391
|
+
}
|
|
1392
|
+
case "message_stop": {
|
|
1393
|
+
if (!this._message) {
|
|
1394
|
+
log5.warn("unexpected message_stop", void 0, {
|
|
1395
|
+
F: __dxlog_file5,
|
|
1396
|
+
L: 111,
|
|
1397
|
+
S: this,
|
|
1398
|
+
C: (f, a) => f(...a)
|
|
1399
|
+
});
|
|
1400
|
+
} else {
|
|
1401
|
+
messagesCollected.push(this._message);
|
|
1402
|
+
this._message = void 0;
|
|
1403
|
+
}
|
|
1404
|
+
break;
|
|
1405
|
+
}
|
|
1406
|
+
//
|
|
1407
|
+
// Content blocks.
|
|
1408
|
+
//
|
|
1409
|
+
case "content_block_start": {
|
|
1410
|
+
if (contentBlock) {
|
|
1411
|
+
log5.warn("unexpected content_block_start", {
|
|
1412
|
+
content: contentBlock
|
|
1413
|
+
}, {
|
|
1414
|
+
F: __dxlog_file5,
|
|
1415
|
+
L: 125,
|
|
1416
|
+
S: this,
|
|
1417
|
+
C: (f, a) => f(...a)
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
contentBlock = event.content;
|
|
1421
|
+
break;
|
|
1422
|
+
}
|
|
1423
|
+
case "content_block_delta": {
|
|
1424
|
+
if (!contentBlock) {
|
|
1425
|
+
log5.warn("unexpected content_block_delta", void 0, {
|
|
1426
|
+
F: __dxlog_file5,
|
|
1427
|
+
L: 135,
|
|
1428
|
+
S: this,
|
|
1429
|
+
C: (f, a) => f(...a)
|
|
1430
|
+
});
|
|
1431
|
+
break;
|
|
1432
|
+
}
|
|
1433
|
+
switch (event.delta.type) {
|
|
1434
|
+
//
|
|
1435
|
+
// Text
|
|
1436
|
+
//
|
|
1437
|
+
case "text_delta": {
|
|
1438
|
+
const chunks = transformer.transform(event.delta.text);
|
|
1439
|
+
for (const chunk of chunks) {
|
|
1440
|
+
switch (streamBlock?.type) {
|
|
1441
|
+
//
|
|
1442
|
+
// XML Fragment.
|
|
1443
|
+
//
|
|
1444
|
+
case "tag": {
|
|
1445
|
+
if (chunk.type === "tag") {
|
|
1446
|
+
if (chunk.selfClosing) {
|
|
1447
|
+
streamBlock.content.push(chunk);
|
|
1448
|
+
} else if (chunk.closing) {
|
|
1449
|
+
if (stack.length > 0) {
|
|
1450
|
+
const top = stack.pop();
|
|
1451
|
+
invariant4(top && top.type === "tag", void 0, {
|
|
1452
|
+
F: __dxlog_file5,
|
|
1453
|
+
L: 159,
|
|
1454
|
+
S: this,
|
|
1455
|
+
A: [
|
|
1456
|
+
"top && top.type === 'tag'",
|
|
1457
|
+
""
|
|
1458
|
+
]
|
|
1459
|
+
});
|
|
1460
|
+
top.content.push(streamBlock);
|
|
1461
|
+
streamBlock = top;
|
|
1462
|
+
} else {
|
|
1463
|
+
this._emitBlock(contentBlock, streamBlock);
|
|
1464
|
+
streamBlock = void 0;
|
|
1465
|
+
}
|
|
1466
|
+
} else {
|
|
1467
|
+
stack.push(streamBlock);
|
|
1468
|
+
streamBlock = chunk;
|
|
1469
|
+
}
|
|
1470
|
+
} else {
|
|
1471
|
+
if (streamBlock.content.length === 0) {
|
|
1472
|
+
streamBlock.content.push(chunk);
|
|
1473
|
+
} else {
|
|
1474
|
+
const last = streamBlock.content.at(-1);
|
|
1475
|
+
invariant4(last, void 0, {
|
|
1476
|
+
F: __dxlog_file5,
|
|
1477
|
+
L: 176,
|
|
1478
|
+
S: this,
|
|
1479
|
+
A: [
|
|
1480
|
+
"last",
|
|
1481
|
+
""
|
|
1482
|
+
]
|
|
1483
|
+
});
|
|
1484
|
+
if (last.type === "text") {
|
|
1485
|
+
last.content += chunk.content;
|
|
1486
|
+
} else {
|
|
1487
|
+
streamBlock.content.push(chunk);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
break;
|
|
1492
|
+
}
|
|
1493
|
+
//
|
|
1494
|
+
// Text Fragment.
|
|
1495
|
+
//
|
|
1496
|
+
case "text": {
|
|
1497
|
+
if (chunk.type === "tag") {
|
|
1498
|
+
this._emitBlock(contentBlock, streamBlock);
|
|
1499
|
+
if (chunk.selfClosing) {
|
|
1500
|
+
this._emitBlock(contentBlock, chunk);
|
|
1501
|
+
streamBlock = void 0;
|
|
1502
|
+
} else {
|
|
1503
|
+
streamBlock = chunk;
|
|
1504
|
+
}
|
|
1505
|
+
} else {
|
|
1506
|
+
streamBlock.content += chunk.content;
|
|
1507
|
+
}
|
|
1508
|
+
break;
|
|
1509
|
+
}
|
|
1510
|
+
//
|
|
1511
|
+
// No current chunk.
|
|
1512
|
+
//
|
|
1513
|
+
default: {
|
|
1514
|
+
if (chunk.type === "tag" && chunk.selfClosing) {
|
|
1515
|
+
this._emitBlock(contentBlock, chunk);
|
|
1516
|
+
} else {
|
|
1517
|
+
streamBlock = chunk;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
break;
|
|
1523
|
+
}
|
|
1524
|
+
//
|
|
1525
|
+
// JSON
|
|
1526
|
+
//
|
|
1527
|
+
case "input_json_delta": {
|
|
1528
|
+
if (!contentBlock) {
|
|
1529
|
+
log5.warn("unexpected input_json_delta", {
|
|
1530
|
+
contentBlock
|
|
1531
|
+
}, {
|
|
1532
|
+
F: __dxlog_file5,
|
|
1533
|
+
L: 226,
|
|
1534
|
+
S: this,
|
|
1535
|
+
C: (f, a) => f(...a)
|
|
1536
|
+
});
|
|
1537
|
+
break;
|
|
1538
|
+
}
|
|
1539
|
+
if (streamBlock?.type === "json") {
|
|
1540
|
+
streamBlock.content += event.delta.partial_json;
|
|
1541
|
+
} else {
|
|
1542
|
+
streamBlock = {
|
|
1543
|
+
type: "json",
|
|
1544
|
+
content: event.delta.partial_json
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
break;
|
|
1551
|
+
}
|
|
1552
|
+
case "content_block_stop": {
|
|
1553
|
+
if (!contentBlock) {
|
|
1554
|
+
log5.warn("unexpected content_block_stop", void 0, {
|
|
1555
|
+
F: __dxlog_file5,
|
|
1556
|
+
L: 243,
|
|
1557
|
+
S: this,
|
|
1558
|
+
C: (f, a) => f(...a)
|
|
1559
|
+
});
|
|
1560
|
+
break;
|
|
1561
|
+
}
|
|
1562
|
+
this._emitBlock(contentBlock, streamBlock);
|
|
1563
|
+
contentBlock = void 0;
|
|
1564
|
+
streamBlock = void 0;
|
|
1565
|
+
break;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
if (contentBlock && streamBlock) {
|
|
1569
|
+
this._emitUpdate(contentBlock, streamBlock);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
return messagesCollected;
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
var mergeMessageBlock = (contentBlock, streamBlock) => {
|
|
1576
|
+
log5("mergeMessageBlock", {
|
|
1577
|
+
contentBlock,
|
|
1578
|
+
streamBlock
|
|
1579
|
+
}, {
|
|
1580
|
+
F: __dxlog_file5,
|
|
1581
|
+
L: 270,
|
|
1582
|
+
S: void 0,
|
|
1583
|
+
C: (f, a) => f(...a)
|
|
1584
|
+
});
|
|
1585
|
+
switch (streamBlock.type) {
|
|
1586
|
+
//
|
|
1587
|
+
// Text
|
|
1588
|
+
//
|
|
1589
|
+
case "text": {
|
|
1590
|
+
return {
|
|
1591
|
+
...contentBlock,
|
|
1592
|
+
type: "text",
|
|
1593
|
+
text: streamBlock.content
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
//
|
|
1597
|
+
// XML
|
|
1598
|
+
//
|
|
1599
|
+
case "tag": {
|
|
1600
|
+
switch (streamBlock.tag) {
|
|
1601
|
+
case "cot": {
|
|
1602
|
+
const content = streamBlock.content.map((block) => {
|
|
1603
|
+
switch (block.type) {
|
|
1604
|
+
case "text":
|
|
1605
|
+
return block.content;
|
|
1606
|
+
default:
|
|
1607
|
+
return null;
|
|
1608
|
+
}
|
|
1609
|
+
}).filter(isNotFalsy).join("\n");
|
|
1610
|
+
return {
|
|
1611
|
+
...contentBlock,
|
|
1612
|
+
type: "text",
|
|
1613
|
+
disposition: "cot",
|
|
1614
|
+
text: content
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
case "artifact": {
|
|
1618
|
+
const { attributes } = streamBlock;
|
|
1619
|
+
return {
|
|
1620
|
+
type: "json",
|
|
1621
|
+
disposition: "artifact",
|
|
1622
|
+
json: JSON.stringify(attributes)
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
case "suggest": {
|
|
1626
|
+
if (streamBlock.content.length === 1 && streamBlock.content[0].type === "text") {
|
|
1627
|
+
return {
|
|
1628
|
+
type: "json",
|
|
1629
|
+
disposition: "suggest",
|
|
1630
|
+
json: JSON.stringify({
|
|
1631
|
+
text: streamBlock.content[0].content
|
|
1632
|
+
})
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
break;
|
|
1636
|
+
}
|
|
1637
|
+
case "select": {
|
|
1638
|
+
return {
|
|
1639
|
+
type: "json",
|
|
1640
|
+
disposition: "select",
|
|
1641
|
+
json: JSON.stringify({
|
|
1642
|
+
options: streamBlock.content.flatMap((content) => content.type === "tag" && content.content.length === 1 && content.content[0].type === "text" ? [
|
|
1643
|
+
content.content[0].content
|
|
1644
|
+
] : [])
|
|
1645
|
+
})
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
case "tool-list": {
|
|
1649
|
+
return {
|
|
1650
|
+
type: "json",
|
|
1651
|
+
disposition: "tool_list",
|
|
1652
|
+
json: JSON.stringify({})
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
break;
|
|
1657
|
+
}
|
|
1658
|
+
//
|
|
1659
|
+
// JSON
|
|
1660
|
+
//
|
|
1661
|
+
case "json": {
|
|
1662
|
+
switch (contentBlock.type) {
|
|
1663
|
+
case "tool_use": {
|
|
1664
|
+
return {
|
|
1665
|
+
...contentBlock,
|
|
1666
|
+
input: safeParseJson(streamBlock.content) ?? {}
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
return {
|
|
1671
|
+
...contentBlock,
|
|
1672
|
+
type: "json",
|
|
1673
|
+
disposition: "artifact",
|
|
1674
|
+
json: streamBlock.content
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1242
1679
|
export {
|
|
1243
|
-
|
|
1680
|
+
AIServiceEdgeClient,
|
|
1244
1681
|
DEFAULT_LLM_MODEL,
|
|
1245
1682
|
DEFAULT_LLM_MODELS,
|
|
1246
1683
|
EchoDataSource,
|
|
1247
1684
|
GenerateRequest,
|
|
1248
|
-
|
|
1685
|
+
GenerationStreamImpl,
|
|
1249
1686
|
LLMModel,
|
|
1687
|
+
MessageCollector,
|
|
1250
1688
|
MixedStreamParser,
|
|
1689
|
+
OllamaClient,
|
|
1251
1690
|
StreamTransform,
|
|
1252
1691
|
ToolTypes,
|
|
1253
1692
|
createArtifactElement,
|
|
1254
1693
|
createGenerationStream,
|
|
1694
|
+
emitMessageAsEvents,
|
|
1255
1695
|
executeQuery,
|
|
1256
1696
|
executeQueryPlan,
|
|
1257
1697
|
formatInferredRelationshipLabel,
|