@dxos/assistant 0.8.0 → 0.8.1-main.a06ad20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/lib/browser/index.mjs +976 -536
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +2 -2
  5. package/dist/lib/browser/testing/index.mjs.map +1 -1
  6. package/dist/lib/node/index.cjs +1172 -735
  7. package/dist/lib/node/index.cjs.map +4 -4
  8. package/dist/lib/node/meta.json +1 -1
  9. package/dist/lib/node/testing/index.cjs +22 -22
  10. package/dist/lib/node/testing/index.cjs.map +2 -2
  11. package/dist/lib/node-esm/index.mjs +976 -536
  12. package/dist/lib/node-esm/index.mjs.map +4 -4
  13. package/dist/lib/node-esm/meta.json +1 -1
  14. package/dist/lib/node-esm/testing/index.mjs +2 -2
  15. package/dist/lib/node-esm/testing/index.mjs.map +1 -1
  16. package/dist/types/src/ai-service/edge-client.d.ts +19 -0
  17. package/dist/types/src/ai-service/edge-client.d.ts.map +1 -0
  18. package/dist/types/src/ai-service/index.d.ts +4 -1
  19. package/dist/types/src/ai-service/index.d.ts.map +1 -1
  20. package/dist/types/src/ai-service/message-collector.d.ts +88 -0
  21. package/dist/types/src/ai-service/message-collector.d.ts.map +1 -0
  22. package/dist/types/src/ai-service/ollama-client.d.ts +43 -0
  23. package/dist/types/src/ai-service/ollama-client.d.ts.map +1 -0
  24. package/dist/types/src/ai-service/parser.d.ts +3 -1
  25. package/dist/types/src/ai-service/parser.d.ts.map +1 -1
  26. package/dist/types/src/ai-service/service.d.ts +9 -0
  27. package/dist/types/src/ai-service/service.d.ts.map +1 -0
  28. package/dist/types/src/ai-service/stream.d.ts +2 -1
  29. package/dist/types/src/ai-service/stream.d.ts.map +1 -1
  30. package/dist/types/src/ai-service/testing/mock.d.ts +2 -2
  31. package/dist/types/src/ai-service/testing/mock.d.ts.map +1 -1
  32. package/dist/types/src/ai-service/types.d.ts +3 -3
  33. package/dist/types/src/ai-service/types.d.ts.map +1 -1
  34. package/dist/types/src/conversation/conversation.d.ts +3 -1
  35. package/dist/types/src/conversation/conversation.d.ts.map +1 -1
  36. package/dist/types/src/conversation/types.d.ts +1 -1
  37. package/dist/types/src/conversation/types.d.ts.map +1 -1
  38. package/dist/types/src/testing/query-promts.d.ts +1 -1
  39. package/dist/types/src/testing/query-promts.d.ts.map +1 -1
  40. package/dist/types/src/testing/test-data.d.ts +1 -1
  41. package/dist/types/src/testing/test-data.d.ts.map +1 -1
  42. package/dist/types/src/testing/test-schema.d.ts +1 -1
  43. package/dist/types/src/testing/test-schema.d.ts.map +1 -1
  44. package/package.json +17 -14
  45. package/src/ai-service/client.test.ts +94 -7
  46. package/src/ai-service/{client.ts → edge-client.ts} +15 -24
  47. package/src/ai-service/index.ts +4 -1
  48. package/src/ai-service/message-collector.ts +84 -0
  49. package/src/ai-service/ollama-client.ts +473 -0
  50. package/src/ai-service/parser.ts +7 -2
  51. package/src/ai-service/service.ts +15 -0
  52. package/src/ai-service/stream.ts +5 -3
  53. package/src/ai-service/testing/mock.ts +7 -2
  54. package/src/ai-service/types.ts +1 -1
  55. package/src/bin/chat.ts +2 -2
  56. package/src/conversation/conversation.test.ts +3 -3
  57. package/src/conversation/conversation.ts +3 -2
  58. package/src/conversation/types.ts +1 -1
  59. package/src/testing/artifacts.test.ts +2 -2
  60. package/src/testing/query-promts.ts +1 -1
  61. package/src/testing/query.test.ts +2 -2
  62. package/src/testing/test-data.ts +1 -1
  63. package/src/testing/test-schema.ts +1 -1
  64. package/dist/types/src/ai-service/client.d.ts +0 -25
  65. package/dist/types/src/ai-service/client.d.ts.map +0 -1
