@dxos/assistant 0.8.4-main.b97322e → 0.8.4-main.dedc0f3
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-5GJYG2ML.mjs +396 -0
- package/dist/lib/browser/chunk-5GJYG2ML.mjs.map +7 -0
- package/dist/lib/browser/chunk-UB26N5UF.mjs +89 -0
- package/dist/lib/browser/chunk-UB26N5UF.mjs.map +7 -0
- package/dist/lib/browser/extraction/index.mjs +268 -0
- package/dist/lib/browser/extraction/index.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +295 -1657
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/named-entity-recognition-QPYHML66.mjs +13 -0
- package/dist/lib/browser/named-entity-recognition-QPYHML66.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-BCFXTNSK.mjs +91 -0
- package/dist/lib/node-esm/chunk-BCFXTNSK.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-FLFCEB4P.mjs +398 -0
- package/dist/lib/node-esm/chunk-FLFCEB4P.mjs.map +7 -0
- package/dist/lib/node-esm/extraction/index.mjs +269 -0
- package/dist/lib/node-esm/extraction/index.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +294 -1657
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/named-entity-recognition-2HDF46YX.mjs +14 -0
- package/dist/lib/node-esm/named-entity-recognition-2HDF46YX.mjs.map +7 -0
- package/dist/types/src/conversation/context.d.ts +96 -0
- package/dist/types/src/conversation/context.d.ts.map +1 -0
- package/dist/types/src/conversation/conversation.d.ts +25 -21
- package/dist/types/src/conversation/conversation.d.ts.map +1 -1
- package/dist/types/src/conversation/index.d.ts +1 -0
- package/dist/types/src/conversation/index.d.ts.map +1 -1
- package/dist/types/src/diff.d.ts +15 -0
- package/dist/types/src/diff.d.ts.map +1 -0
- package/dist/types/src/diff.test.d.ts +2 -0
- package/dist/types/src/diff.test.d.ts.map +1 -0
- package/dist/types/src/errors.d.ts +41 -4
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +1 -1
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +65 -30
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction.d.ts +246 -216
- package/dist/types/src/extraction/extraction.d.ts.map +1 -1
- package/dist/types/src/functions/services.d.ts +4 -3
- package/dist/types/src/functions/services.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +2 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/session/artifact-diff.d.ts +27 -0
- package/dist/types/src/session/artifact-diff.d.ts.map +1 -0
- package/dist/types/src/session/error-handling.d.ts +4 -0
- package/dist/types/src/session/error-handling.d.ts.map +1 -0
- package/dist/types/src/session/format.d.ts +182 -0
- package/dist/types/src/session/format.d.ts.map +1 -0
- package/dist/types/src/session/format.test.d.ts +2 -0
- package/dist/types/src/session/format.test.d.ts.map +1 -0
- package/dist/types/src/session/index.d.ts +3 -0
- package/dist/types/src/session/index.d.ts.map +1 -1
- package/dist/types/src/session/observer.d.ts +22 -0
- package/dist/types/src/session/observer.d.ts.map +1 -0
- package/dist/types/src/session/session.d.ts +23 -236
- package/dist/types/src/session/session.d.ts.map +1 -1
- package/dist/types/src/session/toolkit.d.ts +14 -0
- package/dist/types/src/session/toolkit.d.ts.map +1 -0
- package/dist/types/src/session/version-pin.d.ts +2 -2
- package/dist/types/src/session/version-pin.d.ts.map +1 -1
- package/dist/types/src/templates/index.d.ts +1 -1
- package/dist/types/src/templates/system.d.ts +18 -0
- package/dist/types/src/templates/system.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +3 -0
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +45 -28
- package/src/conversation/context.ts +154 -0
- package/src/conversation/conversation.ts +80 -53
- package/src/conversation/index.ts +1 -0
- package/src/diff.test.ts +43 -0
- package/src/diff.ts +79 -0
- package/src/errors.ts +5 -0
- package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +8 -7
- package/src/extraction/extraction-llm-function/extraction-llm-function.ts +10 -10
- package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +9 -9
- package/src/extraction/extraction-ner-function/extraction-ner-function.ts +12 -7
- package/src/extraction/extraction-ner-function/named-entity-recognition.ts +2 -0
- package/src/extraction/quotes.test.ts +1 -1
- package/src/extraction/quotes.ts +1 -1
- package/src/functions/services.ts +57 -77
- package/src/index.ts +2 -3
- package/src/session/artifact-diff.ts +41 -0
- package/src/session/error-handling.ts +36 -0
- package/src/session/format.test.ts +60 -0
- package/src/session/format.ts +139 -0
- package/src/session/index.ts +3 -0
- package/src/session/observer.ts +58 -0
- package/src/session/session.test.ts +36 -38
- package/src/session/session.ts +113 -248
- package/src/session/toolkit.ts +42 -0
- package/src/session/version-pin.ts +4 -4
- package/src/summarization/summarization.test.ts +11 -22
- package/src/templates/format.tpl +60 -0
- package/src/templates/index.ts +1 -1
- package/src/templates/system.tpl +50 -0
- package/src/templates/system.ts +41 -0
- package/src/types.ts +10 -5
- package/src/typings.d.ts +5 -0
- package/dist/types/bin/chat.d.ts +0 -2
- package/dist/types/bin/chat.d.ts.map +0 -1
- package/dist/types/src/context/binder.d.ts +0 -34
- package/dist/types/src/context/binder.d.ts.map +0 -1
- package/dist/types/src/context/binding.d.ts +0 -56
- package/dist/types/src/context/binding.d.ts.map +0 -1
- package/dist/types/src/context/index.d.ts +0 -3
- package/dist/types/src/context/index.d.ts.map +0 -1
- package/dist/types/src/research/exa.d.ts +0 -12
- package/dist/types/src/research/exa.d.ts.map +0 -1
- package/dist/types/src/research/graph.d.ts +0 -51
- package/dist/types/src/research/graph.d.ts.map +0 -1
- package/dist/types/src/research/graph.test.d.ts +0 -2
- package/dist/types/src/research/graph.test.d.ts.map +0 -1
- package/dist/types/src/research/index.d.ts +0 -4
- package/dist/types/src/research/index.d.ts.map +0 -1
- package/dist/types/src/research/research.d.ts +0 -10
- package/dist/types/src/research/research.d.ts.map +0 -1
- package/dist/types/src/research/research.test.d.ts +0 -2
- package/dist/types/src/research/research.test.d.ts.map +0 -1
- package/dist/types/src/templates/prompt.d.ts +0 -30
- package/dist/types/src/templates/prompt.d.ts.map +0 -1
- package/dist/types/src/testing/exa-search-1748337321991.d.ts +0 -38
- package/dist/types/src/testing/exa-search-1748337321991.d.ts.map +0 -1
- package/dist/types/src/testing/exa-search-1748337331526.d.ts +0 -37
- package/dist/types/src/testing/exa-search-1748337331526.d.ts.map +0 -1
- package/dist/types/src/testing/exa-search-1748337344119.d.ts +0 -58
- package/dist/types/src/testing/exa-search-1748337344119.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -3
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/src/context/binder.ts +0 -103
- package/src/context/binding.ts +0 -32
- package/src/context/index.ts +0 -6
- package/src/research/exa.ts +0 -97
- package/src/research/graph.test.ts +0 -64
- package/src/research/graph.ts +0 -369
- package/src/research/index.ts +0 -7
- package/src/research/instructions-research.tpl +0 -98
- package/src/research/research.test.ts +0 -190
- package/src/research/research.ts +0 -87
- package/src/templates/assistant.tpl +0 -111
- package/src/templates/prompt.ts +0 -45
- package/src/testing/exa-search-1748337321991.ts +0 -131
- package/src/testing/exa-search-1748337331526.ts +0 -144
- package/src/testing/exa-search-1748337344119.ts +0 -133
- package/src/testing/index.ts +0 -11
- package/src/typedefs.d.ts +0 -8
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.dedc0f3",
|
|
4
4
|
"description": "Natural language interface to DXOS",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -10,51 +10,68 @@
|
|
|
10
10
|
"type": "module",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
+
"source": "./src/index.ts",
|
|
13
14
|
"types": "./dist/types/src/index.d.ts",
|
|
14
15
|
"browser": "./dist/lib/browser/index.mjs",
|
|
15
16
|
"node": "./dist/lib/node-esm/index.mjs"
|
|
17
|
+
},
|
|
18
|
+
"./extraction": {
|
|
19
|
+
"source": "./src/extraction/index.ts",
|
|
20
|
+
"types": "./dist/types/src/extraction/index.d.ts",
|
|
21
|
+
"browser": "./dist/lib/browser/extraction/index.mjs",
|
|
22
|
+
"node": "./dist/lib/node-esm/extraction/index.mjs"
|
|
16
23
|
}
|
|
17
24
|
},
|
|
18
25
|
"types": "dist/types/src/index.d.ts",
|
|
26
|
+
"typesVersions": {
|
|
27
|
+
"*": {
|
|
28
|
+
"types": [
|
|
29
|
+
"dist/types/src/types/index.d.ts"
|
|
30
|
+
],
|
|
31
|
+
"extraction": [
|
|
32
|
+
"dist/types/src/extraction/index.d.ts"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
19
36
|
"files": [
|
|
20
37
|
"dist",
|
|
21
38
|
"src"
|
|
22
39
|
],
|
|
23
40
|
"dependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"@effect/ai
|
|
26
|
-
"@effect/
|
|
27
|
-
"@effect/platform
|
|
41
|
+
"@automerge/automerge": "3.1.1",
|
|
42
|
+
"@effect/ai": "0.25.2",
|
|
43
|
+
"@effect/ai-anthropic": "^0.16.1",
|
|
44
|
+
"@effect/platform": "^0.90.2",
|
|
45
|
+
"@effect/platform-node": "^0.95.0",
|
|
28
46
|
"@preact/signals-core": "^1.9.0",
|
|
29
47
|
"@xenova/transformers": "^2.17.2",
|
|
30
48
|
"chalk": "^4.1.0",
|
|
31
|
-
"exa-js": "^1.5.12",
|
|
32
49
|
"handlebars": "4.7.8",
|
|
33
50
|
"lodash.defaultsdeep": "^4.6.1",
|
|
34
|
-
"@dxos/ai": "0.8.4-main.
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/context": "0.8.4-main.
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/debug": "0.8.4-main.
|
|
39
|
-
"@dxos/echo": "0.8.4-main.
|
|
40
|
-
"@dxos/echo-
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/log": "0.8.4-main.
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/util": "0.8.4-main.
|
|
53
|
-
"@dxos/
|
|
51
|
+
"@dxos/ai": "0.8.4-main.dedc0f3",
|
|
52
|
+
"@dxos/blueprints": "0.8.4-main.dedc0f3",
|
|
53
|
+
"@dxos/context": "0.8.4-main.dedc0f3",
|
|
54
|
+
"@dxos/async": "0.8.4-main.dedc0f3",
|
|
55
|
+
"@dxos/debug": "0.8.4-main.dedc0f3",
|
|
56
|
+
"@dxos/echo-db": "0.8.4-main.dedc0f3",
|
|
57
|
+
"@dxos/echo-protocol": "0.8.4-main.dedc0f3",
|
|
58
|
+
"@dxos/echo-schema": "0.8.4-main.dedc0f3",
|
|
59
|
+
"@dxos/errors": "0.8.4-main.dedc0f3",
|
|
60
|
+
"@dxos/functions": "0.8.4-main.dedc0f3",
|
|
61
|
+
"@dxos/effect": "0.8.4-main.dedc0f3",
|
|
62
|
+
"@dxos/graph": "0.8.4-main.dedc0f3",
|
|
63
|
+
"@dxos/keys": "0.8.4-main.dedc0f3",
|
|
64
|
+
"@dxos/echo": "0.8.4-main.dedc0f3",
|
|
65
|
+
"@dxos/log": "0.8.4-main.dedc0f3",
|
|
66
|
+
"@dxos/node-std": "0.8.4-main.dedc0f3",
|
|
67
|
+
"@dxos/schema": "0.8.4-main.dedc0f3",
|
|
68
|
+
"@dxos/invariant": "0.8.4-main.dedc0f3",
|
|
69
|
+
"@dxos/util": "0.8.4-main.dedc0f3",
|
|
70
|
+
"@dxos/live-object": "0.8.4-main.dedc0f3"
|
|
54
71
|
},
|
|
55
72
|
"devDependencies": {
|
|
56
73
|
"@types/lodash.defaultsdeep": "^4.6.6",
|
|
57
|
-
"effect": "3.17.
|
|
74
|
+
"effect": "3.17.7",
|
|
58
75
|
"inquirer": "^12.0.0"
|
|
59
76
|
},
|
|
60
77
|
"peerDependencies": {
|
|
@@ -65,6 +82,6 @@
|
|
|
65
82
|
},
|
|
66
83
|
"beast": {},
|
|
67
84
|
"scripts": {
|
|
68
|
-
"run:chat": "
|
|
85
|
+
"run:chat": "vite-node ./bin/chat.ts"
|
|
69
86
|
}
|
|
70
87
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type ReadonlySignal, computed } from '@preact/signals-core';
|
|
6
|
+
import { Context, Schema } from 'effect';
|
|
7
|
+
import { Array, pipe } from 'effect';
|
|
8
|
+
|
|
9
|
+
import { Blueprint } from '@dxos/blueprints';
|
|
10
|
+
import { DXN, Obj, type Ref, type Relation, Type } from '@dxos/echo';
|
|
11
|
+
import { type Queue } from '@dxos/echo-db';
|
|
12
|
+
import { ComplexSet } from '@dxos/util';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Thread message that binds or unbinds contextual objects to a conversation.
|
|
16
|
+
*/
|
|
17
|
+
// TODO(burdon): Move to @dxos/schema ContentBlock?
|
|
18
|
+
export const ContextBinding = Schema.Struct({
|
|
19
|
+
blueprints: Schema.Struct({
|
|
20
|
+
added: Schema.Array(Type.Ref(Blueprint.Blueprint)),
|
|
21
|
+
removed: Schema.Array(Type.Ref(Blueprint.Blueprint)),
|
|
22
|
+
}),
|
|
23
|
+
|
|
24
|
+
// TODO(burdon): Type.Expando => Type.Obj (or Obj.Any?)
|
|
25
|
+
objects: Schema.Struct({
|
|
26
|
+
added: Schema.Array(Type.Ref(Type.Expando)),
|
|
27
|
+
removed: Schema.Array(Type.Ref(Type.Expando)),
|
|
28
|
+
}),
|
|
29
|
+
}).pipe(
|
|
30
|
+
Type.Obj({
|
|
31
|
+
typename: 'dxos.org/type/ContextBinding',
|
|
32
|
+
version: '0.1.0',
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export interface ContextBinding extends Schema.Schema.Type<typeof ContextBinding> {}
|
|
37
|
+
|
|
38
|
+
export type BindingProps = Partial<{
|
|
39
|
+
blueprints: Ref.Ref<Blueprint.Blueprint>[];
|
|
40
|
+
objects: Ref.Ref<Type.Expando>[];
|
|
41
|
+
}>;
|
|
42
|
+
|
|
43
|
+
export class Bindings {
|
|
44
|
+
readonly blueprints = new ComplexSet<Ref.Ref<Blueprint.Blueprint>>((ref) => ref.dxn.toString());
|
|
45
|
+
// TODO(burdon): Some DXNs have the Space prefix so only compare the object ID.
|
|
46
|
+
readonly objects = new ComplexSet<Ref.Ref<Type.Expando>>((ref) => ref.dxn.asEchoDXN()?.echoId);
|
|
47
|
+
|
|
48
|
+
toJSON() {
|
|
49
|
+
return {
|
|
50
|
+
blueprints: Array.fromIterable(this.blueprints).map((ref) => ref.dxn.toString()),
|
|
51
|
+
objects: Array.fromIterable(this.objects).map((ref) => ref.dxn.toString()),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Manages bindings of blueprints and objects to a conversation.
|
|
58
|
+
*/
|
|
59
|
+
// TODO(burdon): Context should manage ephemeral state of bindings until prompt is issued?
|
|
60
|
+
export class AiContextBinder {
|
|
61
|
+
/**
|
|
62
|
+
* Reactive query of all bindings.
|
|
63
|
+
*/
|
|
64
|
+
// TODO(burdon): Cache value?
|
|
65
|
+
readonly bindings: ReadonlySignal<Bindings> = computed(() => this._reduce(this._queue.objects));
|
|
66
|
+
|
|
67
|
+
readonly blueprints: ReadonlySignal<Ref.Ref<Blueprint.Blueprint>[]> = computed(() => [
|
|
68
|
+
...this.bindings.value.blueprints,
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
readonly objects: ReadonlySignal<Ref.Ref<Type.Expando>[]> = computed(() => [...this.bindings.value.objects]);
|
|
72
|
+
|
|
73
|
+
constructor(private readonly _queue: Queue) {}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Asynchronous query of all bindings.
|
|
77
|
+
*/
|
|
78
|
+
async query(): Promise<Bindings> {
|
|
79
|
+
const queueItems = await this._queue.queryObjects();
|
|
80
|
+
return this._reduce(queueItems);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// TODO(burdon): Pass in Blueprint obj (from registry?) and create reference.
|
|
84
|
+
async bind(props: BindingProps): Promise<void> {
|
|
85
|
+
if (!props.blueprints?.length && !props.objects?.length) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const blueprints =
|
|
90
|
+
props.blueprints?.filter((ref) => !this.blueprints.peek().find((b) => b.dxn.toString() === ref.dxn.toString())) ??
|
|
91
|
+
[];
|
|
92
|
+
const objects =
|
|
93
|
+
props.objects?.filter((ref) => !this.objects.peek().find((o) => o.dxn.toString() === ref.dxn.toString())) ?? [];
|
|
94
|
+
|
|
95
|
+
await this._queue.append([
|
|
96
|
+
Obj.make(ContextBinding, {
|
|
97
|
+
blueprints: {
|
|
98
|
+
added: blueprints,
|
|
99
|
+
removed: [],
|
|
100
|
+
},
|
|
101
|
+
objects: {
|
|
102
|
+
added: objects,
|
|
103
|
+
removed: [],
|
|
104
|
+
},
|
|
105
|
+
}),
|
|
106
|
+
]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async unbind(props: BindingProps): Promise<void> {
|
|
110
|
+
if (!props.blueprints?.length && !props.objects?.length) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await this._queue.append([
|
|
115
|
+
Obj.make(ContextBinding, {
|
|
116
|
+
blueprints: {
|
|
117
|
+
added: [],
|
|
118
|
+
removed: props.blueprints ?? [],
|
|
119
|
+
},
|
|
120
|
+
objects: {
|
|
121
|
+
added: [],
|
|
122
|
+
removed: props.objects ?? [],
|
|
123
|
+
},
|
|
124
|
+
}),
|
|
125
|
+
]);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private _reduce(items: (Obj.Any | Relation.Any)[]): Bindings {
|
|
129
|
+
return pipe(
|
|
130
|
+
items,
|
|
131
|
+
Array.filter(Obj.instanceOf(ContextBinding)),
|
|
132
|
+
Array.reduce(new Bindings(), (context, item) => {
|
|
133
|
+
item.blueprints.removed.forEach((ref) => context.blueprints.delete(ref));
|
|
134
|
+
item.blueprints.added.forEach((ref) => context.blueprints.add(ref));
|
|
135
|
+
item.objects.removed.forEach((ref) => {
|
|
136
|
+
for (const obj of context.objects) {
|
|
137
|
+
if (DXN.equalsEchoId(obj.dxn, ref.dxn)) {
|
|
138
|
+
context.objects.delete(obj);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
item.objects.added.forEach((ref) => context.objects.add(ref));
|
|
143
|
+
return context;
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export class AiContextService extends Context.Tag('@dxos/assistant/AiContextService')<
|
|
150
|
+
AiContextService,
|
|
151
|
+
{
|
|
152
|
+
binder: AiContextBinder;
|
|
153
|
+
}
|
|
154
|
+
>() {}
|
|
@@ -2,95 +2,122 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type
|
|
6
|
-
import { Effect } from 'effect';
|
|
5
|
+
import { type AiTool, type AiToolkit } from '@effect/ai';
|
|
6
|
+
import { Array, Effect, Option } from 'effect';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
type ToolExecutionService,
|
|
10
|
-
type ToolResolverService,
|
|
11
|
-
type AiInputPreprocessingError,
|
|
12
|
-
type AiToolNotFoundError,
|
|
13
|
-
} from '@dxos/ai';
|
|
14
|
-
import { Event } from '@dxos/async';
|
|
15
8
|
import { Obj } from '@dxos/echo';
|
|
16
9
|
import { type Queue } from '@dxos/echo-db';
|
|
17
10
|
import { DatabaseService } from '@dxos/functions';
|
|
11
|
+
import { log } from '@dxos/log';
|
|
18
12
|
import { DataType } from '@dxos/schema';
|
|
19
13
|
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
import {
|
|
15
|
+
AiSession,
|
|
16
|
+
type AiSessionRunError,
|
|
17
|
+
type AiSessionRunRequirements,
|
|
18
|
+
type GenerationObserver,
|
|
19
|
+
createToolkit,
|
|
20
|
+
} from '../session';
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
systemPrompt?: string;
|
|
26
|
-
prompt: string;
|
|
22
|
+
import { AiContextBinder, AiContextService, type ContextBinding } from './context';
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
export type AiConversationRunRequirements<Tools extends AiTool.Any> =
|
|
25
|
+
| AiSessionRunRequirements
|
|
26
|
+
| AiTool.ToHandler<Tools>;
|
|
27
|
+
|
|
28
|
+
export interface AiConversationRunParams {
|
|
29
|
+
prompt: string;
|
|
30
|
+
system?: string;
|
|
31
|
+
observer?: GenerationObserver;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
export type
|
|
34
|
+
export type AiConversationOptions = {
|
|
32
35
|
queue: Queue<DataType.Message | ContextBinding>;
|
|
33
36
|
};
|
|
34
37
|
|
|
35
38
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* Backed by a Queue.
|
|
39
|
+
* Durable conversation state (initiated by users and agents) backed by a Queue.
|
|
40
|
+
* Executes tools based on AI responses and supports cancellation of in-progress requests.
|
|
39
41
|
*/
|
|
40
|
-
export class
|
|
42
|
+
export class AiConversation {
|
|
43
|
+
/**
|
|
44
|
+
* Message and binding queue.
|
|
45
|
+
*/
|
|
41
46
|
private readonly _queue: Queue<DataType.Message | ContextBinding>;
|
|
42
47
|
|
|
43
48
|
/**
|
|
44
|
-
*
|
|
45
|
-
* This is called before the first message is sent.
|
|
49
|
+
* Blueprints bound to the conversation.
|
|
46
50
|
*/
|
|
47
|
-
|
|
51
|
+
private readonly _context: AiContextBinder;
|
|
48
52
|
|
|
49
53
|
/**
|
|
50
|
-
*
|
|
54
|
+
* Toolkit from the current session request.
|
|
51
55
|
*/
|
|
52
|
-
|
|
56
|
+
private _toolkit: AiToolkit.ToHandler<AiTool.Any> | undefined;
|
|
53
57
|
|
|
54
|
-
constructor(options:
|
|
58
|
+
public constructor(options: AiConversationOptions) {
|
|
55
59
|
this._queue = options.queue;
|
|
56
|
-
this.
|
|
60
|
+
this._context = new AiContextBinder(this._queue);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public get context() {
|
|
64
|
+
return this._context;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public get toolkit() {
|
|
68
|
+
return this._toolkit;
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
async getHistory(): Promise<DataType.Message[]> {
|
|
71
|
+
public async getHistory(): Promise<DataType.Message[]> {
|
|
60
72
|
const queueItems = await this._queue.queryObjects();
|
|
61
73
|
return queueItems.filter(Obj.instanceOf(DataType.Message));
|
|
62
74
|
}
|
|
63
75
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
Effect.gen(this, function* () {
|
|
76
|
+
/**
|
|
77
|
+
* Creates a new cancelable request effect.
|
|
78
|
+
*/
|
|
79
|
+
createRequest<Tools extends AiTool.Any>(
|
|
80
|
+
params: AiConversationRunParams,
|
|
81
|
+
): Effect.Effect<DataType.Message[], AiSessionRunError, AiConversationRunRequirements<Tools>> {
|
|
82
|
+
return Effect.gen(this, function* () {
|
|
72
83
|
const session = new AiSession();
|
|
73
|
-
this.onBegin.emit(session);
|
|
74
|
-
|
|
75
84
|
const history = yield* Effect.promise(() => this.getHistory());
|
|
85
|
+
|
|
86
|
+
// Get context objects.
|
|
76
87
|
const context = yield* Effect.promise(() => this.context.query());
|
|
77
|
-
const blueprints = yield* Effect.forEach(context.blueprints.values(), DatabaseService.
|
|
88
|
+
const blueprints = yield* Effect.forEach(context.blueprints.values(), DatabaseService.loadOption).pipe(
|
|
89
|
+
Effect.map(Array.filter(Option.isSome)),
|
|
90
|
+
Effect.map(Array.map((option) => option.value)),
|
|
91
|
+
);
|
|
92
|
+
const objects = yield* Effect.forEach(context.objects.values(), DatabaseService.loadOption).pipe(
|
|
93
|
+
Effect.map(Array.filter(Option.isSome)),
|
|
94
|
+
Effect.map(Array.map((option) => option.value)),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Create toolkit.
|
|
98
|
+
const toolkit = yield* createToolkit<Tools>({ blueprints });
|
|
99
|
+
this._toolkit = toolkit;
|
|
100
|
+
|
|
101
|
+
const start = Date.now();
|
|
102
|
+
log('run', {
|
|
103
|
+
history: history.length,
|
|
104
|
+
blueprints: blueprints.length,
|
|
105
|
+
objects: objects.length,
|
|
106
|
+
tools: this._toolkit?.tools.length ?? 0,
|
|
107
|
+
});
|
|
78
108
|
|
|
79
|
-
|
|
109
|
+
// Process request.
|
|
110
|
+
const messages = yield* session.run<Tools>({ history, blueprints, objects, toolkit, ...params }).pipe(
|
|
111
|
+
Effect.provideService(AiContextService, {
|
|
112
|
+
binder: this.context,
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
80
115
|
|
|
81
|
-
|
|
82
|
-
(options.systemPrompt ?? '') +
|
|
83
|
-
'\n\n' +
|
|
84
|
-
contextObjects.map((obj) => `<object>${Obj.getDXN(obj)}</object>`).join('\n');
|
|
116
|
+
log('result', { messages: messages.length, duration: Date.now() - start });
|
|
85
117
|
|
|
86
|
-
|
|
87
|
-
prompt: options.prompt,
|
|
88
|
-
history,
|
|
89
|
-
systemPrompt,
|
|
90
|
-
toolkit: options.toolkit,
|
|
91
|
-
blueprints,
|
|
92
|
-
});
|
|
118
|
+
// Append to queue.
|
|
93
119
|
yield* Effect.promise(() => this._queue.append(messages));
|
|
94
120
|
return messages;
|
|
95
|
-
}).pipe(Effect.withSpan('
|
|
121
|
+
}).pipe(Effect.withSpan('AiConversation.request'));
|
|
122
|
+
}
|
|
96
123
|
}
|
package/src/diff.test.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { createDocAccessor } from '@dxos/echo-db';
|
|
8
|
+
import { EchoTestBuilder } from '@dxos/echo-db/testing';
|
|
9
|
+
import { DataType } from '@dxos/schema';
|
|
10
|
+
import { trim } from '@dxos/util';
|
|
11
|
+
|
|
12
|
+
import { applyDiffs } from './diff';
|
|
13
|
+
|
|
14
|
+
describe('diff', () => {
|
|
15
|
+
it('should apply diffs', async () => {
|
|
16
|
+
const builder = new EchoTestBuilder();
|
|
17
|
+
const { db, graph } = await builder.createDatabase();
|
|
18
|
+
graph.schemaRegistry.addSchema([DataType.Text]);
|
|
19
|
+
|
|
20
|
+
const document = trim`
|
|
21
|
+
# Hello World
|
|
22
|
+
There'z a typo in this sentence.
|
|
23
|
+
And another oon here.
|
|
24
|
+
But not this one.
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const text = db.add(DataType.makeText(document));
|
|
28
|
+
const accessor = createDocAccessor(text, ['content']);
|
|
29
|
+
const result = applyDiffs(accessor, [
|
|
30
|
+
`- There'z a typo in this sentence.`,
|
|
31
|
+
`+ There is a typo in this sentence.`,
|
|
32
|
+
`- And another oon here.`,
|
|
33
|
+
`+ And another one here.`,
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
expect(result).toBe(trim`
|
|
37
|
+
# Hello World
|
|
38
|
+
There is a typo in this sentence.
|
|
39
|
+
And another one here.
|
|
40
|
+
But not this one.
|
|
41
|
+
`);
|
|
42
|
+
});
|
|
43
|
+
});
|
package/src/diff.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { next as A, type Doc } from '@automerge/automerge';
|
|
6
|
+
|
|
7
|
+
import { DocAccessor, toCursorRange } from '@dxos/echo-db';
|
|
8
|
+
import { log } from '@dxos/log';
|
|
9
|
+
import { isNonNullable } from '@dxos/util';
|
|
10
|
+
|
|
11
|
+
// Relevant info:
|
|
12
|
+
// https://github.com/Minahil-official/GPT-4.1-Promting-Guide?tab=readme-ov-file#-apply-patch-tool-format-v4a-diff
|
|
13
|
+
|
|
14
|
+
// TOOD(burdon): Support append to end (no "-").
|
|
15
|
+
|
|
16
|
+
export type Diff = {
|
|
17
|
+
match: string;
|
|
18
|
+
replace: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Convert a list of diffs into a list of operations.
|
|
23
|
+
*/
|
|
24
|
+
export const reduceDiffs = (diffs: readonly string[]): Diff[] => {
|
|
25
|
+
return diffs.reduce<Diff[]>((acc, diff) => {
|
|
26
|
+
const current = acc[acc.length - 1];
|
|
27
|
+
const [op, ...rest] = diff.split(' ');
|
|
28
|
+
switch (op) {
|
|
29
|
+
case '-': {
|
|
30
|
+
if (current && !current.match) {
|
|
31
|
+
current.match += rest.join(' ');
|
|
32
|
+
} else {
|
|
33
|
+
acc.push({ match: rest.join(' '), replace: '' });
|
|
34
|
+
}
|
|
35
|
+
return acc;
|
|
36
|
+
}
|
|
37
|
+
case '+': {
|
|
38
|
+
if (current && !current.replace) {
|
|
39
|
+
current.replace += rest.join(' ');
|
|
40
|
+
} else {
|
|
41
|
+
acc.push({ match: '', replace: rest.join(' ') });
|
|
42
|
+
}
|
|
43
|
+
return acc;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return acc;
|
|
48
|
+
}, []);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const computeDiffsWithCursors = <T>(accessor: DocAccessor<T>, diffs: readonly string[]) => {
|
|
52
|
+
return reduceDiffs(diffs)
|
|
53
|
+
.map((diff) => {
|
|
54
|
+
const text = DocAccessor.getValue<string>(accessor);
|
|
55
|
+
const idx = text.indexOf(diff.match);
|
|
56
|
+
if (idx !== -1) {
|
|
57
|
+
return { cursor: toCursorRange(accessor, idx, idx + diff.match.length), text: diff.replace };
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.filter(isNonNullable);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const applyDiffs = <T>(accessor: DocAccessor<T>, diffs: readonly string[]): string => {
|
|
64
|
+
for (const diff of reduceDiffs(diffs)) {
|
|
65
|
+
accessor.handle.change((doc: Doc<T>) => {
|
|
66
|
+
const text = DocAccessor.getValue<string>(accessor);
|
|
67
|
+
const idx = text.indexOf(diff.match);
|
|
68
|
+
if (idx !== -1) {
|
|
69
|
+
// TODO(burdon): Replace smallest substring.
|
|
70
|
+
// NOTE: This only replaces the first match.
|
|
71
|
+
A.splice(doc, accessor.path as A.Prop[], idx, diff.match.length, diff.replace);
|
|
72
|
+
} else {
|
|
73
|
+
log.warn('diff not found', diff);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return DocAccessor.getValue<string>(accessor);
|
|
79
|
+
};
|
package/src/errors.ts
CHANGED
|
@@ -8,3 +8,8 @@ import { BaseError } from '@dxos/errors';
|
|
|
8
8
|
* Generic error for AI agent execution.
|
|
9
9
|
*/
|
|
10
10
|
export class AiAssistantError extends BaseError.extend('AI_ASSISTANT_ERROR') {}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parsed error from the anthropic API.
|
|
14
|
+
*/
|
|
15
|
+
export class AnthropicError extends BaseError.extend('ANTHROPIC_ERROR') {}
|
|
@@ -11,7 +11,8 @@ import { createTestData } from '@dxos/schema/testing';
|
|
|
11
11
|
import { range } from '@dxos/util';
|
|
12
12
|
|
|
13
13
|
import { processTranscriptMessage } from '../extraction';
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
import { extractionAnthropicFunction } from './extraction-llm-function';
|
|
15
16
|
|
|
16
17
|
// TODO(burdon): Rewrite test.
|
|
17
18
|
describe.skip('LLM EntityExtraction', () => {
|
|
@@ -32,7 +33,7 @@ describe.skip('LLM EntityExtraction', () => {
|
|
|
32
33
|
message,
|
|
33
34
|
objects: [...documents, ...Object.values(contacts)],
|
|
34
35
|
},
|
|
35
|
-
function:
|
|
36
|
+
function: extractionAnthropicFunction,
|
|
36
37
|
executor,
|
|
37
38
|
});
|
|
38
39
|
log.info('output', enhancedMessage);
|
|
@@ -41,8 +42,8 @@ describe.skip('LLM EntityExtraction', () => {
|
|
|
41
42
|
|
|
42
43
|
test('computational irreducibility', async () => {
|
|
43
44
|
const { transcriptWoflram, documents, contacts } = createTestData();
|
|
44
|
-
|
|
45
45
|
log.info('context', { documents, contacts });
|
|
46
|
+
|
|
46
47
|
const message = transcriptWoflram[0];
|
|
47
48
|
log.info('input', message);
|
|
48
49
|
|
|
@@ -53,9 +54,10 @@ describe.skip('LLM EntityExtraction', () => {
|
|
|
53
54
|
message,
|
|
54
55
|
objects: [...documents, ...Object.values(contacts)],
|
|
55
56
|
},
|
|
56
|
-
function:
|
|
57
|
+
function: extractionAnthropicFunction,
|
|
57
58
|
executor,
|
|
58
59
|
});
|
|
60
|
+
|
|
59
61
|
log.info('output', { message: enhancedMessage.blocks[0], timeElapsed });
|
|
60
62
|
}),
|
|
61
63
|
);
|
|
@@ -63,20 +65,19 @@ describe.skip('LLM EntityExtraction', () => {
|
|
|
63
65
|
|
|
64
66
|
test('org and document linking', async () => {
|
|
65
67
|
const { transcriptJosiah, documents, contacts, organizations } = createTestData();
|
|
66
|
-
|
|
67
68
|
log.info('context', { contacts, organizations, documents });
|
|
68
69
|
|
|
69
70
|
for (const message of transcriptJosiah) {
|
|
70
71
|
log.info('input', message);
|
|
71
|
-
|
|
72
72
|
const { message: enhancedMessage } = await processTranscriptMessage({
|
|
73
73
|
input: {
|
|
74
74
|
message,
|
|
75
75
|
objects: [...documents, ...Object.values(contacts), ...Object.values(organizations)],
|
|
76
76
|
},
|
|
77
|
-
function:
|
|
77
|
+
function: extractionAnthropicFunction,
|
|
78
78
|
executor,
|
|
79
79
|
});
|
|
80
|
+
|
|
80
81
|
log.info('output', enhancedMessage);
|
|
81
82
|
}
|
|
82
83
|
});
|