@dxos/assistant 0.7.4 → 0.7.5-labs.5f04cf6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/chunk-37SDA65J.mjs +565 -0
- package/dist/lib/browser/chunk-37SDA65J.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +936 -47
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +14 -8
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node/chunk-UYNIAO5N.cjs +577 -0
- package/dist/lib/node/chunk-UYNIAO5N.cjs.map +7 -0
- package/dist/lib/node/index.cjs +936 -54
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +20 -13
- package/dist/lib/node/testing/index.cjs.map +4 -4
- package/dist/lib/node-esm/chunk-AR24K7TY.mjs +567 -0
- package/dist/lib/node-esm/chunk-AR24K7TY.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +936 -47
- 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 +14 -8
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/ai-service/client.d.ts +16 -32
- package/dist/types/src/ai-service/client.d.ts.map +1 -1
- package/dist/types/src/ai-service/defs.d.ts +134 -0
- package/dist/types/src/ai-service/defs.d.ts.map +1 -0
- package/dist/types/src/ai-service/index.d.ts +3 -1
- package/dist/types/src/ai-service/index.d.ts.map +1 -1
- package/dist/types/src/ai-service/parser.d.ts +40 -0
- package/dist/types/src/ai-service/parser.d.ts.map +1 -0
- package/dist/types/src/ai-service/stream.d.ts +35 -0
- package/dist/types/src/ai-service/stream.d.ts.map +1 -0
- package/dist/types/src/ai-service/stream.test.d.ts +2 -0
- package/dist/types/src/ai-service/stream.test.d.ts.map +1 -0
- package/dist/types/src/ai-service/util/index.d.ts +2 -0
- package/dist/types/src/ai-service/util/index.d.ts.map +1 -0
- package/dist/types/src/ai-service/{sse.d.ts → util/sse.d.ts} +5 -0
- package/dist/types/src/ai-service/util/sse.d.ts.map +1 -0
- package/dist/types/src/conversation/conversation.d.ts +56 -5
- package/dist/types/src/conversation/conversation.d.ts.map +1 -1
- package/dist/types/src/conversation/index.d.ts +0 -1
- package/dist/types/src/conversation/index.d.ts.map +1 -1
- package/dist/types/src/conversation/types.d.ts +44 -13
- package/dist/types/src/conversation/types.d.ts.map +1 -1
- package/dist/types/src/cypher/combinators.d.ts.map +1 -1
- package/dist/types/src/cypher/parjs.d.ts +1 -1
- package/dist/types/src/cypher/parjs.d.ts.map +1 -1
- package/dist/types/src/cypher/schema.d.ts.map +1 -1
- package/dist/types/src/testing/artifacts.test.d.ts +1 -0
- package/dist/types/src/testing/artifacts.test.d.ts.map +1 -0
- package/dist/types/src/testing/data.d.ts +2 -0
- package/dist/types/src/testing/data.d.ts.map +1 -0
- package/dist/types/src/testing/defs.d.ts +5 -0
- package/dist/types/src/testing/defs.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +1 -0
- 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/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 +4 -4
- package/package.json +20 -18
- package/src/ai-service/client.test.ts +28 -28
- package/src/ai-service/client.ts +31 -177
- package/src/ai-service/defs.ts +98 -0
- package/src/ai-service/index.ts +3 -1
- package/src/ai-service/parser.ts +183 -0
- package/src/ai-service/stream.test.ts +106 -0
- package/src/ai-service/stream.test.txt +33 -0
- package/src/ai-service/stream.ts +182 -0
- package/src/ai-service/util/index.ts +5 -0
- package/src/ai-service/{sse.ts → util/sse.ts} +5 -0
- package/src/bin/chat.ts +8 -10
- package/src/conversation/conversation.test.ts +13 -17
- package/src/conversation/conversation.ts +113 -18
- package/src/conversation/index.ts +0 -1
- package/src/conversation/types.ts +55 -16
- package/src/cypher/combinators.test.ts +4 -4
- package/src/cypher/combinators.ts +0 -1
- package/src/cypher/execution-plan.test.ts +5 -3
- package/src/cypher/parjs-shims.d.ts +4 -0
- package/src/cypher/parjs.ts +2 -2
- package/src/cypher/query-executor.test.ts +1 -1
- package/src/cypher/schema.ts +0 -1
- package/src/index.ts +1 -1
- package/src/testing/artifacts.test.ts +182 -0
- package/src/testing/data.ts +13 -0
- package/src/testing/defs.ts +8 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/logger.ts +3 -2
- package/src/testing/query-promts.ts +3 -3
- package/src/testing/query.test.ts +11 -10
- package/src/testing/test-data.ts +1 -1
- package/vendor/parjs-browser.mjs +4 -0
- package/vendor/parjs-default.mjs +8 -0
- package/dist/lib/browser/chunk-AKP45LQH.mjs +0 -1394
- package/dist/lib/browser/chunk-AKP45LQH.mjs.map +0 -7
- package/dist/lib/node/chunk-G6CGWEMO.cjs +0 -1418
- package/dist/lib/node/chunk-G6CGWEMO.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-WUPUPEFB.mjs +0 -1396
- package/dist/lib/node-esm/chunk-WUPUPEFB.mjs.map +0 -7
- package/dist/types/src/ai-service/schema.d.ts +0 -296
- package/dist/types/src/ai-service/schema.d.ts.map +0 -1
- package/dist/types/src/ai-service/sse.d.ts.map +0 -1
- package/dist/types/src/sanity.test.d.ts +0 -2
- package/dist/types/src/sanity.test.d.ts.map +0 -1
- package/src/ai-service/schema.ts +0 -260
- package/src/sanity.test.ts +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/assistant",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5-labs.5f04cf6",
|
|
4
4
|
"description": "Natural language interface to DXOS",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -33,26 +33,25 @@
|
|
|
33
33
|
"types": "dist/types/src/index.d.ts",
|
|
34
34
|
"files": [
|
|
35
35
|
"dist",
|
|
36
|
+
"vendor",
|
|
36
37
|
"src"
|
|
37
38
|
],
|
|
38
|
-
"scripts": {
|
|
39
|
-
"run:chat": "tsx src/bin/chat.ts"
|
|
40
|
-
},
|
|
41
39
|
"dependencies": {
|
|
42
|
-
"@dxos/async": "workspace:*",
|
|
43
|
-
"@dxos/context": "workspace:*",
|
|
44
|
-
"@dxos/debug": "workspace:*",
|
|
45
|
-
"@dxos/echo-db": "workspace:*",
|
|
46
|
-
"@dxos/echo-schema": "workspace:*",
|
|
47
|
-
"@dxos/invariant": "workspace:*",
|
|
48
|
-
"@dxos/keys": "workspace:*",
|
|
49
|
-
"@dxos/live-object": "workspace:*",
|
|
50
|
-
"@dxos/log": "workspace:*",
|
|
51
|
-
"@dxos/node-std": "workspace:*",
|
|
52
|
-
"@dxos/util": "workspace:*",
|
|
53
40
|
"@effect/schema": "^0.75.5",
|
|
54
41
|
"parjs": "^1.3.9",
|
|
55
|
-
"ulidx": "^2.3.0"
|
|
42
|
+
"ulidx": "^2.3.0",
|
|
43
|
+
"@dxos/async": "0.7.5-labs.5f04cf6",
|
|
44
|
+
"@dxos/context": "0.7.5-labs.5f04cf6",
|
|
45
|
+
"@dxos/artifact": "0.7.5-labs.5f04cf6",
|
|
46
|
+
"@dxos/debug": "0.7.5-labs.5f04cf6",
|
|
47
|
+
"@dxos/echo-db": "0.7.5-labs.5f04cf6",
|
|
48
|
+
"@dxos/echo-schema": "0.7.5-labs.5f04cf6",
|
|
49
|
+
"@dxos/invariant": "0.7.5-labs.5f04cf6",
|
|
50
|
+
"@dxos/keys": "0.7.5-labs.5f04cf6",
|
|
51
|
+
"@dxos/log": "0.7.5-labs.5f04cf6",
|
|
52
|
+
"@dxos/live-object": "0.7.5-labs.5f04cf6",
|
|
53
|
+
"@dxos/node-std": "0.7.5-labs.5f04cf6",
|
|
54
|
+
"@dxos/util": "0.7.5-labs.5f04cf6"
|
|
56
55
|
},
|
|
57
56
|
"devDependencies": {
|
|
58
57
|
"inquirer": "^12.0.0"
|
|
@@ -60,5 +59,8 @@
|
|
|
60
59
|
"publishConfig": {
|
|
61
60
|
"access": "restricted"
|
|
62
61
|
},
|
|
63
|
-
"beast": {}
|
|
64
|
-
|
|
62
|
+
"beast": {},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"run:chat": "tsx src/bin/chat.ts"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -5,26 +5,28 @@
|
|
|
5
5
|
import { Schema as S } from '@effect/schema';
|
|
6
6
|
import { test, describe } from 'vitest';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { type Tool } from '@dxos/artifact';
|
|
9
|
+
import { toJsonSchema, ObjectId } from '@dxos/echo-schema';
|
|
9
10
|
import { invariant } from '@dxos/invariant';
|
|
10
11
|
import { SpaceId } from '@dxos/keys';
|
|
11
12
|
import { log } from '@dxos/log';
|
|
12
13
|
|
|
13
14
|
import { AIServiceClientImpl } from './client';
|
|
14
|
-
import {
|
|
15
|
+
import { ToolTypes } from './defs';
|
|
16
|
+
import { AI_SERVICE_ENDPOINT } from '../testing';
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
// log.config({ filter: 'debug' });
|
|
17
19
|
|
|
18
|
-
describe('AI Service Client', () => {
|
|
20
|
+
describe.skip('AI Service Client', () => {
|
|
19
21
|
test('client generation', async () => {
|
|
20
22
|
const client = new AIServiceClientImpl({
|
|
21
|
-
endpoint:
|
|
23
|
+
endpoint: AI_SERVICE_ENDPOINT.LOCAL,
|
|
22
24
|
});
|
|
23
25
|
|
|
24
26
|
const spaceId = SpaceId.random();
|
|
25
27
|
const threadId = ObjectId.random();
|
|
26
28
|
|
|
27
|
-
await client.
|
|
29
|
+
await client.appendMessages([
|
|
28
30
|
{
|
|
29
31
|
id: ObjectId.random(),
|
|
30
32
|
spaceId,
|
|
@@ -42,20 +44,20 @@ describe('AI Service Client', () => {
|
|
|
42
44
|
tools: [],
|
|
43
45
|
});
|
|
44
46
|
for await (const event of stream) {
|
|
45
|
-
log
|
|
47
|
+
log('event', event);
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
log
|
|
50
|
+
log('full message', {
|
|
49
51
|
message: await stream.complete(),
|
|
50
52
|
});
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
test('tool calls', async () => {
|
|
54
56
|
const client = new AIServiceClientImpl({
|
|
55
|
-
endpoint:
|
|
57
|
+
endpoint: AI_SERVICE_ENDPOINT.LOCAL,
|
|
56
58
|
});
|
|
57
59
|
|
|
58
|
-
const custodian:
|
|
60
|
+
const custodian: Tool = {
|
|
59
61
|
name: 'custodian',
|
|
60
62
|
description: 'Custodian can tell you the password if you say the magic word',
|
|
61
63
|
parameters: toJsonSchema(
|
|
@@ -68,7 +70,7 @@ describe('AI Service Client', () => {
|
|
|
68
70
|
const spaceId = SpaceId.random();
|
|
69
71
|
const threadId = ObjectId.random();
|
|
70
72
|
|
|
71
|
-
await client.
|
|
73
|
+
await client.appendMessages([
|
|
72
74
|
{
|
|
73
75
|
id: ObjectId.random(),
|
|
74
76
|
spaceId,
|
|
@@ -86,17 +88,15 @@ describe('AI Service Client', () => {
|
|
|
86
88
|
tools: [custodian],
|
|
87
89
|
});
|
|
88
90
|
for await (const event of stream) {
|
|
89
|
-
log
|
|
91
|
+
log('event', event);
|
|
90
92
|
}
|
|
91
93
|
const [message] = await stream.complete();
|
|
92
|
-
log
|
|
93
|
-
|
|
94
|
-
});
|
|
95
|
-
await client.insertMessages([message]);
|
|
94
|
+
log('full message', { message });
|
|
95
|
+
await client.appendMessages([message]);
|
|
96
96
|
|
|
97
97
|
const toolUse = message.content.find(({ type }) => type === 'tool_use')!;
|
|
98
98
|
invariant(toolUse.type === 'tool_use');
|
|
99
|
-
await client.
|
|
99
|
+
await client.appendMessages([
|
|
100
100
|
{
|
|
101
101
|
id: ObjectId.random(),
|
|
102
102
|
spaceId,
|
|
@@ -114,23 +114,21 @@ describe('AI Service Client', () => {
|
|
|
114
114
|
tools: [custodian],
|
|
115
115
|
});
|
|
116
116
|
for await (const event of stream2) {
|
|
117
|
-
log
|
|
117
|
+
log('event', event);
|
|
118
118
|
}
|
|
119
119
|
const [message2] = await stream2.complete();
|
|
120
|
-
log
|
|
121
|
-
message: message2,
|
|
122
|
-
});
|
|
120
|
+
log('full message', { message: message2 });
|
|
123
121
|
});
|
|
124
122
|
|
|
125
|
-
test.
|
|
123
|
+
test.skip('image generation', async () => {
|
|
126
124
|
const client = new AIServiceClientImpl({
|
|
127
|
-
endpoint:
|
|
125
|
+
endpoint: AI_SERVICE_ENDPOINT.LOCAL,
|
|
128
126
|
});
|
|
129
127
|
|
|
130
128
|
const spaceId = SpaceId.random();
|
|
131
129
|
const threadId = ObjectId.random();
|
|
132
130
|
|
|
133
|
-
await client.
|
|
131
|
+
await client.appendMessages([
|
|
134
132
|
{
|
|
135
133
|
id: ObjectId.random(),
|
|
136
134
|
spaceId,
|
|
@@ -148,15 +146,17 @@ describe('AI Service Client', () => {
|
|
|
148
146
|
{
|
|
149
147
|
name: 'text-to-image',
|
|
150
148
|
type: ToolTypes.TextToImage,
|
|
149
|
+
// options: {
|
|
150
|
+
// model: '@cf/stabilityai/stable-diffusion-xl-base-1.0',
|
|
151
|
+
// },
|
|
151
152
|
},
|
|
152
153
|
],
|
|
153
154
|
});
|
|
155
|
+
|
|
154
156
|
for await (const event of stream) {
|
|
155
|
-
log
|
|
157
|
+
log('event', event);
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
log
|
|
159
|
-
message: await stream.complete(),
|
|
160
|
-
});
|
|
160
|
+
log('full message', { message: await stream.complete() });
|
|
161
161
|
});
|
|
162
162
|
});
|
package/src/ai-service/client.ts
CHANGED
|
@@ -2,41 +2,38 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
/* eslint-disable no-console */
|
|
6
|
-
|
|
7
5
|
import { Schema as S } from '@effect/schema';
|
|
8
6
|
|
|
9
|
-
import {
|
|
7
|
+
import { Message, type Space, type Thread } from '@dxos/artifact';
|
|
10
8
|
import { invariant } from '@dxos/invariant';
|
|
11
|
-
import type
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
Message,
|
|
15
|
-
type GenerateRequest,
|
|
16
|
-
type ObjectId,
|
|
17
|
-
type ResultStreamEvent,
|
|
18
|
-
type Space,
|
|
19
|
-
type Thread,
|
|
20
|
-
} from './schema';
|
|
21
|
-
import { iterSSEMessages } from './sse';
|
|
22
|
-
import { log } from '@dxos/log';
|
|
9
|
+
import { type SpaceId } from '@dxos/keys';
|
|
23
10
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
};
|
|
11
|
+
import { type GenerateRequest } from './defs';
|
|
12
|
+
import { GenerationStream } from './stream';
|
|
27
13
|
|
|
14
|
+
// TODO(burdon): Rename.
|
|
28
15
|
export interface AIServiceClient {
|
|
29
16
|
getSpace(spaceId: SpaceId): Promise<Space>;
|
|
30
17
|
getThread(spaceId: SpaceId, threadId: string): Promise<Thread>;
|
|
31
|
-
|
|
32
|
-
|
|
18
|
+
getMessages(spaceId: SpaceId, threadId: string): Promise<Message[]>;
|
|
19
|
+
appendMessages(messages: Message[]): Promise<void>;
|
|
33
20
|
updateMessage(spaceId: SpaceId, threadId: string, messageId: string, message: Message): Promise<void>;
|
|
34
21
|
generate(request: GenerateRequest): Promise<GenerationStream>;
|
|
35
22
|
}
|
|
36
23
|
|
|
24
|
+
export type AIServiceClientParams = {
|
|
25
|
+
endpoint: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Edge GPT client.
|
|
30
|
+
*/
|
|
31
|
+
// TODO(burdon): Create mock.
|
|
37
32
|
export class AIServiceClientImpl implements AIServiceClient {
|
|
38
33
|
private readonly _endpoint: string;
|
|
34
|
+
|
|
39
35
|
constructor({ endpoint }: AIServiceClientParams) {
|
|
36
|
+
invariant(endpoint, 'endpoint is required');
|
|
40
37
|
this._endpoint = endpoint;
|
|
41
38
|
}
|
|
42
39
|
|
|
@@ -48,13 +45,14 @@ export class AIServiceClientImpl implements AIServiceClient {
|
|
|
48
45
|
throw new Error('Not implemented');
|
|
49
46
|
}
|
|
50
47
|
|
|
51
|
-
async
|
|
48
|
+
async getMessages(spaceId: SpaceId, threadId: string): Promise<Message[]> {
|
|
52
49
|
const res = await fetch(`${this._endpoint}/space/${spaceId}/thread/${threadId}/message`);
|
|
53
50
|
return S.decodePromise(S.Array(Message).pipe(S.mutable))((await res.json()).data.results);
|
|
54
51
|
}
|
|
55
52
|
|
|
56
|
-
async
|
|
57
|
-
const
|
|
53
|
+
async appendMessages(messages: Message[]): Promise<void> {
|
|
54
|
+
const url = `${this._endpoint}/message`;
|
|
55
|
+
const res = await fetch(url, {
|
|
58
56
|
method: 'POST',
|
|
59
57
|
headers: { 'Content-Type': 'application/json' },
|
|
60
58
|
body: JSON.stringify({
|
|
@@ -63,7 +61,7 @@ export class AIServiceClientImpl implements AIServiceClient {
|
|
|
63
61
|
});
|
|
64
62
|
|
|
65
63
|
if (!res.ok) {
|
|
66
|
-
throw new Error(await res.text());
|
|
64
|
+
throw new Error(`${await res.text()} [${url}]`);
|
|
67
65
|
}
|
|
68
66
|
}
|
|
69
67
|
|
|
@@ -72,172 +70,28 @@ export class AIServiceClientImpl implements AIServiceClient {
|
|
|
72
70
|
throw new Error('Not implemented');
|
|
73
71
|
}
|
|
74
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Open message stream.
|
|
75
|
+
*/
|
|
75
76
|
async generate(request: GenerateRequest): Promise<GenerationStream> {
|
|
77
|
+
const controller = new AbortController();
|
|
78
|
+
const signal = controller.signal;
|
|
79
|
+
|
|
76
80
|
const response = await fetch(`${this._endpoint}/generate`, {
|
|
77
81
|
method: 'POST',
|
|
78
82
|
headers: { 'Content-Type': 'application/json' },
|
|
79
83
|
body: JSON.stringify(request),
|
|
84
|
+
signal,
|
|
80
85
|
});
|
|
81
|
-
invariant(response.body instanceof ReadableStream);
|
|
82
86
|
|
|
87
|
+
invariant(response.body instanceof ReadableStream);
|
|
83
88
|
return GenerationStream.fromSSEResponse(
|
|
84
89
|
{
|
|
85
90
|
spaceId: request.spaceId,
|
|
86
91
|
threadId: request.threadId,
|
|
87
92
|
},
|
|
88
93
|
response,
|
|
94
|
+
controller,
|
|
89
95
|
);
|
|
90
96
|
}
|
|
91
97
|
}
|
|
92
|
-
|
|
93
|
-
type GenerationParams = {
|
|
94
|
-
spaceId: SpaceId;
|
|
95
|
-
threadId: ObjectId;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
export class GenerationStream implements AsyncIterable<ResultStreamEvent> {
|
|
99
|
-
static fromSSEResponse(params: GenerationParams, response: Response) {
|
|
100
|
-
const controller = new AbortController();
|
|
101
|
-
const iterator = async function* () {
|
|
102
|
-
for await (const sse of iterSSEMessages(response, controller)) {
|
|
103
|
-
if (sse.event === 'completion') {
|
|
104
|
-
try {
|
|
105
|
-
yield JSON.parse(sse.data);
|
|
106
|
-
} catch (e) {
|
|
107
|
-
console.error('Could not parse message into JSON:', sse.data);
|
|
108
|
-
console.error('From chunk:', sse.raw);
|
|
109
|
-
throw e;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
sse.event === 'message_start' ||
|
|
115
|
-
sse.event === 'message_delta' ||
|
|
116
|
-
sse.event === 'message_stop' ||
|
|
117
|
-
sse.event === 'content_block_start' ||
|
|
118
|
-
sse.event === 'content_block_delta' ||
|
|
119
|
-
sse.event === 'content_block_stop'
|
|
120
|
-
) {
|
|
121
|
-
try {
|
|
122
|
-
yield JSON.parse(sse.data);
|
|
123
|
-
} catch (e) {
|
|
124
|
-
console.error('Could not parse message into JSON:', sse.data);
|
|
125
|
-
console.error('From chunk:', sse.raw);
|
|
126
|
-
throw e;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (sse.event === 'ping') {
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (sse.event === 'error') {
|
|
135
|
-
throw new Error(`Message generation error: ${sse.data}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
return new GenerationStream(params, iterator);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private _previousMessages: Message[] = [];
|
|
143
|
-
private _accumulatedMessage?: Message = undefined;
|
|
144
|
-
private _done = new Trigger();
|
|
145
|
-
private _iterator?: AsyncIterator<ResultStreamEvent> = undefined;
|
|
146
|
-
|
|
147
|
-
constructor(
|
|
148
|
-
private readonly _params: GenerationParams,
|
|
149
|
-
private readonly _getIterator: () => AsyncIterableIterator<ResultStreamEvent>,
|
|
150
|
-
) {}
|
|
151
|
-
|
|
152
|
-
[Symbol.asyncIterator](): AsyncIterator<ResultStreamEvent> {
|
|
153
|
-
return this._createIterator();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
cancel() {
|
|
157
|
-
throw new Error('Not implemented');
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
get accumulatedMessages(): Message[] {
|
|
161
|
-
// TODO(dmaretskyi): Support multiple accumulated messages.
|
|
162
|
-
return [...this._previousMessages, ...(this._accumulatedMessage == null ? [] : [this._accumulatedMessage])];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Returns the complete message list generated by the AI service.
|
|
167
|
-
*/
|
|
168
|
-
async complete(): Promise<Message[]> {
|
|
169
|
-
await this._done.wait();
|
|
170
|
-
return this.accumulatedMessages;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private _createIterator(): AsyncIterator<ResultStreamEvent> {
|
|
174
|
-
const self = this;
|
|
175
|
-
return (this._iterator ??= (async function* () {
|
|
176
|
-
for await (const event of self._getIterator()) {
|
|
177
|
-
self._processEvent(event);
|
|
178
|
-
yield event;
|
|
179
|
-
}
|
|
180
|
-
})());
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
private _processEvent(event: ResultStreamEvent) {
|
|
184
|
-
// TODO(dmaretskyi): Support multiple message, e.g. for service-defined tools.
|
|
185
|
-
switch (event.type) {
|
|
186
|
-
case 'message_start': {
|
|
187
|
-
if (this._accumulatedMessage) {
|
|
188
|
-
this._previousMessages.push(this._accumulatedMessage);
|
|
189
|
-
this._accumulatedMessage = undefined;
|
|
190
|
-
}
|
|
191
|
-
this._accumulatedMessage = {
|
|
192
|
-
...event.message,
|
|
193
|
-
threadId: this._params.threadId,
|
|
194
|
-
spaceId: this._params.spaceId,
|
|
195
|
-
};
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
case 'message_delta': {
|
|
199
|
-
if (this._accumulatedMessage === undefined) {
|
|
200
|
-
throw new Error('Received message delta without a message start');
|
|
201
|
-
}
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
case 'message_stop': {
|
|
205
|
-
if (this._accumulatedMessage === undefined) {
|
|
206
|
-
throw new Error('Received message stop without a message start');
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
case 'content_block_start': {
|
|
212
|
-
invariant(this._accumulatedMessage);
|
|
213
|
-
this._accumulatedMessage.content.push(event.content);
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
|
-
case 'content_block_delta': {
|
|
217
|
-
invariant(this._accumulatedMessage);
|
|
218
|
-
const snapshotContent = this._accumulatedMessage.content.at(event.index);
|
|
219
|
-
if (snapshotContent?.type === 'text' && event.delta.type === 'text_delta') {
|
|
220
|
-
snapshotContent.text += event.delta.text;
|
|
221
|
-
} else if (snapshotContent?.type === 'tool_use' && event.delta.type === 'input_json_delta') {
|
|
222
|
-
snapshotContent.inputJson ??= '';
|
|
223
|
-
snapshotContent.inputJson += event.delta.partial_json;
|
|
224
|
-
|
|
225
|
-
// TODO(dmaretskyi): Partial parsing.
|
|
226
|
-
// if (jsonBuf) {
|
|
227
|
-
// snapshotContent.input = partialParse(jsonBuf);
|
|
228
|
-
// }
|
|
229
|
-
}
|
|
230
|
-
break;
|
|
231
|
-
}
|
|
232
|
-
case 'content_block_stop': {
|
|
233
|
-
invariant(this._accumulatedMessage);
|
|
234
|
-
const lastBlock = this._accumulatedMessage.content.at(-1);
|
|
235
|
-
if (lastBlock?.type === 'tool_use') {
|
|
236
|
-
lastBlock.input = JSON.parse(lastBlock.inputJson ?? '');
|
|
237
|
-
}
|
|
238
|
-
break;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
this._done.wake();
|
|
242
|
-
}
|
|
243
|
-
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Schema as S } from '@effect/schema';
|
|
6
|
+
|
|
7
|
+
import { Tool, Message, type MessageContentBlock, SpaceIdSchema } from '@dxos/artifact';
|
|
8
|
+
import { ObjectId } from '@dxos/echo-schema';
|
|
9
|
+
|
|
10
|
+
export const createArtifactElement = (id: ObjectId) => `<artifact id=${id} />`;
|
|
11
|
+
|
|
12
|
+
export const LLMModel = S.Literal(
|
|
13
|
+
'@anthropic/claude-3-5-haiku-20241022',
|
|
14
|
+
'@anthropic/claude-3-5-sonnet-20241022',
|
|
15
|
+
'@cf/deepseek-ai/deepseek-r1-distill-qwen-32b',
|
|
16
|
+
'@hf/nousresearch/hermes-2-pro-mistral-7b',
|
|
17
|
+
'@ollama/llama-3-2-3b',
|
|
18
|
+
'@ollama/llama-3-1-nemotron-70b-instruct',
|
|
19
|
+
'@ollama/llama-3-1-nemotron-mini-4b-instruct',
|
|
20
|
+
'@cf/deepseek-ai/deepseek-r1-distill-qwen-32b',
|
|
21
|
+
);
|
|
22
|
+
export type LLMModel = S.Schema.Type<typeof LLMModel>;
|
|
23
|
+
|
|
24
|
+
export const ToolTypes = Object.freeze({
|
|
25
|
+
// TODO(dmaretskyi): Not implemented yet.
|
|
26
|
+
// DatabaseQuery: 'database_query',
|
|
27
|
+
TextToImage: 'text_to_image',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
33
|
+
export const GenerateRequest = S.Struct({
|
|
34
|
+
spaceId: S.optional(SpaceIdSchema),
|
|
35
|
+
threadId: S.optional(ObjectId),
|
|
36
|
+
model: LLMModel,
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* History of messages to include in the context window.
|
|
40
|
+
*/
|
|
41
|
+
history: S.optional(S.Array(Message)),
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* System instructions to the LLM.
|
|
45
|
+
*/
|
|
46
|
+
// TODO(dmaretskyi): Should this be part of the thread?.
|
|
47
|
+
systemPrompt: S.optional(S.String),
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Tools available for the LLM.
|
|
51
|
+
*/
|
|
52
|
+
tools: S.optional(S.Array(Tool).pipe(S.mutable)),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export type GenerateRequest = S.Schema.Type<typeof GenerateRequest>;
|
|
56
|
+
|
|
57
|
+
export type LLMStopReason = 'tool_use' | 'end_turn';
|
|
58
|
+
|
|
59
|
+
// TODO(dmaretskyi): Effect schema.
|
|
60
|
+
// TODO(dmaretskyi): Rename GenerationStreamEvent.
|
|
61
|
+
export type ResultStreamEvent =
|
|
62
|
+
| {
|
|
63
|
+
// TODO(dmaretskyi): Normalize types to our schema.
|
|
64
|
+
type: 'message_start';
|
|
65
|
+
message: Omit<Message, 'spaceId' | 'threadId'>;
|
|
66
|
+
}
|
|
67
|
+
| {
|
|
68
|
+
type: 'message_delta';
|
|
69
|
+
delta: {
|
|
70
|
+
stopReason: LLMStopReason;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
| {
|
|
74
|
+
type: 'message_stop';
|
|
75
|
+
}
|
|
76
|
+
| {
|
|
77
|
+
type: 'content_block_start';
|
|
78
|
+
index: number;
|
|
79
|
+
content: MessageContentBlock;
|
|
80
|
+
}
|
|
81
|
+
| {
|
|
82
|
+
type: 'content_block_delta';
|
|
83
|
+
index: number;
|
|
84
|
+
delta:
|
|
85
|
+
| {
|
|
86
|
+
type: 'text_delta';
|
|
87
|
+
text: string;
|
|
88
|
+
}
|
|
89
|
+
| {
|
|
90
|
+
partial_json: string;
|
|
91
|
+
|
|
92
|
+
type: 'input_json_delta';
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
| {
|
|
96
|
+
type: 'content_block_stop';
|
|
97
|
+
index: number;
|
|
98
|
+
};
|