@dxos/assistant 0.8.2-main.fbd8ed0 → 0.8.2-staging.7ac8446
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/{chunk-B4HAWUFW.mjs → chunk-PND5VR6D.mjs} +286 -757
- package/dist/lib/browser/chunk-PND5VR6D.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +324 -7
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +102 -139
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node/{chunk-NOB7CIIC.cjs → chunk-IBOBRBDF.cjs} +303 -768
- package/dist/lib/node/chunk-IBOBRBDF.cjs.map +7 -0
- package/dist/lib/node/index.cjs +353 -36
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +93 -128
- package/dist/lib/node/testing/index.cjs.map +4 -4
- package/dist/lib/node-esm/{chunk-SAWYU3V5.mjs → chunk-DCQJOLSR.mjs} +286 -757
- package/dist/lib/node-esm/chunk-DCQJOLSR.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +324 -7
- 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 +102 -139
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/ai-service/edge-client.d.ts +5 -4
- package/dist/types/src/ai-service/edge-client.d.ts.map +1 -1
- package/dist/types/src/ai-service/message-collector.d.ts +6 -0
- package/dist/types/src/ai-service/message-collector.d.ts.map +1 -1
- package/dist/types/src/ai-service/ollama-client.d.ts +2 -6
- package/dist/types/src/ai-service/ollama-client.d.ts.map +1 -1
- package/dist/types/src/ai-service/parser.d.ts +3 -0
- package/dist/types/src/ai-service/parser.d.ts.map +1 -1
- package/dist/types/src/ai-service/service.d.ts +2 -18
- package/dist/types/src/ai-service/service.d.ts.map +1 -1
- package/dist/types/src/ai-service/stream.d.ts.map +1 -1
- package/dist/types/src/ai-service/testing/mock.d.ts +2 -6
- package/dist/types/src/ai-service/testing/mock.d.ts.map +1 -1
- package/dist/types/src/ai-service/testing/test-stream.d.ts.map +1 -1
- package/dist/types/src/ai-service/types.d.ts +101 -144
- package/dist/types/src/ai-service/types.d.ts.map +1 -1
- package/dist/types/src/ai-service/util/sse.d.ts.map +1 -1
- package/dist/types/src/{experimental/conversation → conversation}/conversation.d.ts +21 -2
- package/dist/types/src/conversation/conversation.d.ts.map +1 -0
- package/dist/types/src/conversation/conversation.test.d.ts.map +1 -0
- package/dist/types/src/conversation/index.d.ts.map +1 -0
- package/dist/types/src/{experimental/conversation → conversation}/types.d.ts +12 -11
- package/dist/types/src/conversation/types.d.ts.map +1 -0
- package/dist/types/src/cypher/ast.d.ts.map +1 -0
- package/dist/types/src/cypher/combinators.d.ts.map +1 -0
- package/dist/types/src/cypher/combinators.test.d.ts.map +1 -0
- package/dist/types/src/cypher/echo-data-source.d.ts.map +1 -0
- package/dist/types/src/cypher/execution-plan.d.ts.map +1 -0
- package/dist/types/src/cypher/execution-plan.test.d.ts.map +1 -0
- package/dist/types/src/cypher/index.d.ts.map +1 -0
- package/dist/types/src/cypher/parjs.d.ts.map +1 -0
- package/dist/types/src/cypher/parser.d.ts.map +1 -0
- package/dist/types/src/cypher/query-executor.d.ts.map +1 -0
- package/dist/types/src/cypher/query-executor.test.d.ts.map +1 -0
- package/dist/types/src/cypher/schema.d.ts.map +1 -0
- package/dist/types/src/cypher/schema.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/session/index.d.ts +0 -2
- package/dist/types/src/session/index.d.ts.map +1 -1
- package/dist/types/src/session/session.d.ts +13 -33
- package/dist/types/src/session/session.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +0 -1
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/logger.d.ts +1 -1
- package/dist/types/src/testing/logger.d.ts.map +1 -1
- package/dist/types/src/testing/ollama.d.ts.map +1 -1
- package/dist/types/src/testing/query-promts.d.ts +4 -4
- package/dist/types/src/testing/query-promts.d.ts.map +1 -1
- package/dist/types/src/testing/test-data.d.ts +5 -5
- package/dist/types/src/testing/test-data.d.ts.map +1 -1
- package/dist/types/src/testing/test-schema.d.ts +23 -25
- package/dist/types/src/testing/test-schema.d.ts.map +1 -1
- package/dist/types/src/util/trim.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -20
- package/src/ai-service/client.test.ts +73 -55
- package/src/ai-service/edge-client.ts +20 -13
- package/src/ai-service/ollama-client.ts +10 -17
- package/src/ai-service/parser.ts +2 -13
- package/src/ai-service/service.ts +3 -19
- package/src/ai-service/testing/mock.ts +1 -9
- package/src/ai-service/types.ts +15 -24
- package/src/bin/chat.ts +4 -4
- package/src/{experimental/conversation → conversation}/conversation.test.ts +5 -5
- package/src/{experimental/conversation → conversation}/conversation.ts +119 -4
- package/src/{experimental/conversation → conversation}/types.ts +14 -11
- package/src/{experimental/cypher → cypher}/combinators.test.ts +4 -4
- package/src/{experimental/cypher → cypher}/echo-data-source.ts +11 -11
- package/src/{experimental/cypher → cypher}/execution-plan.test.ts +1 -1
- package/src/{experimental/cypher → cypher}/query-executor.test.ts +2 -2
- package/src/{experimental/cypher → cypher}/schema.test.ts +3 -3
- package/src/{experimental/cypher → cypher}/schema.ts +1 -1
- package/src/index.ts +2 -3
- package/src/session/index.ts +0 -2
- package/src/session/session.test.ts +63 -35
- package/src/session/session.ts +151 -297
- package/src/testing/artifacts.test.ts +22 -22
- package/src/testing/index.ts +0 -1
- package/src/testing/logger.ts +1 -1
- package/src/testing/query-promts.ts +6 -6
- package/src/testing/query.test.ts +21 -18
- package/src/testing/test-data.ts +72 -79
- package/src/testing/test-schema.ts +29 -28
- package/dist/lib/browser/chunk-B4HAWUFW.mjs.map +0 -7
- package/dist/lib/node/chunk-NOB7CIIC.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-SAWYU3V5.mjs.map +0 -7
- package/dist/types/src/experimental/conversation/conversation.d.ts.map +0 -1
- package/dist/types/src/experimental/conversation/conversation.test.d.ts.map +0 -1
- package/dist/types/src/experimental/conversation/index.d.ts.map +0 -1
- package/dist/types/src/experimental/conversation/types.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/ast.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/combinators.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/combinators.test.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/echo-data-source.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/execution-plan.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/execution-plan.test.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/index.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/parjs.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/parser.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/query-executor.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/query-executor.test.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/schema.d.ts.map +0 -1
- package/dist/types/src/experimental/cypher/schema.test.d.ts.map +0 -1
- package/dist/types/src/experimental/index.d.ts +0 -3
- package/dist/types/src/experimental/index.d.ts.map +0 -1
- package/dist/types/src/experimental/ner/ner.test.d.ts +0 -2
- package/dist/types/src/experimental/ner/ner.test.d.ts.map +0 -1
- package/dist/types/src/experimental/research/exa.d.ts +0 -6
- package/dist/types/src/experimental/research/exa.d.ts.map +0 -1
- package/dist/types/src/experimental/research/research.test.d.ts +0 -2
- package/dist/types/src/experimental/research/research.test.d.ts.map +0 -1
- package/dist/types/src/session/tools.d.ts +0 -18
- package/dist/types/src/session/tools.d.ts.map +0 -1
- package/dist/types/src/session/version-pin.d.ts +0 -22
- package/dist/types/src/session/version-pin.d.ts.map +0 -1
- package/dist/types/src/session/version-pin.test.d.ts +0 -2
- package/dist/types/src/session/version-pin.test.d.ts.map +0 -1
- package/dist/types/src/testing/printer.d.ts +0 -6
- package/dist/types/src/testing/printer.d.ts.map +0 -1
- package/src/experimental/index.ts +0 -6
- package/src/experimental/ner/ner.test.ts +0 -163
- package/src/experimental/research/exa.ts +0 -38
- package/src/experimental/research/instructions.tpl +0 -64
- package/src/experimental/research/research.test.ts +0 -263
- package/src/session/tools.ts +0 -117
- package/src/session/version-pin.test.ts +0 -26
- package/src/session/version-pin.ts +0 -49
- package/src/testing/printer.ts +0 -41
- package/src/typedefs.d.ts +0 -8
- /package/dist/types/src/{experimental/conversation → conversation}/conversation.test.d.ts +0 -0
- /package/dist/types/src/{experimental/conversation → conversation}/index.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/ast.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/combinators.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/combinators.test.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/echo-data-source.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/execution-plan.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/execution-plan.test.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/index.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/parjs.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/parser.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/query-executor.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/query-executor.test.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/schema.d.ts +0 -0
- /package/dist/types/src/{experimental/cypher → cypher}/schema.test.d.ts +0 -0
- /package/src/{experimental/conversation → conversation}/index.ts +0 -0
- /package/src/{experimental/cypher → cypher}/ast.ts +0 -0
- /package/src/{experimental/cypher → cypher}/combinators.ts +0 -0
- /package/src/{experimental/cypher → cypher}/execution-plan.ts +0 -0
- /package/src/{experimental/cypher → cypher}/index.ts +0 -0
- /package/src/{experimental/cypher → cypher}/parjs-shims.d.ts +0 -0
- /package/src/{experimental/cypher → cypher}/parjs.ts +0 -0
- /package/src/{experimental/cypher → cypher}/parser.ts +0 -0
- /package/src/{experimental/cypher → cypher}/query-executor.ts +0 -0
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Schema as S } from 'effect';
|
|
6
|
+
|
|
7
|
+
import { Message } from '@dxos/artifact';
|
|
5
8
|
import { assertArgument, invariant } from '@dxos/invariant';
|
|
6
9
|
import { log } from '@dxos/log';
|
|
7
10
|
|
|
8
11
|
import type { AIServiceClient, GenerationStream } from './service';
|
|
9
12
|
import { createGenerationStream } from './stream';
|
|
10
|
-
import { type GenerateRequest
|
|
13
|
+
import { type GenerateRequest } from './types';
|
|
11
14
|
|
|
12
15
|
export type AIServiceEdgeClientOptions = {
|
|
13
16
|
endpoint: string;
|
|
@@ -22,16 +25,28 @@ export class AIServiceEdgeClient implements AIServiceClient {
|
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
|
-
*
|
|
28
|
+
* @deprecated
|
|
26
29
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
// TODO(burdon): Remove.
|
|
31
|
+
async appendMessages(messages: Message[]): Promise<void> {
|
|
32
|
+
const url = `${this._endpoint}/message`;
|
|
33
|
+
const res = await fetch(url, {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: { 'Content-Type': 'application/json' },
|
|
36
|
+
body: JSON.stringify({
|
|
37
|
+
messages: await S.encodePromise(S.Array(Message))(messages),
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
throw new Error(`${await res.text()} [${url}]`);
|
|
43
|
+
}
|
|
29
44
|
}
|
|
30
45
|
|
|
31
46
|
/**
|
|
32
47
|
* Process request and open message stream.
|
|
33
48
|
*/
|
|
34
|
-
async
|
|
49
|
+
async exec(request: GenerateRequest): Promise<GenerationStream> {
|
|
35
50
|
assertArgument(typeof request.model === 'string', 'model is required');
|
|
36
51
|
log('requesting', { endpoint: this._endpoint });
|
|
37
52
|
const controller = new AbortController();
|
|
@@ -46,15 +61,7 @@ export class AIServiceEdgeClient implements AIServiceClient {
|
|
|
46
61
|
}),
|
|
47
62
|
});
|
|
48
63
|
|
|
49
|
-
if (!response.ok) {
|
|
50
|
-
throw await parseErrorResponse(response);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
64
|
invariant(response.body instanceof ReadableStream);
|
|
54
65
|
return createGenerationStream(response, controller);
|
|
55
66
|
}
|
|
56
67
|
}
|
|
57
|
-
|
|
58
|
-
const parseErrorResponse = async (response: Response): Promise<Error> => {
|
|
59
|
-
throw new Error(`AI Service error: ${response.status} ${response.statusText} ${await response.text()}`);
|
|
60
|
-
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Schema } from 'effect';
|
|
5
|
+
import { Schema as S } from 'effect';
|
|
6
6
|
|
|
7
7
|
import { type MessageContentBlock, defineTool, type Tool, ToolResult } from '@dxos/artifact';
|
|
8
8
|
import { ObjectId } from '@dxos/echo-schema';
|
|
@@ -13,8 +13,8 @@ import { DEFAULT_OLLAMA_ENDPOINT } from './defs';
|
|
|
13
13
|
import { MessageCollector, emitMessageAsEvents } from './message-collector';
|
|
14
14
|
import { type AIServiceClient, type GenerationStream } from './service';
|
|
15
15
|
import { GenerationStreamImpl } from './stream';
|
|
16
|
-
import { ToolTypes, type GenerateRequest, type
|
|
17
|
-
import { isToolUse, runTools } from '../
|
|
16
|
+
import { ToolTypes, type GenerateRequest, type GenerationStreamEvent } from './types';
|
|
17
|
+
import { isToolUse, runTools } from '../conversation';
|
|
18
18
|
|
|
19
19
|
export type OllamaClientParams = {
|
|
20
20
|
endpoint?: string;
|
|
@@ -300,14 +300,7 @@ export class OllamaClient implements AIServiceClient {
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
|
|
304
|
-
* Generate non-streaming response.
|
|
305
|
-
*/
|
|
306
|
-
async exec(request: GenerateRequest): Promise<GenerateResponse> {
|
|
307
|
-
throw new Error('Not implemented');
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
async execStream(request: GenerateRequest): Promise<GenerationStream> {
|
|
303
|
+
async exec(request: GenerateRequest): Promise<GenerationStream> {
|
|
311
304
|
const controller = new AbortController();
|
|
312
305
|
|
|
313
306
|
try {
|
|
@@ -388,8 +381,8 @@ const WELL_KNOWN_TOOLS: Record<string, Tool> = {
|
|
|
388
381
|
[ToolTypes.TextToImage]: defineTool('system', {
|
|
389
382
|
name: 'text-to-image',
|
|
390
383
|
description: 'Generate an image from a text prompt',
|
|
391
|
-
schema:
|
|
392
|
-
prompt:
|
|
384
|
+
schema: S.Struct({
|
|
385
|
+
prompt: S.String.annotations({ description: 'The text prompt describing the image to generate' }),
|
|
393
386
|
}),
|
|
394
387
|
execute: async ({ prompt }) => {
|
|
395
388
|
const image = await fetch(SAMPLE_IMAGE_URL).then(async (res) => Buffer.from(await res.arrayBuffer()));
|
|
@@ -467,15 +460,15 @@ class OllamaDecoderStream extends TransformStream<string, OllamaResponseData> {
|
|
|
467
460
|
}
|
|
468
461
|
}
|
|
469
462
|
|
|
470
|
-
class ModelDoesNotSupportToolsError extends
|
|
463
|
+
class ModelDoesNotSupportToolsError extends S.TaggedError<ModelDoesNotSupportToolsError>()(
|
|
471
464
|
'ModelDoesNotSupportToolsError',
|
|
472
465
|
{
|
|
473
|
-
model:
|
|
466
|
+
model: S.String,
|
|
474
467
|
},
|
|
475
468
|
) {}
|
|
476
469
|
|
|
477
|
-
class ModelNotFoundError extends
|
|
478
|
-
model:
|
|
470
|
+
class ModelNotFoundError extends S.TaggedError<ModelNotFoundError>()('ModelNotFoundError', {
|
|
471
|
+
model: S.String,
|
|
479
472
|
}) {}
|
|
480
473
|
|
|
481
474
|
const parseOllamaError = (error: string): Error => {
|
package/src/ai-service/parser.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { Message, type MessageContentBlock } from '@dxos/artifact';
|
|
6
6
|
import { Event } from '@dxos/async';
|
|
7
|
-
import {
|
|
7
|
+
import { createStatic } from '@dxos/echo-schema';
|
|
8
8
|
import { invariant } from '@dxos/invariant';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
import { isNotFalsy, safeParseJson } from '@dxos/util';
|
|
@@ -94,7 +94,7 @@ export class MixedStreamParser {
|
|
|
94
94
|
log.warn('unexpected message_start');
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
this._message =
|
|
97
|
+
this._message = createStatic(Message, { role: event.message.role, content: [...event.message.content] });
|
|
98
98
|
this.message.emit(this._message);
|
|
99
99
|
break;
|
|
100
100
|
}
|
|
@@ -316,17 +316,6 @@ export const mergeMessageBlock = (
|
|
|
316
316
|
break;
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
case 'proposal': {
|
|
320
|
-
if (streamBlock.content.length === 1 && streamBlock.content[0].type === 'text') {
|
|
321
|
-
return {
|
|
322
|
-
type: 'json',
|
|
323
|
-
disposition: 'proposal',
|
|
324
|
-
json: JSON.stringify({ text: streamBlock.content[0].content }),
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
break;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
319
|
case 'select': {
|
|
331
320
|
return {
|
|
332
321
|
type: 'json',
|
|
@@ -2,29 +2,13 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type GenerateRequest, type GenerationStreamEvent
|
|
5
|
+
import { type GenerateRequest, type GenerationStreamEvent } from './types';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
* Interface for AI service.
|
|
9
|
-
* Generates chat completions, supports, history and tools.
|
|
10
|
-
*/
|
|
7
|
+
// TODO(burdon): Create wrapper for edge/ollama. Create session?
|
|
11
8
|
export interface AIServiceClient {
|
|
12
|
-
|
|
13
|
-
* Generate chat completions, supports, history and tools.
|
|
14
|
-
* Generate non-streaming response.
|
|
15
|
-
*/
|
|
16
|
-
exec(request: GenerateRequest): Promise<GenerateResponse>;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Generate chat completions, supports, history and tools.
|
|
20
|
-
* Process request and open message stream.
|
|
21
|
-
*/
|
|
22
|
-
execStream(request: GenerateRequest): Promise<GenerationStream>;
|
|
9
|
+
exec(request: GenerateRequest): Promise<GenerationStream>;
|
|
23
10
|
}
|
|
24
11
|
|
|
25
|
-
/**
|
|
26
|
-
* Streaming chat completion stream.
|
|
27
|
-
*/
|
|
28
12
|
export interface GenerationStream extends AsyncIterable<GenerationStreamEvent> {
|
|
29
13
|
abort(): void;
|
|
30
14
|
complete(): Promise<void>;
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
type AIServiceClient,
|
|
8
8
|
type GenerateRequest,
|
|
9
9
|
createGenerationStream,
|
|
10
|
-
type GenerateResponse,
|
|
11
10
|
} from '@dxos/assistant';
|
|
12
11
|
|
|
13
12
|
import { createTestSSEStream } from './test-stream';
|
|
@@ -15,16 +14,9 @@ import { createTestSSEStream } from './test-stream';
|
|
|
15
14
|
export class MockAIService implements AIServiceClient {
|
|
16
15
|
constructor(private readonly _responses: string[]) {}
|
|
17
16
|
|
|
18
|
-
/**
|
|
19
|
-
* Generate non-streaming response.
|
|
20
|
-
*/
|
|
21
|
-
async exec(request: GenerateRequest): Promise<GenerateResponse> {
|
|
22
|
-
throw new Error('Not implemented');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
17
|
// TODO(burdon): Match response to request.
|
|
26
18
|
// TODO(burdon): See MockGpt (from conductor).
|
|
27
|
-
async
|
|
19
|
+
async exec(request: GenerateRequest): Promise<GenerationStream> {
|
|
28
20
|
return createGenerationStream(new Response(createTestSSEStream(this._responses)));
|
|
29
21
|
}
|
|
30
22
|
}
|
package/src/ai-service/types.ts
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Schema } from 'effect';
|
|
5
|
+
import { Schema as S } from 'effect';
|
|
6
6
|
|
|
7
|
-
import { Tool, Message, type MessageContentBlock } from '@dxos/artifact';
|
|
8
|
-
import {
|
|
7
|
+
import { Tool, Message, type MessageContentBlock, SpaceIdSchema } from '@dxos/artifact';
|
|
8
|
+
import { ObjectId } from '@dxos/echo-schema';
|
|
9
9
|
|
|
10
10
|
import { DEFAULT_EDGE_MODELS, DEFAULT_OLLAMA_MODELS } from './defs';
|
|
11
11
|
|
|
12
12
|
export const createArtifactElement = (id: ObjectId) => `<artifact id=${id} />`;
|
|
13
13
|
|
|
14
|
-
export const LLMModel =
|
|
14
|
+
export const LLMModel = S.Literal(...DEFAULT_EDGE_MODELS, ...DEFAULT_OLLAMA_MODELS);
|
|
15
15
|
|
|
16
|
-
export type LLMModel =
|
|
16
|
+
export type LLMModel = S.Schema.Type<typeof LLMModel>;
|
|
17
17
|
|
|
18
18
|
export const ToolTypes = Object.freeze({
|
|
19
19
|
// TODO(dmaretskyi): Not implemented yet.
|
|
@@ -24,47 +24,38 @@ export const ToolTypes = Object.freeze({
|
|
|
24
24
|
/**
|
|
25
25
|
* Client GPT request.
|
|
26
26
|
*/
|
|
27
|
-
export const GenerateRequest =
|
|
27
|
+
export const GenerateRequest = S.Struct({
|
|
28
|
+
spaceId: S.optional(SpaceIdSchema),
|
|
29
|
+
threadId: S.optional(ObjectId),
|
|
30
|
+
|
|
28
31
|
/**
|
|
29
32
|
* Preferred model or system default.
|
|
30
33
|
*/
|
|
31
|
-
model:
|
|
34
|
+
model: S.optional(LLMModel),
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
37
|
* Tools available for the LLM.
|
|
35
38
|
*/
|
|
36
|
-
tools:
|
|
39
|
+
tools: S.optional(S.Array(Tool).pipe(S.mutable)),
|
|
37
40
|
|
|
38
41
|
/**
|
|
39
42
|
* System instructions to the LLM.
|
|
40
43
|
*/
|
|
41
|
-
systemPrompt:
|
|
44
|
+
systemPrompt: S.optional(S.String),
|
|
42
45
|
|
|
43
46
|
/**
|
|
44
47
|
* History of messages to include in the context window.
|
|
45
48
|
*/
|
|
46
49
|
// TODO(burdon): Rename messages.
|
|
47
|
-
history:
|
|
50
|
+
history: S.optional(S.Array(Message)),
|
|
48
51
|
|
|
49
52
|
/**
|
|
50
53
|
* Current request.
|
|
51
54
|
*/
|
|
52
|
-
prompt:
|
|
55
|
+
prompt: S.optional(Message),
|
|
53
56
|
});
|
|
54
|
-
export type GenerateRequest = Schema.Schema.Type<typeof GenerateRequest>;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Non-streaming response.
|
|
58
|
-
*/
|
|
59
|
-
export const GenerateResponse = Schema.Struct({
|
|
60
|
-
messages: Schema.Array(Message),
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
* Number of tokens used in the response.
|
|
64
|
-
*/
|
|
65
|
-
tokenCount: Schema.optional(Schema.Number),
|
|
66
|
-
});
|
|
67
|
-
export type GenerateResponse = Schema.Schema.Type<typeof GenerateResponse>;
|
|
58
|
+
export type GenerateRequest = S.Schema.Type<typeof GenerateRequest>;
|
|
68
59
|
|
|
69
60
|
/**
|
|
70
61
|
* Server-Sent Events (SSE) stream from the AI service.
|
package/src/bin/chat.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { SpaceId } from '@dxos/keys';
|
|
|
11
11
|
import { log } from '@dxos/log';
|
|
12
12
|
|
|
13
13
|
import { AIServiceEdgeClient, ToolTypes, DEFAULT_EDGE_MODEL } from '../ai-service';
|
|
14
|
-
import { runLLM } from '../
|
|
14
|
+
import { runLLM } from '../conversation';
|
|
15
15
|
import {
|
|
16
16
|
AI_SERVICE_ENDPOINT,
|
|
17
17
|
createLogger,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
createSystemPrompt,
|
|
20
20
|
createTestData,
|
|
21
21
|
Contact,
|
|
22
|
-
|
|
22
|
+
Org,
|
|
23
23
|
Project,
|
|
24
24
|
Task,
|
|
25
25
|
} from '../testing';
|
|
@@ -33,7 +33,7 @@ const dataSource = createTestData();
|
|
|
33
33
|
|
|
34
34
|
const cypherTool = createCypherTool(dataSource);
|
|
35
35
|
|
|
36
|
-
const schemaTypes = [
|
|
36
|
+
const schemaTypes = [Org, Project, Task, Contact];
|
|
37
37
|
|
|
38
38
|
const spaceId = SpaceId.random();
|
|
39
39
|
const threadId = ObjectId.random();
|
|
@@ -46,6 +46,7 @@ while (true) {
|
|
|
46
46
|
message: 'Enter a message:',
|
|
47
47
|
},
|
|
48
48
|
]);
|
|
49
|
+
await client.appendMessages([createUserMessage(spaceId, threadId, prompt.message)]);
|
|
49
50
|
|
|
50
51
|
await runLLM({
|
|
51
52
|
model: DEFAULT_EDGE_MODEL,
|
|
@@ -60,7 +61,6 @@ while (true) {
|
|
|
60
61
|
},
|
|
61
62
|
],
|
|
62
63
|
client,
|
|
63
|
-
history: [createUserMessage(spaceId, threadId, prompt.message)],
|
|
64
64
|
logger: createLogger({
|
|
65
65
|
stream: true,
|
|
66
66
|
filter: (e) => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Schema } from 'effect';
|
|
5
|
+
import { Schema as S } from 'effect';
|
|
6
6
|
import { describe, test } from 'vitest';
|
|
7
7
|
|
|
8
8
|
import { createUserMessage, defineTool, ToolResult } from '@dxos/artifact';
|
|
@@ -11,8 +11,8 @@ import { SpaceId } from '@dxos/keys';
|
|
|
11
11
|
import { log } from '@dxos/log';
|
|
12
12
|
|
|
13
13
|
import { runLLM, type ConversationEvent } from './conversation';
|
|
14
|
-
import { AIServiceEdgeClient, DEFAULT_EDGE_MODEL } from '
|
|
15
|
-
import { AI_SERVICE_ENDPOINT } from '
|
|
14
|
+
import { AIServiceEdgeClient, DEFAULT_EDGE_MODEL } from '../ai-service';
|
|
15
|
+
import { AI_SERVICE_ENDPOINT } from '../testing';
|
|
16
16
|
|
|
17
17
|
// TODO(burdon): Local live LLM test.
|
|
18
18
|
describe.skip('Conversation tests', () => {
|
|
@@ -40,8 +40,8 @@ describe.skip('Conversation tests', () => {
|
|
|
40
40
|
const custodian = defineTool('testing', {
|
|
41
41
|
name: 'custodian',
|
|
42
42
|
description: 'Custodian can tell you the password if you say the magic word',
|
|
43
|
-
schema:
|
|
44
|
-
magicWord:
|
|
43
|
+
schema: S.Struct({
|
|
44
|
+
magicWord: S.String.annotations({ description: 'The magic word. Should be exactly "pretty please"' }),
|
|
45
45
|
}),
|
|
46
46
|
execute: async ({ magicWord }) => {
|
|
47
47
|
if (magicWord === 'pretty please') {
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type
|
|
6
|
-
import { ObjectId } from '@dxos/echo-schema';
|
|
5
|
+
import { type Tool, ToolResult, Message } from '@dxos/artifact';
|
|
6
|
+
import { createStatic, ObjectId } from '@dxos/echo-schema';
|
|
7
7
|
import { invariant } from '@dxos/invariant';
|
|
8
8
|
import { type SpaceId } from '@dxos/keys';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
|
|
11
|
-
import { type AIServiceClient, type GenerationStreamEvent, type LLMModel } from '
|
|
11
|
+
import { type AIServiceClient, type GenerationStreamEvent, type LLMModel } from '../ai-service';
|
|
12
12
|
|
|
13
13
|
export type CreateLLMConversationParams = {
|
|
14
14
|
model: LLMModel;
|
|
@@ -43,8 +43,10 @@ export const runLLM = async (params: CreateLLMConversationParams) => {
|
|
|
43
43
|
const generate = async () => {
|
|
44
44
|
log('llm generate', { tools: params.tools });
|
|
45
45
|
const beginTs = Date.now();
|
|
46
|
-
const stream = await params.client.
|
|
46
|
+
const stream = await params.client.exec({
|
|
47
47
|
model: params.model,
|
|
48
|
+
spaceId: params.spaceId,
|
|
49
|
+
threadId: params.threadId,
|
|
48
50
|
history,
|
|
49
51
|
systemPrompt: params.system,
|
|
50
52
|
tools: params.tools as any,
|
|
@@ -86,6 +88,8 @@ export const runLLM = async (params: CreateLLMConversationParams) => {
|
|
|
86
88
|
log.warn('tool error', { message: toolResult.message });
|
|
87
89
|
const resultMessage: Message = {
|
|
88
90
|
id: ObjectId.random(),
|
|
91
|
+
spaceId: params.spaceId,
|
|
92
|
+
threadId: params.threadId,
|
|
89
93
|
role: 'user',
|
|
90
94
|
content: [
|
|
91
95
|
{
|
|
@@ -106,6 +110,8 @@ export const runLLM = async (params: CreateLLMConversationParams) => {
|
|
|
106
110
|
log('tool success', { result: toolResult.result });
|
|
107
111
|
const resultMessage: Message = {
|
|
108
112
|
id: ObjectId.random(),
|
|
113
|
+
spaceId: params.spaceId,
|
|
114
|
+
threadId: params.threadId,
|
|
109
115
|
role: 'user',
|
|
110
116
|
content: [
|
|
111
117
|
{
|
|
@@ -140,3 +146,112 @@ export const runLLM = async (params: CreateLLMConversationParams) => {
|
|
|
140
146
|
history,
|
|
141
147
|
};
|
|
142
148
|
};
|
|
149
|
+
|
|
150
|
+
export const isToolUse = (message: Message, { onlyToolNames }: { onlyToolNames?: string[] } = {}) => {
|
|
151
|
+
const block = message.content.at(-1);
|
|
152
|
+
invariant(block);
|
|
153
|
+
return block.type === 'tool_use' && (!onlyToolNames || onlyToolNames.includes(block.name));
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export type RunToolsOptions = {
|
|
157
|
+
message: Message;
|
|
158
|
+
tools: Tool[];
|
|
159
|
+
extensions?: ToolContextExtensions;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export type RunToolsResult =
|
|
163
|
+
| {
|
|
164
|
+
type: 'continue';
|
|
165
|
+
message: Message;
|
|
166
|
+
}
|
|
167
|
+
| {
|
|
168
|
+
type: 'break';
|
|
169
|
+
result: unknown;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export const runTools = async ({ message, tools, extensions }: RunToolsOptions): Promise<RunToolsResult> => {
|
|
173
|
+
const toolCalls = message.content.filter((block) => block.type === 'tool_use');
|
|
174
|
+
invariant(toolCalls.length === 1);
|
|
175
|
+
const toolCall = toolCalls[0];
|
|
176
|
+
const tool = tools.find((tool) => tool.name === toolCall.name);
|
|
177
|
+
if (!tool) {
|
|
178
|
+
const resultMessage = createStatic(Message, {
|
|
179
|
+
role: 'user',
|
|
180
|
+
content: [
|
|
181
|
+
{
|
|
182
|
+
type: 'tool_result',
|
|
183
|
+
toolUseId: toolCall.id,
|
|
184
|
+
content: `Tool not found: ${toolCall.name}`,
|
|
185
|
+
isError: true,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// TODO(burdon): Retry count?
|
|
191
|
+
return {
|
|
192
|
+
type: 'continue',
|
|
193
|
+
message: resultMessage,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let toolResult: ToolResult;
|
|
198
|
+
try {
|
|
199
|
+
invariant(tool.execute);
|
|
200
|
+
toolResult = await tool.execute(toolCall.input, { extensions });
|
|
201
|
+
} catch (error: any) {
|
|
202
|
+
log('tool error', { error });
|
|
203
|
+
toolResult = ToolResult.Error(error.message);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
switch (toolResult.kind) {
|
|
207
|
+
case 'error': {
|
|
208
|
+
log('tool error', { message: toolResult.message });
|
|
209
|
+
const resultMessage = createStatic(Message, {
|
|
210
|
+
role: 'user',
|
|
211
|
+
content: [
|
|
212
|
+
{
|
|
213
|
+
type: 'tool_result',
|
|
214
|
+
toolUseId: toolCall.id,
|
|
215
|
+
content: toolResult.message,
|
|
216
|
+
isError: true,
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// TODO(burdon): Retry count?
|
|
222
|
+
return {
|
|
223
|
+
type: 'continue',
|
|
224
|
+
message: resultMessage,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
case 'success': {
|
|
229
|
+
log('tool success', { result: toolResult.result });
|
|
230
|
+
const resultMessage = createStatic(Message, {
|
|
231
|
+
role: 'user',
|
|
232
|
+
content: [
|
|
233
|
+
{
|
|
234
|
+
type: 'tool_result',
|
|
235
|
+
toolUseId: toolCall.id,
|
|
236
|
+
content:
|
|
237
|
+
typeof toolResult.result === 'string' ? toolResult.result : JSON.stringify(toolResult.result) ?? '',
|
|
238
|
+
},
|
|
239
|
+
...(toolResult.extractContentBlocks ?? []),
|
|
240
|
+
],
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
type: 'continue',
|
|
245
|
+
message: resultMessage,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
case 'break': {
|
|
250
|
+
log('tool break', { result: toolResult.result });
|
|
251
|
+
return {
|
|
252
|
+
type: 'break',
|
|
253
|
+
result: toolResult.result,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
};
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Schema } from 'effect';
|
|
5
|
+
import { Schema as S } from 'effect';
|
|
6
6
|
|
|
7
7
|
import { type Message, type MessageContentBlock } from '@dxos/artifact';
|
|
8
8
|
import { toJsonSchema, JsonSchemaType, ObjectId } from '@dxos/echo-schema';
|
|
9
|
+
import type { SpaceId } from '@dxos/keys';
|
|
9
10
|
import { log } from '@dxos/log';
|
|
10
11
|
|
|
11
|
-
export const createUserMessage = (text: string): Message => ({
|
|
12
|
+
export const createUserMessage = (spaceId: SpaceId, threadId: ObjectId, text: string): Message => ({
|
|
12
13
|
id: ObjectId.random(),
|
|
14
|
+
spaceId,
|
|
15
|
+
threadId,
|
|
13
16
|
role: 'user',
|
|
14
17
|
content: [{ type: 'text', text }],
|
|
15
18
|
});
|
|
@@ -17,11 +20,11 @@ export const createUserMessage = (text: string): Message => ({
|
|
|
17
20
|
/**
|
|
18
21
|
* @deprecated Use {@link LLMTool} instead.
|
|
19
22
|
*/
|
|
20
|
-
export const ToolDefinition =
|
|
21
|
-
name:
|
|
22
|
-
description:
|
|
23
|
+
export const ToolDefinition = S.Struct({
|
|
24
|
+
name: S.String,
|
|
25
|
+
description: S.String,
|
|
23
26
|
parameters: JsonSchemaType,
|
|
24
|
-
execute:
|
|
27
|
+
execute: S.Any,
|
|
25
28
|
});
|
|
26
29
|
|
|
27
30
|
/**
|
|
@@ -83,14 +86,14 @@ export const ToolResult = Object.freeze({
|
|
|
83
86
|
Break: (result: unknown): ToolResult => ({ kind: 'break', result }),
|
|
84
87
|
});
|
|
85
88
|
|
|
86
|
-
export type DefineToolParams<Params extends
|
|
89
|
+
export type DefineToolParams<Params extends S.Schema.AnyNoContext> = {
|
|
87
90
|
name: string;
|
|
88
91
|
description: string;
|
|
89
92
|
schema: Params;
|
|
90
|
-
execute: (params:
|
|
93
|
+
execute: (params: S.Schema.Type<Params>, context: ToolExecutionContext) => Promise<ToolResult>;
|
|
91
94
|
};
|
|
92
95
|
|
|
93
|
-
export const defineTool = <Params extends
|
|
96
|
+
export const defineTool = <Params extends S.Schema.AnyNoContext>({
|
|
94
97
|
name,
|
|
95
98
|
description,
|
|
96
99
|
schema,
|
|
@@ -101,7 +104,7 @@ export const defineTool = <Params extends Schema.Schema.AnyNoContext>({
|
|
|
101
104
|
description,
|
|
102
105
|
parameters: toFunctionParameterSchema(schema),
|
|
103
106
|
execute: (params, context) => {
|
|
104
|
-
const sanitized =
|
|
107
|
+
const sanitized = S.decodeSync(schema)(params);
|
|
105
108
|
return execute(sanitized, context);
|
|
106
109
|
},
|
|
107
110
|
};
|
|
@@ -110,7 +113,7 @@ export const defineTool = <Params extends Schema.Schema.AnyNoContext>({
|
|
|
110
113
|
/**
|
|
111
114
|
* Adapts schems to be able to pass to AI providers.
|
|
112
115
|
*/
|
|
113
|
-
const toFunctionParameterSchema = (schema:
|
|
116
|
+
const toFunctionParameterSchema = (schema: S.Schema.All) => {
|
|
114
117
|
const jsonSchema = toJsonSchema(schema);
|
|
115
118
|
log('tool schema', { jsonSchema });
|
|
116
119
|
delete jsonSchema.anyOf;
|
|
@@ -77,7 +77,7 @@ describe('Cypher Combinators', () => {
|
|
|
77
77
|
test('match clause', ({ expect }) => {
|
|
78
78
|
testParser(matchClause, 'MATCH (n:Person)');
|
|
79
79
|
testParser(matchClause, 'MATCH (n:Person)-[r:KNOWS]->(m:Person)');
|
|
80
|
-
testParser(matchClause, 'MATCH (o:
|
|
80
|
+
testParser(matchClause, 'MATCH (o:Org)-[:HAS_EMPLOYEE]->(e:Employee)');
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
test('where clause', ({ expect }) => {
|
|
@@ -116,15 +116,15 @@ describe('Cypher Combinators', () => {
|
|
|
116
116
|
);
|
|
117
117
|
testParser(
|
|
118
118
|
cypherQuery,
|
|
119
|
-
'MATCH (o:
|
|
119
|
+
'MATCH (o:Org)-[:HAS_EMPLOYEE]->(e:Employee)-[:WORKS_ON]->(p:Project) WHERE o.name = "DXOS" AND p.name = "Composer" RETURN e.name, e.id, p.name, p.id',
|
|
120
120
|
);
|
|
121
121
|
testParser(
|
|
122
122
|
cypherQuery,
|
|
123
|
-
'MATCH (n:
|
|
123
|
+
'MATCH (n:Org {name: "DXOS"})-[:HAS_EMPLOYEE]->(m:Employee)<-[:WORKS_ON]-(p:Project {name: "Composer"}) RETURN m.name AS employee',
|
|
124
124
|
);
|
|
125
125
|
testParser(
|
|
126
126
|
cypherQuery,
|
|
127
|
-
"MATCH (org:
|
|
127
|
+
"MATCH (org:Org {name: 'DXOS'})-[:ORG_EMPLOYEES]->(c:Contact)<-[:TASK_ASSIGNEE]-(t:Task)-[:TASK_PROJECT]->(p:Project {name: 'Composer'}) RETURN c.name",
|
|
128
128
|
);
|
|
129
129
|
});
|
|
130
130
|
});
|