@@ -2,17 +2,19 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema as S } from '@effect/schema';
5
+ import { Schema as S } from 'effect';
6
6
  import { test, describe } from 'vitest';
7
7
 
8
- import { type Message, type Tool } from '@dxos/artifact';
9
- import { toJsonSchema, ObjectId } from '@dxos/echo-schema';
8
+ import { defineTool, Message, ToolResult, type Tool } from '@dxos/artifact';
9
+ import { toJsonSchema, ObjectId, createStatic } from '@dxos/echo-schema';
10
10
  import { invariant } from '@dxos/invariant';
11
11
  import { SpaceId } from '@dxos/keys';
12
12
  import { log } from '@dxos/log';
13
13
 
14
- import { AIServiceClientImpl } from './client';
15
14
  import { DEFAULT_LLM_MODEL } from './defs';
15
+ import { AIServiceEdgeClient } from './edge-client';
16
+ import { OllamaClient } from './ollama-client';
17
+ import { MixedStreamParser } from './parser';
16
18
  import { ToolTypes } from './types';
17
19
  import { AI_SERVICE_ENDPOINT } from '../testing';
18
20
 
@@ -20,7 +22,7 @@ import { AI_SERVICE_ENDPOINT } from '../testing';
20
22
 
21
23
  describe.skip('AI Service Client', () => {
22
24
  test('client generation', async () => {
23
- const client = new AIServiceClientImpl({
25
+ const client = new AIServiceEdgeClient({
24
26
  endpoint: AI_SERVICE_ENDPOINT.LOCAL,
25
27
  });
26
28
 
@@ -54,7 +56,7 @@ describe.skip('AI Service Client', () => {
54
56
  });
55
57
 
56
58
  test('tool calls', async () => {
57
- const client = new AIServiceClientImpl({
59
+ const client = new AIServiceEdgeClient({
58
60
  endpoint: AI_SERVICE_ENDPOINT.LOCAL,
59
61
  });
60
62
 
@@ -137,7 +139,7 @@ describe.skip('AI Service Client', () => {
137
139
  });
138
140
 
139
141
  test.skip('image generation', async () => {
140
- const client = new AIServiceClientImpl({
142
+ const client = new AIServiceEdgeClient({
141
143
  endpoint: AI_SERVICE_ENDPOINT.LOCAL,
142
144
  });
143
145
 
@@ -176,3 +178,88 @@ describe.skip('AI Service Client', () => {
176
178
  log('full message', { message: await stream.complete() });
177
179
  });
178
180
  });
181
+
182
+ describe('Ollama Client', () => {
183
+ test('basic', async (ctx) => {
184
+ const isRunning = await OllamaClient.isRunning();
185
+ if (!isRunning) {
186
+ ctx.skip();
187
+ }
188
+
189
+ const client = OllamaClient.createClient();
190
+ const parser = new MixedStreamParser();
191
+
192
+ const messages = await parser.parse(
193
+ await client.exec({
194
+ prompt: createStatic(Message, {
195
+ role: 'user',
196
+ content: [{ type: 'text', text: 'Hello, world!' }],
197
+ }),
198
+ }),
199
+ );
200
+
201
+ log.info('messages', { messages });
202
+ });
203
+
204
+ test('tool calls', async (ctx) => {
205
+ const isRunning = await OllamaClient.isRunning();
206
+ if (!isRunning) {
207
+ ctx.skip();
208
+ }
209
+
210
+ const client = OllamaClient.createClient({
211
+ tools: [
212
+ defineTool('test', {
213
+ name: 'encrypt',
214
+ description: 'Encrypt a message',
215
+ schema: S.Struct({
216
+ message: S.String.annotations({ description: 'The message to encrypt' }),
217
+ }),
218
+ execute: async ({ message }) => ToolResult.Success(message.split('').reverse().join('')),
219
+ }),
220
+ ],
221
+ });
222
+ const parser = new MixedStreamParser();
223
+ parser.streamEvent.on((event) => {
224
+ // log.info('event', { event });
225
+ });
226
+
227
+ const messages = await parser.parse(
228
+ await client.exec({
229
+ prompt: createStatic(Message, {
230
+ role: 'user',
231
+ content: [{ type: 'text', text: 'What is the encrypted message for "Hello, world!"' }],
232
+ }),
233
+ }),
234
+ );
235
+
236
+ log.info('messages', { messages });
237
+ });
238
+
239
+ test('text-to-image', async (ctx) => {
240
+ const isRunning = await OllamaClient.isRunning();
241
+ if (!isRunning) {
242
+ ctx.skip();
243
+ }
244
+
245
+ const client = OllamaClient.createClient();
246
+ const parser = new MixedStreamParser();
247
+
248
+ const messages = await parser.parse(
249
+ await client.exec({
250
+ prompt: createStatic(Message, {
251
+ role: 'user',
252
+ content: [{ type: 'text', text: 'Generate an image of a cat' }],
253
+ }),
254
+ tools: [
255
+ {
256
+ name: 'text-to-image',
257
+ type: ToolTypes.TextToImage,
258
+ },
259
+ ],
260
+ }),
261
+ );
262
+
263
+ log.info('messages', { messages });
264
+ });
265
+ });
@@ -2,39 +2,32 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema as S } from '@effect/schema';
5
+ import { Schema as S } from 'effect';
6
6
 
7
7
  import { Message } from '@dxos/artifact';
8
8
  import { invariant } from '@dxos/invariant';
9
9
  import { log } from '@dxos/log';
10
10
 
11
- import { createGenerationStream, type GenerationStream } from './stream';
11
+ import type { AIServiceClient, GenerationStream } from './service';
12
+ import { createGenerationStream } from './stream';
12
13
  import { type GenerateRequest } from './types';
13
14
 
14
- export interface AIService {
15
- exec(request: GenerateRequest): Promise<GenerationStream>;
16
- }
17
-
18
- export interface AIServiceClient extends AIService {
19
- appendMessages(messages: Message[]): Promise<void>;
20
- }
21
-
22
- export type AIServiceClientParams = {
15
+ export type AIServiceEdgeClientOptions = {
23
16
  endpoint: string;
24
17
  };
25
18
 
26
- /**
27
- * Edge GPT client.
28
- */
29
- // TODO(burdon): Create mock.
30
- export class AIServiceClientImpl implements AIServiceClient {
19
+ export class AIServiceEdgeClient implements AIServiceClient {
31
20
  private readonly _endpoint: string;
32
21
 
33
- constructor({ endpoint }: AIServiceClientParams) {
22
+ constructor({ endpoint }: AIServiceEdgeClientOptions) {
34
23
  invariant(endpoint, 'endpoint is required');
35
24
  this._endpoint = endpoint;
36
25
  }
37
26
 
27
+ /**
28
+ * @deprecated
29
+ */
30
+ // TODO(burdon): Remove.
38
31
  async appendMessages(messages: Message[]): Promise<void> {
39
32
  const url = `${this._endpoint}/message`;
40
33
  const res = await fetch(url, {
@@ -54,19 +47,17 @@ export class AIServiceClientImpl implements AIServiceClient {
54
47
  * Process request and open message stream.
55
48
  */
56
49
  async exec(request: GenerateRequest): Promise<GenerationStream> {
57
- // TODO(dmaretskyi): Errors if tools are not provided.
58
- request = {
59
- tools: request.tools ?? [],
60
- ...request,
61
- };
62
-
63
50
  log.info('requesting', { endpoint: this._endpoint });
64
51
  const controller = new AbortController();
65
52
  const response = await fetch(`${this._endpoint}/generate`, {
66
53
  signal: controller.signal,
67
54
  method: 'POST',
68
55
  headers: { 'Content-Type': 'application/json' },
69
- body: JSON.stringify(request),
56
+ body: JSON.stringify({
57
+ // TODO(dmaretskyi): Errors if tools are not provided.
58
+ tools: request.tools ?? [],
59
+ ...request,
60
+ }),
70
61
  });
71
62
 
72
63
  invariant(response.body instanceof ReadableStream);
@@ -2,9 +2,12 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export * from './client';
6
5
  export * from './defs';
6
+ export * from './edge-client';
7
+ export * from './message-collector';
8
+ export * from './ollama-client';
7
9
  export * from './parser';
10
+ export * from './service';
8
11
  export * from './stream';
9
12
  export * from './transform';
10
13
  export * from './types';
@@ -0,0 +1,84 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import type { Message } from '@dxos/artifact';
6
+
7
+ import type { GenerationStreamEvent } from './types';
8
+
9
+ export class MessageCollector {
10
+ private _messages: Message[] = [];
11
+ private _pendingMessage?: Message = undefined;
12
+
13
+ public get messages() {
14
+ return this._messages;
15
+ }
16
+
17
+ public get pendingMessage() {
18
+ return this._pendingMessage;
19
+ }
20
+
21
+ push(event: GenerationStreamEvent) {
22
+ switch (event.type) {
23
+ case 'message_start':
24
+ this._pendingMessage = event.message;
25
+ break;
26
+
27
+ case 'message_stop':
28
+ if (this._pendingMessage) {
29
+ this._messages.push(this._pendingMessage);
30
+ this._pendingMessage = undefined;
31
+ }
32
+ break;
33
+
34
+ case 'content_block_start':
35
+ if (this._pendingMessage) {
36
+ // Ensure the content array exists
37
+ this._pendingMessage.content = this._pendingMessage.content || [];
38
+ // Add the new content block at the specified index
39
+ this._pendingMessage.content[event.index] = event.content;
40
+ }
41
+ break;
42
+
43
+ case 'content_block_delta':
44
+ if (this._pendingMessage && this._pendingMessage.content) {
45
+ const block = this._pendingMessage.content[event.index];
46
+ if (block && event.delta.type === 'text_delta' && block.type === 'text') {
47
+ block.text = (block.text || '') + event.delta.text;
48
+ }
49
+ }
50
+ break;
51
+
52
+ case 'content_block_stop':
53
+ // Mark the block as no longer pending if needed
54
+ if (this._pendingMessage && this._pendingMessage.content) {
55
+ const block = this._pendingMessage.content[event.index];
56
+ if (block) {
57
+ block.pending = false;
58
+ }
59
+ }
60
+ break;
61
+
62
+ case 'message_delta':
63
+ // Apply any delta updates to the message
64
+ if (this._pendingMessage && event.delta) {
65
+ Object.assign(this._pendingMessage, event.delta);
66
+ }
67
+ break;
68
+ }
69
+ }
70
+
71
+ pushMessage(message: Message) {
72
+ this._messages.push(message);
73
+ }
74
+ }
75
+
76
+ export function* emitMessageAsEvents(message: Message): Iterable<GenerationStreamEvent> {
77
+ yield {
78
+ type: 'message_start',
79
+ message,
80
+ };
81
+ yield {
82
+ type: 'message_stop',
83
+ };
84
+ }