@dxos/assistant 0.8.3 → 0.8.4-main.f9ba587

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 (62) hide show
  1. package/dist/lib/browser/index.mjs +456 -595
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +456 -595
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/blueprint/blueprint.d.ts +33 -47
  8. package/dist/types/src/blueprint/blueprint.d.ts.map +1 -1
  9. package/dist/types/src/blueprint/index.d.ts +1 -2
  10. package/dist/types/src/blueprint/index.d.ts.map +1 -1
  11. package/dist/types/src/blueprint/registry.d.ts +7 -0
  12. package/dist/types/src/blueprint/registry.d.ts.map +1 -0
  13. package/dist/types/src/context/binder.d.ts +34 -0
  14. package/dist/types/src/context/binder.d.ts.map +1 -0
  15. package/dist/types/src/context/binding.d.ts +38 -0
  16. package/dist/types/src/context/binding.d.ts.map +1 -0
  17. package/dist/types/src/context/index.d.ts +3 -0
  18. package/dist/types/src/context/index.d.ts.map +1 -0
  19. package/dist/types/src/conversation/conversation.d.ts +44 -0
  20. package/dist/types/src/conversation/conversation.d.ts.map +1 -0
  21. package/dist/types/src/conversation/index.d.ts +2 -0
  22. package/dist/types/src/conversation/index.d.ts.map +1 -0
  23. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +1 -1
  24. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +10 -10
  25. package/dist/types/src/index.d.ts +4 -1
  26. package/dist/types/src/index.d.ts.map +1 -1
  27. package/dist/types/src/research/research.d.ts.map +1 -1
  28. package/dist/types/src/session/session.d.ts +25 -3
  29. package/dist/types/src/session/session.d.ts.map +1 -1
  30. package/dist/types/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +21 -30
  32. package/src/blueprint/blueprint.ts +57 -57
  33. package/src/blueprint/index.ts +1 -2
  34. package/src/blueprint/registry.ts +15 -0
  35. package/src/context/binder.ts +101 -0
  36. package/src/context/binding.ts +31 -0
  37. package/src/context/index.ts +6 -0
  38. package/src/conversation/conversation.ts +103 -0
  39. package/src/conversation/index.ts +5 -0
  40. package/src/extraction/extraction-llm-function/extraction-llm-function.ts +2 -1
  41. package/src/index.ts +4 -1
  42. package/src/research/research.ts +3 -2
  43. package/src/session/session.test.ts +48 -2
  44. package/src/session/session.ts +51 -18
  45. package/dist/lib/node/index.cjs +0 -2178
  46. package/dist/lib/node/index.cjs.map +0 -7
  47. package/dist/lib/node/meta.json +0 -1
  48. package/dist/types/src/blueprint/ai.test.d.ts +0 -2
  49. package/dist/types/src/blueprint/ai.test.d.ts.map +0 -1
  50. package/dist/types/src/blueprint/blueprint.test.d.ts +0 -2
  51. package/dist/types/src/blueprint/blueprint.test.d.ts.map +0 -1
  52. package/dist/types/src/blueprint/logger.d.ts +0 -25
  53. package/dist/types/src/blueprint/logger.d.ts.map +0 -1
  54. package/dist/types/src/blueprint/machine.d.ts +0 -143
  55. package/dist/types/src/blueprint/machine.d.ts.map +0 -1
  56. package/dist/types/src/blueprint/test-data.d.ts +0 -3
  57. package/dist/types/src/blueprint/test-data.d.ts.map +0 -1
  58. package/src/blueprint/ai.test.ts +0 -156
  59. package/src/blueprint/blueprint.test.ts +0 -151
  60. package/src/blueprint/logger.ts +0 -155
  61. package/src/blueprint/machine.ts +0 -251
  62. package/src/blueprint/test-data.ts +0 -55
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/assistant",
3
- "version": "0.8.3",
3
+ "version": "0.8.4-main.f9ba587",
4
4
  "description": "Natural language interface to DXOS",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -25,37 +25,28 @@
