@dxos/assistant 0.8.4-main.937b3ca → 0.8.4-main.9be5663bfe
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/README.md +1 -0
- package/dist/lib/neutral/chunk-UAWIXKF6.mjs +1875 -0
- package/dist/lib/neutral/chunk-UAWIXKF6.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/extraction/index.mjs +26 -32
- package/dist/lib/neutral/extraction/index.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/index.mjs +73 -372
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/testing/index.mjs +114 -0
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/conversation/context.d.ts +46 -20
- package/dist/types/src/conversation/context.d.ts.map +1 -1
- package/dist/types/src/conversation/conversation.d.ts +37 -10
- package/dist/types/src/conversation/conversation.d.ts.map +1 -1
- package/dist/types/src/diff.d.ts +3 -1
- package/dist/types/src/diff.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction-ner-function/index.d.ts +0 -1
- package/dist/types/src/extraction/extraction-ner-function/index.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction.d.ts +933 -221
- package/dist/types/src/extraction/extraction.d.ts.map +1 -1
- package/dist/types/src/extraction/index.d.ts +0 -1
- package/dist/types/src/extraction/index.d.ts.map +1 -1
- package/dist/types/src/functions/services.d.ts +27 -8
- package/dist/types/src/functions/services.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/service/AgentService.d.ts +61 -0
- package/dist/types/src/service/AgentService.d.ts.map +1 -0
- package/dist/types/src/service/AgentService.test.d.ts +2 -0
- package/dist/types/src/service/AgentService.test.d.ts.map +1 -0
- package/dist/types/src/service/agent-process.d.ts +17 -0
- package/dist/types/src/service/agent-process.d.ts.map +1 -0
- package/dist/types/src/service/index.d.ts +3 -0
- package/dist/types/src/service/index.d.ts.map +1 -0
- package/dist/types/src/session/format.d.ts.map +1 -1
- package/dist/types/src/session/index.d.ts +1 -1
- package/dist/types/src/session/index.d.ts.map +1 -1
- package/dist/types/src/session/observer.d.ts.map +1 -1
- package/dist/types/src/session/session.d.ts +64 -8
- package/dist/types/src/session/session.d.ts.map +1 -1
- package/dist/types/src/session/toolkit.d.ts +6 -2
- package/dist/types/src/session/toolkit.d.ts.map +1 -1
- package/dist/types/src/templates/system.d.ts +0 -1
- package/dist/types/src/templates/system.d.ts.map +1 -1
- package/dist/types/src/testing/layer.d.ts +23 -9
- package/dist/types/src/testing/layer.d.ts.map +1 -1
- package/dist/types/src/tracing.d.ts +264 -0
- package/dist/types/src/tracing.d.ts.map +1 -0
- package/dist/types/src/types/artifact.d.ts +5 -5
- package/dist/types/src/types/artifact.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +33 -33
- package/src/conversation/context.test.ts +19 -13
- package/src/conversation/context.ts +150 -50
- package/src/conversation/conversation.test.ts +25 -32
- package/src/conversation/conversation.ts +197 -47
- package/src/diff.test.ts +36 -5
- package/src/diff.ts +13 -4
- package/src/execution-graph.test.ts +2 -2
- package/src/execution-graph.ts +3 -3
- package/src/extraction/extraction-ner-function/index.ts +0 -1
- package/src/extraction/extraction.ts +38 -10
- package/src/extraction/index.ts +0 -1
- package/src/functions/services.ts +199 -50
- package/src/functions.conversations.json +1 -1
- package/src/functions.test.ts +28 -20
- package/src/index.ts +2 -0
- package/src/service/AgentService.conversations.json +1 -0
- package/src/service/AgentService.test.ts +169 -0
- package/src/service/AgentService.ts +165 -0
- package/src/service/agent-process.ts +359 -0
- package/src/service/index.ts +6 -0
- package/src/session/format.test.ts +23 -15
- package/src/session/format.ts +0 -1
- package/src/session/index.ts +1 -2
- package/src/session/observer.ts +3 -2
- package/src/session/session.conversations.json +1 -1
- package/src/session/session.test.ts +52 -5
- package/src/session/session.ts +232 -117
- package/src/session/toolkit.ts +12 -7
- package/src/templates/instructions/format.tpl +0 -4
- package/src/templates/instructions/system.tpl +39 -2
- package/src/templates/system.ts +6 -5
- package/src/testing/layer.ts +205 -24
- package/src/tracing.ts +113 -0
- package/src/types/artifact.ts +17 -10
- package/dist/lib/browser/chunk-7CDDG6LL.mjs +0 -578
- package/dist/lib/browser/chunk-7CDDG6LL.mjs.map +0 -7
- package/dist/lib/browser/extraction/index.mjs.map +0 -7
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs +0 -32
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CC7ER6Q3.mjs +0 -579
- package/dist/lib/node-esm/chunk-CC7ER6Q3.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/extraction/index.mjs +0 -226
- package/dist/lib/node-esm/extraction/index.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -851
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -33
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +0 -4
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts +0 -2
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-llm-function/index.d.ts +0 -2
- package/dist/types/src/extraction/extraction-llm-function/index.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +0 -347
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts +0 -2
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts.map +0 -1
- package/dist/types/src/session/GenericToolkit.d.ts +0 -85
- package/dist/types/src/session/GenericToolkit.d.ts.map +0 -1
- package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +0 -84
- package/src/extraction/extraction-llm-function/extraction-llm-function.ts +0 -62
- package/src/extraction/extraction-llm-function/index.ts +0 -5
- package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +0 -116
- package/src/extraction/extraction-ner-function/extraction-ner-function.ts +0 -56
- package/src/session/GenericToolkit.ts +0 -131
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/assistant",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.9be5663bfe",
|
|
4
4
|
"description": "Natural language interface to DXOS",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -16,20 +16,17 @@
|
|
|
16
16
|
".": {
|
|
17
17
|
"source": "./src/index.ts",
|
|
18
18
|
"types": "./dist/types/src/index.d.ts",
|
|
19
|
-
"
|
|
20
|
-
"node": "./dist/lib/node-esm/index.mjs"
|
|
19
|
+
"default": "./dist/lib/neutral/index.mjs"
|
|
21
20
|
},
|
|
22
21
|
"./extraction": {
|
|
23
22
|
"source": "./src/extraction/index.ts",
|
|
24
23
|
"types": "./dist/types/src/extraction/index.d.ts",
|
|
25
|
-
"
|
|
26
|
-
"node": "./dist/lib/node-esm/extraction/index.mjs"
|
|
24
|
+
"default": "./dist/lib/neutral/extraction/index.mjs"
|
|
27
25
|
},
|
|
28
26
|
"./testing": {
|
|
29
27
|
"source": "./src/testing/index.ts",
|
|
30
28
|
"types": "./dist/types/src/testing/index.d.ts",
|
|
31
|
-
"
|
|
32
|
-
"node": "./dist/lib/node-esm/testing/index.mjs"
|
|
29
|
+
"default": "./dist/lib/neutral/testing/index.mjs"
|
|
33
30
|
}
|
|
34
31
|
},
|
|
35
32
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -49,39 +46,42 @@
|
|
|
49
46
|
],
|
|
50
47
|
"dependencies": {
|
|
51
48
|
"@automerge/automerge": "3.2.3",
|
|
52
|
-
"@effect-atom/atom
|
|
53
|
-
"@effect/
|
|
54
|
-
"@effect/
|
|
55
|
-
"@effect/
|
|
49
|
+
"@effect-atom/atom": "^0.5.1",
|
|
50
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
51
|
+
"@effect/ai": "0.33.2",
|
|
52
|
+
"@effect/experimental": "0.58.0",
|
|
53
|
+
"@effect/platform": "0.94.4",
|
|
56
54
|
"@xenova/transformers": "^2.17.2",
|
|
57
55
|
"lodash.defaultsdeep": "^4.6.1",
|
|
58
|
-
"@dxos/ai": "0.8.4-main.
|
|
59
|
-
"@dxos/
|
|
60
|
-
"@dxos/
|
|
61
|
-
"@dxos/
|
|
62
|
-
"@dxos/
|
|
63
|
-
"@dxos/echo": "0.8.4-main.
|
|
64
|
-
"@dxos/echo
|
|
65
|
-
"@dxos/
|
|
66
|
-
"@dxos/
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/
|
|
73
|
-
"@dxos/log": "0.8.4-main.
|
|
74
|
-
"@dxos/
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
56
|
+
"@dxos/ai": "0.8.4-main.9be5663bfe",
|
|
57
|
+
"@dxos/async": "0.8.4-main.9be5663bfe",
|
|
58
|
+
"@dxos/debug": "0.8.4-main.9be5663bfe",
|
|
59
|
+
"@dxos/echo-db": "0.8.4-main.9be5663bfe",
|
|
60
|
+
"@dxos/context": "0.8.4-main.9be5663bfe",
|
|
61
|
+
"@dxos/echo-protocol": "0.8.4-main.9be5663bfe",
|
|
62
|
+
"@dxos/echo": "0.8.4-main.9be5663bfe",
|
|
63
|
+
"@dxos/effect": "0.8.4-main.9be5663bfe",
|
|
64
|
+
"@dxos/functions": "0.8.4-main.9be5663bfe",
|
|
65
|
+
"@dxos/errors": "0.8.4-main.9be5663bfe",
|
|
66
|
+
"@dxos/functions-runtime": "0.8.4-main.9be5663bfe",
|
|
67
|
+
"@dxos/blueprints": "0.8.4-main.9be5663bfe",
|
|
68
|
+
"@dxos/invariant": "0.8.4-main.9be5663bfe",
|
|
69
|
+
"@dxos/graph": "0.8.4-main.9be5663bfe",
|
|
70
|
+
"@dxos/keys": "0.8.4-main.9be5663bfe",
|
|
71
|
+
"@dxos/log": "0.8.4-main.9be5663bfe",
|
|
72
|
+
"@dxos/node-std": "0.8.4-main.9be5663bfe",
|
|
73
|
+
"@dxos/mcp-client": "0.8.4-main.9be5663bfe",
|
|
74
|
+
"@dxos/operation": "0.8.4-main.9be5663bfe",
|
|
75
|
+
"@dxos/schema": "0.8.4-main.9be5663bfe",
|
|
76
|
+
"@dxos/types": "0.8.4-main.9be5663bfe",
|
|
77
|
+
"@dxos/util": "0.8.4-main.9be5663bfe"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@types/lodash.defaultsdeep": "^4.6.6",
|
|
81
|
-
"effect": "3.
|
|
81
|
+
"effect": "3.20.0"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"effect": "3.
|
|
84
|
+
"effect": "3.20.0"
|
|
85
85
|
},
|
|
86
86
|
"publishConfig": {
|
|
87
87
|
"access": "public"
|
|
@@ -2,35 +2,41 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import * as Context from 'effect/Context';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Layer from 'effect/Layer';
|
|
8
|
+
import * as Runtime from 'effect/Runtime';
|
|
5
9
|
import * as Schema from 'effect/Schema';
|
|
6
10
|
import { describe, it } from 'vitest';
|
|
7
11
|
|
|
8
|
-
import { Obj, Ref, Type } from '@dxos/echo';
|
|
9
|
-
import { type Queue } from '@dxos/echo-db';
|
|
12
|
+
import { Feed, Obj, Ref, Type } from '@dxos/echo';
|
|
10
13
|
|
|
11
14
|
import { AiContextBinder } from './context';
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
+
const createMockFeedRuntime = (): Runtime.Runtime<Feed.FeedService> => {
|
|
17
|
+
const mockFeedService: Context.Tag.Service<Feed.FeedService> = {
|
|
18
|
+
append: async () => {},
|
|
19
|
+
remove: async () => {},
|
|
16
20
|
query: () =>
|
|
17
21
|
({
|
|
18
|
-
subscribe: () => (
|
|
22
|
+
subscribe: () => () => {},
|
|
19
23
|
results: [],
|
|
24
|
+
run: async () => [],
|
|
20
25
|
}) as any,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
};
|
|
27
|
+
const layer = Layer.succeed(Feed.FeedService, mockFeedService);
|
|
28
|
+
return Effect.runSync(Effect.runtime<Feed.FeedService>().pipe(Effect.provide(layer)));
|
|
29
|
+
};
|
|
25
30
|
|
|
26
31
|
describe('AiContextBinder', () => {
|
|
27
32
|
it('should handle bind with Ref', async () => {
|
|
28
|
-
const
|
|
29
|
-
const
|
|
33
|
+
const feed = Feed.make();
|
|
34
|
+
const runtime = createMockFeedRuntime();
|
|
35
|
+
const binder = new AiContextBinder({ feed, runtime });
|
|
30
36
|
|
|
31
37
|
const TestSchema = Schema.Struct({}).pipe(
|
|
32
38
|
Type.object({
|
|
33
|
-
typename: 'dxos.
|
|
39
|
+
typename: 'org.dxos.type.example',
|
|
34
40
|
version: '0.1.0',
|
|
35
41
|
}),
|
|
36
42
|
);
|
|
@@ -5,15 +5,17 @@
|
|
|
5
5
|
import { Atom, Registry } from '@effect-atom/atom-react';
|
|
6
6
|
import * as EArray from 'effect/Array';
|
|
7
7
|
import * as Context from 'effect/Context';
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
8
9
|
import * as Function from 'effect/Function';
|
|
10
|
+
import * as Runtime from 'effect/Runtime';
|
|
9
11
|
import * as Schema from 'effect/Schema';
|
|
10
12
|
|
|
11
13
|
import { Blueprint } from '@dxos/blueprints';
|
|
12
14
|
import { Resource } from '@dxos/context';
|
|
13
|
-
import { DXN, Obj, Query,
|
|
14
|
-
import {
|
|
15
|
+
import { DXN, Feed, Obj, type QueryResult, Query, Ref, Type } from '@dxos/echo';
|
|
16
|
+
import { assertArgument } from '@dxos/invariant';
|
|
15
17
|
import { log } from '@dxos/log';
|
|
16
|
-
import { ComplexSet,
|
|
18
|
+
import { ComplexSet, isNonNullable } from '@dxos/util';
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Thread message that binds or unbinds contextual objects to a conversation.
|
|
@@ -21,17 +23,17 @@ import { ComplexSet, isTruthy } from '@dxos/util';
|
|
|
21
23
|
// TODO(burdon): Move to @dxos/schema ContentBlock?
|
|
22
24
|
export const ContextBinding = Schema.Struct({
|
|
23
25
|
blueprints: Schema.Struct({
|
|
24
|
-
added: Schema.Array(
|
|
25
|
-
removed: Schema.Array(
|
|
26
|
+
added: Schema.Array(Ref.Ref(Blueprint.Blueprint)),
|
|
27
|
+
removed: Schema.Array(Ref.Ref(Blueprint.Blueprint)),
|
|
26
28
|
}),
|
|
27
29
|
|
|
28
30
|
objects: Schema.Struct({
|
|
29
|
-
added: Schema.Array(
|
|
30
|
-
removed: Schema.Array(
|
|
31
|
+
added: Schema.Array(Ref.Ref(Obj.Unknown)),
|
|
32
|
+
removed: Schema.Array(Ref.Ref(Obj.Unknown)),
|
|
31
33
|
}),
|
|
32
34
|
}).pipe(
|
|
33
35
|
Type.object({
|
|
34
|
-
typename: 'dxos.
|
|
36
|
+
typename: 'org.dxos.type.contextBinding',
|
|
35
37
|
version: '0.1.0',
|
|
36
38
|
}),
|
|
37
39
|
);
|
|
@@ -58,7 +60,8 @@ export class Bindings {
|
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export type AiContextBinderOptions = {
|
|
61
|
-
|
|
63
|
+
feed: Feed.Feed;
|
|
64
|
+
runtime: Runtime.Runtime<Feed.FeedService>;
|
|
62
65
|
registry?: Registry.Registry;
|
|
63
66
|
};
|
|
64
67
|
|
|
@@ -70,11 +73,16 @@ export class AiContextBinder extends Resource {
|
|
|
70
73
|
private readonly _blueprints = Atom.make<Blueprint.Blueprint[]>([]).pipe(Atom.keepAlive);
|
|
71
74
|
private readonly _objects = Atom.make<Obj.Unknown[]>([]).pipe(Atom.keepAlive);
|
|
72
75
|
private readonly _registry: Registry.Registry;
|
|
73
|
-
private readonly
|
|
76
|
+
private readonly _feed: Feed.Feed;
|
|
77
|
+
private readonly _runtime: Runtime.Runtime<Feed.FeedService>;
|
|
78
|
+
#bindingsQuery: QueryResult.QueryResult<ContextBinding> | undefined;
|
|
74
79
|
|
|
75
80
|
constructor(options: AiContextBinderOptions) {
|
|
76
81
|
super();
|
|
77
|
-
|
|
82
|
+
assertArgument(options.feed, 'options.feed', 'Feed is required');
|
|
83
|
+
assertArgument(options.runtime, 'options.runtime', 'Feed runtime is required');
|
|
84
|
+
this._feed = options.feed;
|
|
85
|
+
this._runtime = options.runtime;
|
|
78
86
|
this._registry = options.registry ?? Registry.make();
|
|
79
87
|
}
|
|
80
88
|
|
|
@@ -121,20 +129,35 @@ export class AiContextBinder extends Resource {
|
|
|
121
129
|
}
|
|
122
130
|
|
|
123
131
|
protected override async _open(): Promise<void> {
|
|
124
|
-
|
|
132
|
+
this.#bindingsQuery = await Runtime.runPromise(this._runtime)(Feed.query(this._feed, Query.type(ContextBinding)));
|
|
125
133
|
|
|
126
134
|
// Process initial state before returning.
|
|
127
|
-
const initialResults = await
|
|
135
|
+
const initialResults = await this.#bindingsQuery.run();
|
|
128
136
|
await this._updateBindings(initialResults);
|
|
129
137
|
|
|
130
138
|
// Subscribe to future changes.
|
|
131
139
|
this._ctx.onDispose(
|
|
132
|
-
|
|
133
|
-
await this._updateBindings(
|
|
140
|
+
this.#bindingsQuery.subscribe(async () => {
|
|
141
|
+
await this._updateBindings(this.#bindingsQuery!.results);
|
|
134
142
|
}),
|
|
135
143
|
);
|
|
136
144
|
}
|
|
137
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Re-reads bindings from the feed to pick up changes made by other processes.
|
|
148
|
+
*/
|
|
149
|
+
async sync(): Promise<void> {
|
|
150
|
+
if (this.#bindingsQuery) {
|
|
151
|
+
const results = await this.#bindingsQuery.run();
|
|
152
|
+
log('sync', { bindingItems: results.length });
|
|
153
|
+
await this._updateBindings(results);
|
|
154
|
+
log('sync complete', {
|
|
155
|
+
blueprints: this._registry.get(this._blueprints).length,
|
|
156
|
+
blueprintKeys: this._registry.get(this._blueprints).map((bp) => (bp as any).key),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
138
161
|
private async _updateBindings(items: ContextBinding[]): Promise<void> {
|
|
139
162
|
// Skip update if no items - preserve existing state set by bind().
|
|
140
163
|
if (items.length === 0) {
|
|
@@ -143,19 +166,43 @@ export class AiContextBinder extends Resource {
|
|
|
143
166
|
|
|
144
167
|
const bindings = this._reduce(items);
|
|
145
168
|
|
|
169
|
+
log('_updateBindings', {
|
|
170
|
+
items: items.length,
|
|
171
|
+
blueprintRefs: [...bindings.blueprints].map((ref) => ({ dxn: ref.dxn.toString(), available: ref.isAvailable })),
|
|
172
|
+
});
|
|
173
|
+
|
|
146
174
|
// Resolve references (loading them first if needed).
|
|
147
175
|
const currentBlueprints = this._registry.get(this._blueprints);
|
|
148
176
|
const currentObjects = this._registry.get(this._objects);
|
|
149
|
-
const
|
|
150
|
-
const
|
|
177
|
+
const resolvedBlueprints = await this._resolve(bindings.blueprints, currentBlueprints);
|
|
178
|
+
const resolvedObjects = await this._resolve(bindings.objects, currentObjects);
|
|
151
179
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
180
|
+
log('_updateBindings resolved', {
|
|
181
|
+
resolvedBlueprints: resolvedBlueprints.length,
|
|
182
|
+
resolvedBlueprintKeys: resolvedBlueprints.map((bp) => (bp as any).key),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Filter current state to only items still in the reduced binding set,
|
|
186
|
+
// then merge in newly resolved items. This ensures unbind events are respected.
|
|
187
|
+
const reducedBlueprintDxns = new ComplexSet<DXN>(
|
|
188
|
+
DXN.hash,
|
|
189
|
+
[...bindings.blueprints].map((ref) => ref.dxn),
|
|
190
|
+
);
|
|
191
|
+
const reducedObjectDxns = new ComplexSet<DXN>(
|
|
192
|
+
DXN.hash,
|
|
193
|
+
[...bindings.objects].map((ref) => ref.dxn),
|
|
194
|
+
);
|
|
195
|
+
const filteredBlueprints = currentBlueprints.filter((obj) => reducedBlueprintDxns.has(Obj.getDXN(obj)));
|
|
196
|
+
const filteredObjects = currentObjects.filter((obj) => reducedObjectDxns.has(Obj.getDXN(obj)));
|
|
197
|
+
const mergedBlueprints = this._mergeInto(filteredBlueprints, resolvedBlueprints);
|
|
198
|
+
const mergedObjects = this._mergeInto(filteredObjects, resolvedObjects);
|
|
199
|
+
|
|
200
|
+
this._registry.set(this._blueprints, mergedBlueprints);
|
|
201
|
+
this._registry.set(this._objects, mergedObjects);
|
|
155
202
|
|
|
156
203
|
log('updated bindings', {
|
|
157
|
-
blueprints:
|
|
158
|
-
objects:
|
|
204
|
+
blueprints: mergedBlueprints.length,
|
|
205
|
+
objects: mergedObjects.length,
|
|
159
206
|
});
|
|
160
207
|
}
|
|
161
208
|
|
|
@@ -180,18 +227,20 @@ export class AiContextBinder extends Resource {
|
|
|
180
227
|
this._registry.set(this._objects, nextObjects);
|
|
181
228
|
|
|
182
229
|
log('bind', { blueprints: addedBlueprints.length, objects: addedObjects.length });
|
|
183
|
-
await this.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
230
|
+
await Runtime.runPromise(this._runtime)(
|
|
231
|
+
Feed.append(this._feed, [
|
|
232
|
+
Obj.make(ContextBinding, {
|
|
233
|
+
blueprints: {
|
|
234
|
+
added: addedBlueprints,
|
|
235
|
+
removed: [],
|
|
236
|
+
},
|
|
237
|
+
objects: {
|
|
238
|
+
added: addedObjects,
|
|
239
|
+
removed: [],
|
|
240
|
+
},
|
|
241
|
+
}),
|
|
242
|
+
]),
|
|
243
|
+
);
|
|
195
244
|
}
|
|
196
245
|
|
|
197
246
|
async unbind({ blueprints, objects }: BindingProps): Promise<void> {
|
|
@@ -199,19 +248,39 @@ export class AiContextBinder extends Resource {
|
|
|
199
248
|
return;
|
|
200
249
|
}
|
|
201
250
|
|
|
251
|
+
// Immediately update atom state so removals are reflected before the queue round-trips.
|
|
252
|
+
const removedBlueprintDxns = (blueprints ?? []).map((ref) => ref.dxn);
|
|
253
|
+
const removedObjectDxns = (objects ?? []).map((ref) => ref.dxn);
|
|
254
|
+
if (removedBlueprintDxns.length > 0) {
|
|
255
|
+
const current = this._registry.get(this._blueprints);
|
|
256
|
+
this._registry.set(
|
|
257
|
+
this._blueprints,
|
|
258
|
+
current.filter((obj) => !removedBlueprintDxns.some((dxn) => DXN.equalsEchoId(Obj.getDXN(obj), dxn))),
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
if (removedObjectDxns.length > 0) {
|
|
262
|
+
const current = this._registry.get(this._objects);
|
|
263
|
+
this._registry.set(
|
|
264
|
+
this._objects,
|
|
265
|
+
current.filter((obj) => !removedObjectDxns.some((dxn) => DXN.equalsEchoId(Obj.getDXN(obj), dxn))),
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
202
269
|
log('unbind', { blueprints: blueprints?.length, objects: objects?.length });
|
|
203
|
-
await this.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
270
|
+
await Runtime.runPromise(this._runtime)(
|
|
271
|
+
Feed.append(this._feed, [
|
|
272
|
+
Obj.make(ContextBinding, {
|
|
273
|
+
blueprints: {
|
|
274
|
+
added: [],
|
|
275
|
+
removed: blueprints ?? [],
|
|
276
|
+
},
|
|
277
|
+
objects: {
|
|
278
|
+
added: [],
|
|
279
|
+
removed: objects ?? [],
|
|
280
|
+
},
|
|
281
|
+
}),
|
|
282
|
+
]),
|
|
283
|
+
);
|
|
215
284
|
}
|
|
216
285
|
|
|
217
286
|
/**
|
|
@@ -271,14 +340,30 @@ export class AiContextBinder extends Resource {
|
|
|
271
340
|
);
|
|
272
341
|
}
|
|
273
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Merge resolved items into the current set, adding only items not already present (by DXN).
|
|
345
|
+
*/
|
|
346
|
+
private _mergeInto<T extends Obj.Unknown>(current: T[], resolved: T[]): T[] {
|
|
347
|
+
const seen = new Set(current.map((obj) => Obj.getDXN(obj).toString()));
|
|
348
|
+
const merged = [...current];
|
|
349
|
+
for (const obj of resolved) {
|
|
350
|
+
const dxn = Obj.getDXN(obj).toString();
|
|
351
|
+
if (!seen.has(dxn)) {
|
|
352
|
+
seen.add(dxn);
|
|
353
|
+
merged.push(obj);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return merged;
|
|
357
|
+
}
|
|
358
|
+
|
|
274
359
|
/**
|
|
275
360
|
* Resolve references to objects, loading them first if needed and falling back to existing objects.
|
|
276
361
|
*/
|
|
277
|
-
private async _resolve<T>(refs: Iterable<Ref.Ref<T>>, current: T[]): Promise<T[]> {
|
|
362
|
+
private async _resolve<T extends Obj.Unknown>(refs: Iterable<Ref.Ref<T>>, current: T[]): Promise<T[]> {
|
|
278
363
|
const refArray = [...refs];
|
|
279
364
|
|
|
280
365
|
// Load all refs that need loading.
|
|
281
|
-
await Promise.all(refArray.map((ref) => ref.
|
|
366
|
+
await Promise.all(refArray.map((ref) => ref.tryLoad()));
|
|
282
367
|
|
|
283
368
|
return refArray
|
|
284
369
|
.map((ref) => {
|
|
@@ -291,7 +376,7 @@ export class AiContextBinder extends Resource {
|
|
|
291
376
|
// Fallback to existing object.
|
|
292
377
|
return target ?? current.find((obj) => Obj.getDXN(obj as any).toString() === ref.dxn.toString());
|
|
293
378
|
})
|
|
294
|
-
.filter(
|
|
379
|
+
.filter(isNonNullable);
|
|
295
380
|
}
|
|
296
381
|
}
|
|
297
382
|
|
|
@@ -300,4 +385,19 @@ export class AiContextService extends Context.Tag('@dxos/assistant/AiContextServ
|
|
|
300
385
|
{
|
|
301
386
|
binder: AiContextBinder;
|
|
302
387
|
}
|
|
303
|
-
>() {
|
|
388
|
+
>() {
|
|
389
|
+
static bindContext = ({ blueprints, objects }: BindingProps): Effect.Effect<void, never, AiContextService> =>
|
|
390
|
+
Effect.gen(function* () {
|
|
391
|
+
const { binder } = yield* AiContextService;
|
|
392
|
+
yield* Effect.promise(() => binder.bind({ blueprints, objects }));
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
static findObjects = <T extends Type.AnyEntity>(
|
|
396
|
+
type: T,
|
|
397
|
+
): Effect.Effect<Schema.Schema.Type<T>[], never, AiContextService> => {
|
|
398
|
+
return Effect.gen(function* () {
|
|
399
|
+
const { binder } = yield* AiContextService;
|
|
400
|
+
return binder.getObjects().filter(Obj.instanceOf(type));
|
|
401
|
+
});
|
|
402
|
+
};
|
|
403
|
+
}
|
|
@@ -4,32 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
import { it } from '@effect/vitest';
|
|
6
6
|
import * as Effect from 'effect/Effect';
|
|
7
|
-
import {
|
|
7
|
+
import { describe, expect } from 'vitest';
|
|
8
8
|
|
|
9
9
|
import { Blueprint } from '@dxos/blueprints';
|
|
10
|
-
import { Obj, Ref } from '@dxos/echo';
|
|
11
|
-
import {
|
|
10
|
+
import { Database, Feed, Obj, Ref } from '@dxos/echo';
|
|
11
|
+
import { TestDatabaseLayer } from '@dxos/functions-runtime/testing';
|
|
12
12
|
|
|
13
13
|
import { ContextBinding } from './context';
|
|
14
14
|
import { AiConversation } from './conversation';
|
|
15
15
|
|
|
16
16
|
describe('AiConversation', () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
beforeEach(async () => {
|
|
20
|
-
builder = await new EchoTestBuilder().open();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
afterEach(async () => {
|
|
24
|
-
await builder.close();
|
|
17
|
+
const TestLayer = TestDatabaseLayer({
|
|
18
|
+
types: [Blueprint.Blueprint, ContextBinding],
|
|
25
19
|
});
|
|
26
20
|
|
|
27
21
|
it.effect('loads blueprints on open', () =>
|
|
28
22
|
Effect.gen(function* () {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
23
|
+
const { db } = yield* Database.Service;
|
|
24
|
+
|
|
25
|
+
// Create feed.
|
|
26
|
+
const feed = db.add(Feed.make());
|
|
33
27
|
|
|
34
28
|
// Create blueprint.
|
|
35
29
|
const blueprint = db.add(
|
|
@@ -39,27 +33,26 @@ describe('AiConversation', () => {
|
|
|
39
33
|
}),
|
|
40
34
|
);
|
|
41
35
|
|
|
42
|
-
// Add blueprint to
|
|
43
|
-
yield*
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
]),
|
|
56
|
-
);
|
|
36
|
+
// Add blueprint to feed via binding.
|
|
37
|
+
yield* Feed.append(feed, [
|
|
38
|
+
Obj.make(ContextBinding, {
|
|
39
|
+
blueprints: {
|
|
40
|
+
added: [Ref.make(blueprint)],
|
|
41
|
+
removed: [],
|
|
42
|
+
},
|
|
43
|
+
objects: {
|
|
44
|
+
added: [],
|
|
45
|
+
removed: [],
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
]);
|
|
57
49
|
|
|
58
|
-
const
|
|
50
|
+
const runtime = yield* Effect.runtime<Feed.FeedService>();
|
|
51
|
+
const conversation = new AiConversation({ feed, runtime });
|
|
59
52
|
yield* Effect.promise(() => conversation.open());
|
|
60
53
|
|
|
61
54
|
expect(conversation.context.getBlueprints()).toHaveLength(1);
|
|
62
55
|
expect(conversation.context.getObjects()).toHaveLength(0);
|
|
63
|
-
}),
|
|
56
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
64
57
|
);
|
|
65
58
|
});
|