25
25
  "@xenova/transformers": "^2.17.2",
26
26
  "chalk": "^4.1.0",
27
27
  "exa-js": "^1.5.12",
28
- "@dxos/ai": "0.8.3",
29
- "@dxos/artifact": "0.8.3",
30
- "@dxos/context": "0.8.3",
31
- "@dxos/async": "0.8.3",
32
- "@dxos/debug": "0.8.3",
33
- "@dxos/echo": "0.8.3",
34
- "@dxos/echo-db": "0.8.3",
35
- "@dxos/echo-protocol": "0.8.3",
36
- "@dxos/echo-schema": "0.8.3",
37
- "@dxos/functions": "0.8.3",
38
- "@dxos/graph": "0.8.3",
39
- "@dxos/effect": "0.8.3",
40
- "@dxos/keys": "0.8.3",
41
- "@dxos/invariant": "0.8.3",
42
- "@dxos/live-object": "0.8.3",
43
- "@dxos/log": "0.8.3",
44
- "@dxos/node-std": "0.8.3",
45
- "@dxos/util": "0.8.3",
46
- "@dxos/schema": "0.8.3"
28
+ "@dxos/ai": "0.8.4-main.f9ba587",
29
+ "@dxos/artifact": "0.8.4-main.f9ba587",
30
+ "@dxos/async": "0.8.4-main.f9ba587",
31
+ "@dxos/context": "0.8.4-main.f9ba587",
32
+ "@dxos/debug": "0.8.4-main.f9ba587",
33
+ "@dxos/echo-db": "0.8.4-main.f9ba587",
34
+ "@dxos/echo-protocol": "0.8.4-main.f9ba587",
35
+ "@dxos/echo-schema": "0.8.4-main.f9ba587",
36
+ "@dxos/effect": "0.8.4-main.f9ba587",
37
+ "@dxos/graph": "0.8.4-main.f9ba587",
38
+ "@dxos/functions": "0.8.4-main.f9ba587",
39
+ "@dxos/echo": "0.8.4-main.f9ba587",
40
+ "@dxos/invariant": "0.8.4-main.f9ba587",
41
+ "@dxos/live-object": "0.8.4-main.f9ba587",
42
+ "@dxos/keys": "0.8.4-main.f9ba587",
43
+ "@dxos/log": "0.8.4-main.f9ba587",
44
+ "@dxos/schema": "0.8.4-main.f9ba587",
45
+ "@dxos/node-std": "0.8.4-main.f9ba587",
46
+ "@dxos/util": "0.8.4-main.f9ba587"
47
47
  },
48
48
  "devDependencies": {
49
- "@effect/ai": "^0.19.4",
50
- "@effect/ai-anthropic": "^0.9.5",
51
- "@effect/ai-openai": "^0.22.4",
52
- "@effect/cluster": "^0.39.4",
53
- "@effect/experimental": "^0.49.2",
54
- "@effect/platform": "^0.85.2",
55
- "@effect/platform-node": "^0.86.4",
56
- "@effect/rpc": "^0.62.4",
57
- "@effect/sql": "^0.38.2",
58
- "effect": "3.14.21",
49
+ "effect": "3.16.13",
59
50
  "inquirer": "^12.0.0"
60
51
  },
61
52
  "peerDependencies": {
@@ -4,72 +4,72 @@
4
4
 
5
5
  import { Schema } from 'effect';
6
6
 
7
- import { Key, Obj, Type } from '@dxos/echo';
7
+ import { ToolId } from '@dxos/ai';
8
+ import { Type } from '@dxos/echo';
9
+ import { LabelAnnotation } from '@dxos/echo-schema';
8
10
 
9
- export const BlueprintStep = Schema.Struct({
10
- id: Key.ObjectId,
11
- instructions: Schema.String,
12
- tools: Schema.optional(Schema.Array(Schema.String)),
13
- });
14
- export interface BlueprintStep extends Schema.Schema.Type<typeof BlueprintStep> {}
11
+ /**
12
+ * Blueprint schema defines the structure for AI assistant blueprints.
13
+ * Blueprints contain instructions, tools, and artifacts that guide the AI's behavior.
14
+ */
15
+ export const Blueprint = Schema.Struct({
16
+ /**
17
+ * Global registry ID.
18
+ * NOTE: Once a blueprint has been cloned from the registry, its stored definition may converge,
19
+ * but the `key` property refers to the original registry entry.
20
+ */
21
+ // TODO(burdon): Create Format type.
22
+ key: Schema.String.annotations({
23
+ description: 'Unique registration key for the blueprint',
24
+ }),
15
25
 
16
- export const BlueprintDefinition = Schema.Struct({
17
- steps: Schema.Array(BlueprintStep.pipe(Schema.omit('id'))),
18
- });
19
- export interface BlueprintDefinition extends Schema.Schema.Type<typeof BlueprintDefinition> {}
26
+ /**
27
+ * Human-readable name of the blueprint.
28
+ */
29
+ name: Schema.String.annotations({
30
+ description: 'Human-readable name of the blueprint',
31
+ }),
20
32
 
21
- export const Blueprint = Schema.Struct({
22
- name: Schema.optional(Schema.String),
23
- steps: Schema.Array(BlueprintStep),
33
+ /**
34
+ * Description of the blueprint's purpose and functionality.
35
+ */
36
+ description: Schema.optional(Schema.String).annotations({
37
+ description: "Description of the blueprint's purpose and functionality",
38
+ }),
39
+
40
+ /**
41
+ * Instructions that guide the AI assistant's behavior and responses.
42
+ * These are system prompts or guidelines that the AI should follow.
43
+ */
44
+ instructions: Schema.String.annotations({
45
+ description: "Instructions that guide the AI assistant's behavior and responses",
46
+ }),
47
+
48
+ /**
49
+ * Array of tools that the AI assistant can use when this blueprint is active.
50
+ */
51
+ tools: Schema.Array(ToolId).annotations({
52
+ description: 'Array of tools that the AI assistant can use when this blueprint is active',
53
+ }),
54
+
55
+ /**
56
+ * Array of artifacts that the AI assistant can create or modify.
57
+ */
58
+ artifacts: Schema.Array(Schema.String).annotations({
59
+ description: 'Ids of artifact definitions that should be pulled into the blueprint',
60
+ }),
24
61
  }).pipe(
25
62
  Type.Obj({
63
+ // TODO(burdon): Is this a DXN? Need to create a Format type for these IDs.
26
64
  typename: 'dxos.org/type/Blueprint',
27
65
  version: '0.1.0',
28
66
  }),
29
- );
30
- export interface Blueprint extends Schema.Schema.Type<typeof Blueprint> {}
31
-
32
- /**
33
- * Blueprint builder API.
34
- */
35
- export namespace BlueprintBuilder {
36
- export const create = () => new Builder();
37
-
38
- class Builder {
39
- private readonly _steps: BlueprintStep[] = [];
40
67
 
41
- step(instructions: string, options?: { tools?: string[] }): Builder {
42
- this._steps.push({
43
- id: Key.ObjectId.random(),
44
- instructions,
45
- tools: options?.tools ?? [],
46
- });
47
-
48
- return this;
49
- }
50
-
51
- build(): Blueprint {
52
- return Obj.make(Blueprint, { steps: this._steps });
53
- }
54
- }
55
- }
68
+ // TODO(burdon): Move to main API.
69
+ LabelAnnotation.set(['name']),
70
+ );
56
71
 
57
72
  /**
58
- * Blueprint parser API.
73
+ * TypeScript type for Blueprint.
59
74
  */
60
- export namespace BlueprintParser {
61
- export const create = () => new Parser();
62
-
63
- class Parser {
64
- parse({ steps }: BlueprintDefinition): Blueprint {
65
- const builder = BlueprintBuilder.create();
66
- for (const step of steps) {
67
- builder.step(step.instructions, {
68
- tools: (step.tools ?? []) as string[],
69
- });
70
- }
71
-
72
- return builder.build();
73
- }
74
- }
75
- }
75
+ export interface Blueprint extends Schema.Schema.Type<typeof Blueprint> {}
@@ -3,5 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './blueprint';
6
- export * from './machine';
7
- export * from './logger';
6
+ export * from './registry';
@@ -0,0 +1,15 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Blueprint } from './blueprint';
6
+
7
+ export class BlueprintRegistry {
8
+ constructor(private readonly _blueprints: Blueprint[]) {
9
+ this._blueprints.sort(({ name: a }, { name: b }) => a.localeCompare(b));
10
+ }
11
+
12
+ query(): Blueprint[] {
13
+ return this._blueprints;
14
+ }
15
+ }
@@ -0,0 +1,101 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { computed, type ReadonlySignal } from '@preact/signals-core';
6
+ import { Array, pipe } from 'effect';
7
+
8
+ import { Obj, type Type, type Ref, type Relation } from '@dxos/echo';
9
+ import { type Queue } from '@dxos/echo-db';
10
+ import { ComplexSet } from '@dxos/util';
11
+
12
+ import { ContextBinding } from './binding';
13
+ import { type Blueprint } from '../blueprint';
14
+
15
+ /**
16
+ * Manages bindings of blueprints and objects to a conversation.
17
+ */
18
+ export class ContextBinder {
19
+ constructor(private readonly _queue: Queue) {}
20
+
21
+ /**
22
+ * Reactive query of all bindings.
23
+ */
24
+ // TODO(burdon): Cache value?
25
+ readonly bindings: ReadonlySignal<Bindings> = computed(() => this._reduce(this._queue.objects));
26
+
27
+ // TODO(burdon): load refs?
28
+ readonly blueprints: ReadonlySignal<Ref.Ref<Blueprint>[]> = computed(() => [...this.bindings.value.blueprints]);
29
+ readonly objects: ReadonlySignal<Ref.Ref<Type.Expando>[]> = computed(() => [...this.bindings.value.objects]);
30
+
31
+ /**
32
+ * Asynchronous query of all bindings.
33
+ */
34
+ async query(): Promise<Bindings> {
35
+ const queueItems = await this._queue.queryObjects();
36
+ return this._reduce(queueItems);
37
+ }
38
+
39
+ // TODO(burdon): Pass in Blueprint obj (from registry?) and create reference.
40
+ async bind(props: BindingProps): Promise<void> {
41
+ if (!props.blueprints?.length && !props.objects?.length) {
42
+ return;
43
+ }
44
+
45
+ await this._queue.append([
46
+ Obj.make(ContextBinding, {
47
+ blueprints: {
48
+ added: props.blueprints ?? [],
49
+ removed: [],
50
+ },
51
+ objects: {
52
+ added: props.objects ?? [],
53
+ removed: [],
54
+ },
55
+ }),
56
+ ]);
57
+ }
58
+
59
+ async unbind(props: BindingProps): Promise<void> {
60
+ if (!props.blueprints?.length && !props.objects?.length) {
61
+ return;
62
+ }
63
+
64
+ await this._queue.append([
65
+ Obj.make(ContextBinding, {
66
+ blueprints: {
67
+ added: [],
68
+ removed: props.blueprints ?? [],
69
+ },
70
+ objects: {
71
+ added: [],
72
+ removed: props.objects ?? [],
73
+ },
74
+ }),
75
+ ]);
76
+ }
77
+
78
+ private _reduce(items: (Obj.Any | Relation.Any)[]): Bindings {
79
+ return pipe(
80
+ items,
81
+ Array.filter(Obj.instanceOf(ContextBinding)),
82
+ Array.reduce(new Bindings(), (context, item) => {
83
+ item.blueprints.removed.forEach((item) => context.blueprints.delete(item));
84
+ item.blueprints.added.forEach((item) => context.blueprints.add(item));
85
+ item.objects.removed.forEach((item) => context.objects.delete(item));
86
+ item.objects.added.forEach((item) => context.objects.add(item));
87
+ return context;
88
+ }),
89
+ );
90
+ }
91
+ }
92
+
93
+ export type BindingProps = Partial<{
94
+ blueprints: Ref.Ref<Blueprint>[];
95
+ objects: Ref.Ref<Type.Expando>[];
96
+ }>;
97
+
98
+ export class Bindings {
99
+ blueprints = new ComplexSet<Ref.Ref<Blueprint>>((ref) => ref.dxn.toString());
100
+ objects = new ComplexSet<Ref.Ref<Type.Expando>>((ref) => ref.dxn.toString());
101
+ }
@@ -0,0 +1,31 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Schema } from 'effect';
6
+
7
+ import { Type } from '@dxos/echo';
8
+
9
+ import { Blueprint } from '../blueprint';
10
+ /**
11
+ * Thread message that binds or unbinds contextual objects to a conversation.
12
+ */
13
+ export const ContextBinding = Schema.Struct({
14
+ blueprints: Schema.Struct({
15
+ added: Schema.Array(Type.Ref(Blueprint)),
16
+ removed: Schema.Array(Type.Ref(Blueprint)),
17
+ }),
18
+
19
+ // TODO(burdon): Type.Expando => Type.Obj (or Obj.Any?)
20
+ objects: Schema.Struct({
21
+ added: Schema.Array(Type.Ref(Type.Expando)),
22
+ removed: Schema.Array(Type.Ref(Type.Expando)),
23
+ }),
24
+ }).pipe(
25
+ Type.Obj({
26
+ typename: 'dxos.org/type/ContextBinding',
27
+ version: '0.1.0',
28
+ }),
29
+ );
30
+
31
+ export interface ContextBinding extends Schema.Schema.Type<typeof ContextBinding> {}
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './binder';
6
+ export * from './binding';
@@ -0,0 +1,103 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Message, type ExecutableTool } from '@dxos/ai';
6
+ import { type ArtifactDefinition } from '@dxos/artifact';
7
+ import { Event } from '@dxos/async';
8
+ import { Obj, Ref } from '@dxos/echo';
9
+ import { type Queue } from '@dxos/echo-db';
10
+ import { AiService, ToolResolverService, type ServiceContainer } from '@dxos/functions';
11
+ import { log } from '@dxos/log';
12
+ import { isNonNullable } from '@dxos/util';
13
+
14
+ import { ContextBinder, type ContextBinding } from '../context';
15
+ import { AISession, type SessionRunOptions } from '../session';
16
+
17
+ export interface ConversationRunOptions {
18
+ systemPrompt?: string;
19
+ prompt: string;
20
+
21
+ tools?: ExecutableTool[];
22
+ artifacts?: ArtifactDefinition[];
23
+ extensions?: ToolContextExtensions;
24
+ generationOptions?: SessionRunOptions['generationOptions'];
25
+
26
+ /** @depreacated Should be managed by the conversation. */
27
+ requiredArtifactIds?: string[];
28
+
29
+ // TODO(dmaretskyi): Move into conversation.
30
+ artifactDiffResolver?: SessionRunOptions['artifactDiffResolver'];
31
+ }
32
+
33
+ export type ConversationOptions = {
34
+ serviceContainer: ServiceContainer;
35
+ queue: Queue<Message | ContextBinding>;
36
+ };
37
+
38
+ /**
39
+ * Persistent conversation state.
40
+ * Context + history + artifacts.
41
+ * Backed by a Queue.
42
+ */
43
+ export class Conversation {
44
+ private readonly _serviceContainer: ServiceContainer;
45
+ private readonly _queue: Queue<Message | ContextBinding>;
46
+
47
+ /**
48
+ * Fired when the execution loop begins.
49
+ * This is called before the first message is sent.
50
+ */
51
+ public readonly onBegin = new Event<AISession>();
52
+
53
+ /**
54
+ * Blueprints bound to the conversation.
55
+ */
56
+ public readonly context: ContextBinder;
57
+
58
+ constructor(options: ConversationOptions) {
59
+ this._serviceContainer = options.serviceContainer;
60
+ this._queue = options.queue;
61
+ this.context = new ContextBinder(this._queue);
62
+ }
63
+
64
+ async run(options: ConversationRunOptions): Promise<Message[]> {
65
+ const session = new AISession({ operationModel: 'configured' });
66
+ this.onBegin.emit(session);
67
+
68
+ const history = await this.getHistory();
69
+ const context = await this.context.query();
70
+ const blueprints = (await Ref.Array.loadAll([...context.blueprints])).filter(isNonNullable);
71
+ if (blueprints.length > 1) {
72
+ log.warn('multiple blueprints are not yet supported');
73
+ }
74
+
75
+ const messages = await session.run({
76
+ history,
77
+ prompt: options.prompt,
78
+ systemPrompt: (options.systemPrompt ?? '') + (blueprints.at(0)?.instructions ?? ''),
79
+
80
+ // TODO(dmaretskyi): Artifacts come from the blueprint?
81
+ artifacts: options.artifacts ?? [],
82
+ tools: blueprints.at(0)?.tools ?? [],
83
+ executableTools: options.tools,
84
+ requiredArtifactIds: options.requiredArtifactIds,
85
+ client: this._serviceContainer.getService(AiService).client,
86
+ toolResolver: this._serviceContainer.getService(ToolResolverService).toolResolver,
87
+ extensions: {
88
+ serviceContainer: this._serviceContainer,
89
+ ...options.extensions,
90
+ },
91
+ generationOptions: options.generationOptions,
92
+ });
93
+
94
+ await this._queue.append(messages);
95
+
96
+ return messages;
97
+ }
98
+
99
+ async getHistory(): Promise<Message[]> {
100
+ const queueItems = await this._queue.queryObjects();
101
+ return queueItems.filter(Obj.instanceOf(Message));
102
+ }
103
+ }
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './conversation';
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Message } from '@dxos/ai';
5
+ import { Message, ToolRegistry } from '@dxos/ai';
6
6
  import { Obj } from '@dxos/echo';
7
7
  import { create } from '@dxos/echo-schema';
8
8
  import { AiService, defineFunction, type FunctionDefinition } from '@dxos/functions';
@@ -45,6 +45,7 @@ export const extractionAnthropicFn: FunctionDefinition<ExtractionInput, Extracti
45
45
  artifacts: [],
46
46
  prompt: '',
47
47
  tools: [],
48
+ toolResolver: new ToolRegistry([]),
48
49
  });
49
50
 
50
51
  return {
package/src/index.ts CHANGED
@@ -2,8 +2,11 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ export * from './blueprint';
6
+ export * from './context';
7
+ export * from './conversation';
5
8
  export * from './extraction';
6
9
  export * from './research';
7
10
  export * from './session';
8
11
  export * from './types';
9
- export * from './blueprint';
12
+ export * from './conversation';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { Schema } from 'effect';
6
6
 
7
- import { AgentStatusReport, ConsolePrinter } from '@dxos/ai';
7
+ import { AgentStatusReport, ConsolePrinter, ToolRegistry } from '@dxos/ai';
8
8
  import { create } from '@dxos/echo-schema';
9
9
  import { AiService, CredentialsService, DatabaseService, defineFunction, TracingService } from '@dxos/functions';
10
10
  import { log } from '@dxos/log';
@@ -68,9 +68,10 @@ export const researchFn = defineFunction({
68
68
  client: ai.client,
69
69
  systemPrompt: PROMPT,
70
70
  artifacts: [],
71
- tools: [searchTool, graphWriteTool],
71
+ tools: [searchTool.id, graphWriteTool.id],
72
72
  history: [],
73
73
  prompt: query,
74
+ toolResolver: new ToolRegistry([searchTool, graphWriteTool]),
74
75
  });
75
76
 
76
77
  return {
@@ -5,7 +5,7 @@
5
5
  import { Schema } from 'effect';
6
6
  import { describe, test } from 'vitest';
7
7
 
8
- import { EdgeAiServiceClient, ConsolePrinter, createTool, ToolResult } from '@dxos/ai';
8
+ import { EdgeAiServiceClient, ConsolePrinter, createTool, ToolResult, ToolRegistry } from '@dxos/ai';
9
9
  import { AI_SERVICE_ENDPOINT } from '@dxos/ai/testing';
10
10
  import { ArtifactId, defineArtifact } from '@dxos/artifact';
11
11
  import { Type, Obj } from '@dxos/echo';
@@ -31,7 +31,52 @@ const CalendarEventSchema = Schema.Struct({
31
31
  type CalendarEvent = Schema.Schema.Type<typeof CalendarEventSchema>;
32
32
 
33
33
  // TODO(burdon): Flaky.
34
- describe.skip('AISession with Ollama', () => {
34
+ describe.runIf(process.env.DX_RUN_SLOW_TESTS === '1')('AISession with Ollama', () => {
35
+ test('tool', async () => {
36
+ const aiClient = new EdgeAiServiceClient({ endpoint: AI_SERVICE_ENDPOINT.REMOTE });
37
+ // const aiClient = new OllamaAiServiceClient({
38
+ // overrides: { model: 'llama3.1:8b' },
39
+ // });
40
+ const session = new AISession({ operationModel: 'configured' });
41
+
42
+ const printer = new ConsolePrinter();
43
+ session.message.on((message) => printer.printMessage(message));
44
+ session.userMessage.on((message) => printer.printMessage(message));
45
+ session.block.on((block) => printer.printContentBlock(block));
46
+
47
+ // session.update.on((update) => {
48
+ // log('update', { update });
49
+ // });
50
+
51
+ const calculatorTool = createTool('test', {
52
+ name: 'calculator',
53
+ description: 'Adds to numbers',
54
+ schema: Schema.Struct({
55
+ operand1: Schema.Number,
56
+ operand2: Schema.Number,
57
+ }),
58
+ execute: async ({ operand1, operand2 }) => {
59
+ return ToolResult.Success(operand1 + operand2);
60
+ },
61
+ });
62
+
63
+ // Test creating an itinerary
64
+ const response = await session.run({
65
+ client: aiClient,
66
+ tools: [calculatorTool.id],
67
+ artifacts: [],
68
+ requiredArtifactIds: [],
69
+ history: [],
70
+ generationOptions: {
71
+ model: '@anthropic/claude-3-5-haiku-20241022',
72
+ },
73
+ prompt: 'What is 10 + 20?',
74
+ toolResolver: new ToolRegistry([calculatorTool]),
75
+ });
76
+
77
+ log('result', { response });
78
+ });
79
+
35
80
  test('create calendar itinerary', { timeout: 60_000 }, async () => {
36
81
  const aiClient = new EdgeAiServiceClient({ endpoint: AI_SERVICE_ENDPOINT.REMOTE });
37
82
  // const aiClient = new OllamaAiServiceClient({
@@ -145,6 +190,7 @@ describe.skip('AISession with Ollama', () => {
145
190
  model: '@anthropic/claude-3-5-haiku-20241022',
146
191
  },
147
192
  prompt: 'create a table and map for a travel itinerary based on events in my calendar',
193
+ toolResolver: new ToolRegistry([]),
148
194
  });
149
195
 
150
196
  log('result', {