@dxos/assistant 0.8.4-main.3a94e84 → 0.8.4-main.406dc2a
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-CWGVG235.mjs +380 -0
- package/dist/lib/browser/chunk-CWGVG235.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 +272 -0
- package/dist/lib/browser/extraction/index.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +747 -1645
- 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-OV5PMQPB.mjs +382 -0
- package/dist/lib/node-esm/chunk-OV5PMQPB.mjs.map +7 -0
- package/dist/lib/node-esm/extraction/index.mjs +273 -0
- package/dist/lib/node-esm/extraction/index.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +746 -1645
- 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 +103 -0
- package/dist/types/src/conversation/context.d.ts.map +1 -0
- package/dist/types/src/conversation/conversation.d.ts +28 -22
- 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 +46 -9
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/execution-graph.d.ts +82 -0
- package/dist/types/src/execution-graph.d.ts.map +1 -0
- package/dist/types/src/execution-graph.test.d.ts +2 -0
- package/dist/types/src/execution-graph.test.d.ts.map +1 -0
- 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 +103 -64
- 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 +252 -218
- package/dist/types/src/extraction/extraction.d.ts.map +1 -1
- package/dist/types/src/extraction/quotes.d.ts +1 -1
- package/dist/types/src/extraction/quotes.d.ts.map +1 -1
- package/dist/types/src/functions/services.d.ts +17 -4
- package/dist/types/src/functions/services.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -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 +184 -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 +27 -236
- package/dist/types/src/session/session.d.ts.map +1 -1
- package/dist/types/src/session/toolkit.d.ts +15 -0
- package/dist/types/src/session/toolkit.d.ts.map +1 -0
- package/dist/types/src/session/version-pin.d.ts +3 -3
- 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 → types/artifact.d.ts} +6 -3
- package/dist/types/src/types/artifact.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +2 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +44 -32
- package/src/conversation/context.ts +186 -0
- package/src/conversation/conversation.ts +89 -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 +6 -1
- package/src/execution-graph.test.ts +738 -0
- package/src/execution-graph.ts +500 -0
- package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +8 -7
- package/src/extraction/extraction-llm-function/extraction-llm-function.ts +13 -11
- package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +9 -9
- package/src/extraction/extraction-ner-function/extraction-ner-function.ts +15 -8
- package/src/extraction/extraction-ner-function/named-entity-recognition.test.ts +2 -1
- package/src/extraction/extraction-ner-function/named-entity-recognition.ts +2 -0
- package/src/extraction/extraction.ts +2 -2
- package/src/extraction/quotes.test.ts +3 -3
- package/src/extraction/quotes.ts +4 -4
- package/src/functions/services.ts +114 -95
- package/src/index.ts +3 -3
- package/src/session/artifact-diff.ts +41 -0
- package/src/session/error-handling.ts +41 -0
- package/src/session/format.test.ts +60 -0
- package/src/session/format.ts +142 -0
- package/src/session/index.ts +3 -0
- package/src/session/observer.ts +59 -0
- package/src/session/session.conversations.json +1 -0
- package/src/session/session.test.ts +37 -198
- package/src/session/session.ts +125 -249
- package/src/session/toolkit.ts +48 -0
- package/src/session/version-pin.ts +5 -5
- package/src/summarization/summarization.test.ts +16 -22
- package/src/templates/format.tpl +60 -0
- package/src/templates/index.ts +1 -1
- package/src/templates/system.tpl +48 -0
- package/src/templates/system.ts +41 -0
- package/src/{types.ts → types/artifact.ts} +12 -7
- package/src/types/index.ts +5 -0
- 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/session/version-pin.test.d.ts +0 -2
- package/dist/types/src/session/version-pin.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/dist/types/src/types.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/session/__snapshots__/session.test.ts.snap +0 -32
- package/src/session/version-pin.test.ts +0 -7
- 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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,GAAG,EAAmB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAEhE,eAAO,MAAM,qBAAqB,GAAI,IAAI,QAAQ,WAA4B,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG;IAC/C,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,GAAG,CAAC;CA4BhE,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,UAAU,CAAC,CAAC"}
|
package/src/context/binder.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
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 { type Blueprint } from '@dxos/blueprints';
|
|
9
|
-
import { Obj, type Type, type Ref, type Relation } from '@dxos/echo';
|
|
10
|
-
import { type Queue } from '@dxos/echo-db';
|
|
11
|
-
import { ComplexSet } from '@dxos/util';
|
|
12
|
-
|
|
13
|
-
import { ContextBinding } from './binding';
|
|
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.Blueprint>[]> = computed(() => [
|
|
29
|
-
...this.bindings.value.blueprints,
|
|
30
|
-
]);
|
|
31
|
-
readonly objects: ReadonlySignal<Ref.Ref<Type.Expando>[]> = computed(() => [...this.bindings.value.objects]);
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Asynchronous query of all bindings.
|
|
35
|
-
*/
|
|
36
|
-
async query(): Promise<Bindings> {
|
|
37
|
-
const queueItems = await this._queue.queryObjects();
|
|
38
|
-
return this._reduce(queueItems);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// TODO(burdon): Pass in Blueprint obj (from registry?) and create reference.
|
|
42
|
-
async bind(props: BindingProps): Promise<void> {
|
|
43
|
-
if (!props.blueprints?.length && !props.objects?.length) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
await this._queue.append([
|
|
48
|
-
Obj.make(ContextBinding, {
|
|
49
|
-
blueprints: {
|
|
50
|
-
added: props.blueprints ?? [],
|
|
51
|
-
removed: [],
|
|
52
|
-
},
|
|
53
|
-
objects: {
|
|
54
|
-
added: props.objects ?? [],
|
|
55
|
-
removed: [],
|
|
56
|
-
},
|
|
57
|
-
}),
|
|
58
|
-
]);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async unbind(props: BindingProps): Promise<void> {
|
|
62
|
-
if (!props.blueprints?.length && !props.objects?.length) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
await this._queue.append([
|
|
67
|
-
Obj.make(ContextBinding, {
|
|
68
|
-
blueprints: {
|
|
69
|
-
added: [],
|
|
70
|
-
removed: props.blueprints ?? [],
|
|
71
|
-
},
|
|
72
|
-
objects: {
|
|
73
|
-
added: [],
|
|
74
|
-
removed: props.objects ?? [],
|
|
75
|
-
},
|
|
76
|
-
}),
|
|
77
|
-
]);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private _reduce(items: (Obj.Any | Relation.Any)[]): Bindings {
|
|
81
|
-
return pipe(
|
|
82
|
-
items,
|
|
83
|
-
Array.filter(Obj.instanceOf(ContextBinding)),
|
|
84
|
-
Array.reduce(new Bindings(), (context, item) => {
|
|
85
|
-
item.blueprints.removed.forEach((item) => context.blueprints.delete(item));
|
|
86
|
-
item.blueprints.added.forEach((item) => context.blueprints.add(item));
|
|
87
|
-
item.objects.removed.forEach((item) => context.objects.delete(item));
|
|
88
|
-
item.objects.added.forEach((item) => context.objects.add(item));
|
|
89
|
-
return context;
|
|
90
|
-
}),
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export type BindingProps = Partial<{
|
|
96
|
-
blueprints: Ref.Ref<Blueprint.Blueprint>[];
|
|
97
|
-
objects: Ref.Ref<Type.Expando>[];
|
|
98
|
-
}>;
|
|
99
|
-
|
|
100
|
-
export class Bindings {
|
|
101
|
-
blueprints = new ComplexSet<Ref.Ref<Blueprint.Blueprint>>((ref) => ref.dxn.toString());
|
|
102
|
-
objects = new ComplexSet<Ref.Ref<Type.Expando>>((ref) => ref.dxn.toString());
|
|
103
|
-
}
|
package/src/context/binding.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { Schema } from 'effect';
|
|
6
|
-
|
|
7
|
-
import { Blueprint } from '@dxos/blueprints';
|
|
8
|
-
import { Type } from '@dxos/echo';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Thread message that binds or unbinds contextual objects to a conversation.
|
|
12
|
-
*/
|
|
13
|
-
// TODO(burdon): Make ContentBlock.
|
|
14
|
-
export const ContextBinding = Schema.Struct({
|
|
15
|
-
blueprints: Schema.Struct({
|
|
16
|
-
added: Schema.Array(Type.Ref(Blueprint.Blueprint)),
|
|
17
|
-
removed: Schema.Array(Type.Ref(Blueprint.Blueprint)),
|
|
18
|
-
}),
|
|
19
|
-
|
|
20
|
-
// TODO(burdon): Type.Expando => Type.Obj (or Obj.Any?)
|
|
21
|
-
objects: Schema.Struct({
|
|
22
|
-
added: Schema.Array(Type.Ref(Type.Expando)),
|
|
23
|
-
removed: Schema.Array(Type.Ref(Type.Expando)),
|
|
24
|
-
}),
|
|
25
|
-
}).pipe(
|
|
26
|
-
Type.Obj({
|
|
27
|
-
typename: 'dxos.org/type/ContextBinding',
|
|
28
|
-
version: '0.1.0',
|
|
29
|
-
}),
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
export interface ContextBinding extends Schema.Schema.Type<typeof ContextBinding> {}
|
package/src/context/index.ts
DELETED
package/src/research/exa.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { AiTool, AiToolkit } from '@effect/ai';
|
|
6
|
-
import { Effect, Schema } from 'effect';
|
|
7
|
-
import Exa from 'exa-js';
|
|
8
|
-
|
|
9
|
-
import { CredentialsService } from '@dxos/functions';
|
|
10
|
-
import { log } from '@dxos/log';
|
|
11
|
-
|
|
12
|
-
import { SEARCH_RESULTS } from '../testing';
|
|
13
|
-
|
|
14
|
-
export class ExaToolkit extends AiToolkit.make(
|
|
15
|
-
AiTool.make('exa_search', {
|
|
16
|
-
description: 'Search the web for information',
|
|
17
|
-
parameters: {
|
|
18
|
-
query: Schema.String.annotations({ description: 'The query to search for' }),
|
|
19
|
-
},
|
|
20
|
-
success: Schema.Unknown,
|
|
21
|
-
failure: Schema.Never,
|
|
22
|
-
}).addRequirement<CredentialsService>(),
|
|
23
|
-
) {}
|
|
24
|
-
|
|
25
|
-
export const LiveExaHandler = ExaToolkit.toLayer({
|
|
26
|
-
exa_search: Effect.fn(function* ({ query }) {
|
|
27
|
-
const credential = yield* CredentialsService.getCredential({ service: 'exa.ai' });
|
|
28
|
-
const exa = new Exa(credential.apiKey);
|
|
29
|
-
|
|
30
|
-
const context = yield* Effect.promise(async () =>
|
|
31
|
-
exa.searchAndContents(query, {
|
|
32
|
-
type: 'auto',
|
|
33
|
-
text: {
|
|
34
|
-
maxCharacters: 3_000,
|
|
35
|
-
},
|
|
36
|
-
livecrawl: 'always',
|
|
37
|
-
}),
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
log.info('exa search', { query, costDollars: context.costDollars });
|
|
41
|
-
|
|
42
|
-
return context;
|
|
43
|
-
}),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export const MockExaHandler = ExaToolkit.toLayer({
|
|
47
|
-
exa_search: Effect.fn(function* ({ query }) {
|
|
48
|
-
// Find result with closest matching autoprompt using weighted Levenshtein distance
|
|
49
|
-
const result = SEARCH_RESULTS.reduce(
|
|
50
|
-
(closest, current) => {
|
|
51
|
-
if (!current.autopromptString) {
|
|
52
|
-
return closest;
|
|
53
|
-
}
|
|
54
|
-
if (!closest) {
|
|
55
|
-
return current;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Calculate Levenshtein distance
|
|
59
|
-
const dist1 = levenshteinDistance(query, current.autopromptString);
|
|
60
|
-
const dist2 = levenshteinDistance(query, closest.autopromptString || '');
|
|
61
|
-
|
|
62
|
-
// Weight by length of the longer string to normalize
|
|
63
|
-
const weight1 = dist1 / Math.max(query.length, current.autopromptString.length);
|
|
64
|
-
const weight2 = dist2 / Math.max(query.length, closest.autopromptString?.length || 0);
|
|
65
|
-
|
|
66
|
-
return weight1 < weight2 ? current : closest;
|
|
67
|
-
},
|
|
68
|
-
null as (typeof SEARCH_RESULTS)[0] | null,
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
return result;
|
|
72
|
-
}),
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const levenshteinDistance = (str1: string, str2: string): number => {
|
|
76
|
-
const m = str1.length;
|
|
77
|
-
const n = str2.length;
|
|
78
|
-
const dp: number[][] = Array(m + 1)
|
|
79
|
-
.fill(null)
|
|
80
|
-
.map(() => Array(n + 1).fill(0));
|
|
81
|
-
|
|
82
|
-
for (let i = 0; i <= m; i++) {
|
|
83
|
-
dp[i][0] = i;
|
|
84
|
-
}
|
|
85
|
-
for (let j = 0; j <= n; j++) {
|
|
86
|
-
dp[0][j] = j;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
for (let i = 1; i <= m; i++) {
|
|
90
|
-
for (let j = 1; j <= n; j++) {
|
|
91
|
-
dp[i][j] =
|
|
92
|
-
str1[i - 1] === str2[j - 1] ? dp[i - 1][j - 1] : Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return dp[m][n];
|
|
97
|
-
};
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { AnthropicClient } from '@effect/ai-anthropic';
|
|
6
|
-
import { FetchHttpClient } from '@effect/platform';
|
|
7
|
-
import { describe, it } from '@effect/vitest';
|
|
8
|
-
import { Config, Effect, Layer, pipe } from 'effect';
|
|
9
|
-
|
|
10
|
-
import { AiService, AiServiceRouter, ToolExecutionService, ToolResolverService } from '@dxos/ai';
|
|
11
|
-
import { tapHttpErrors } from '@dxos/ai/testing';
|
|
12
|
-
import { TestHelpers } from '@dxos/effect';
|
|
13
|
-
import { DatabaseService } from '@dxos/functions';
|
|
14
|
-
import { log } from '@dxos/log';
|
|
15
|
-
import { DataType } from '@dxos/schema';
|
|
16
|
-
|
|
17
|
-
import { makeGraphWriterHandler, makeGraphWriterToolkit } from './graph';
|
|
18
|
-
import { AiSession } from '../session';
|
|
19
|
-
|
|
20
|
-
// import { type EchoTestBuilder } from '@dxos/echo-db/testing';
|
|
21
|
-
|
|
22
|
-
const TestLayer = pipe(
|
|
23
|
-
AiService.model('@anthropic/claude-3-5-sonnet-20241022'),
|
|
24
|
-
Layer.provideMerge(DatabaseService.notAvailable),
|
|
25
|
-
Layer.provideMerge(ToolResolverService.layerEmpty),
|
|
26
|
-
Layer.provideMerge(ToolExecutionService.layerEmpty),
|
|
27
|
-
Layer.provide(AiServiceRouter.AiServiceRouter),
|
|
28
|
-
Layer.provide(
|
|
29
|
-
AnthropicClient.layerConfig({
|
|
30
|
-
apiKey: Config.redacted('ANTHROPIC_API_KEY'),
|
|
31
|
-
transformClient: tapHttpErrors,
|
|
32
|
-
}),
|
|
33
|
-
),
|
|
34
|
-
Layer.provide(FetchHttpClient.layer),
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
describe('graph', () => {
|
|
38
|
-
// let builder: EchoTestBuilder;
|
|
39
|
-
// test('findRelatedSchema', async () => {
|
|
40
|
-
// const db = await createEchoDatabase();
|
|
41
|
-
// const relatedSchemas = await findRelatedSchema(db, Schema.Struct({}));
|
|
42
|
-
// });
|
|
43
|
-
|
|
44
|
-
it.effect.skip(
|
|
45
|
-
'calculator',
|
|
46
|
-
Effect.fn(
|
|
47
|
-
function* ({ expect }) {
|
|
48
|
-
const graphWriteToolkit = makeGraphWriterToolkit({ schema: [DataType.Project] });
|
|
49
|
-
|
|
50
|
-
const session = new AiSession();
|
|
51
|
-
const response = yield* session
|
|
52
|
-
.run({
|
|
53
|
-
prompt: 'What is 10 + 20?',
|
|
54
|
-
history: [],
|
|
55
|
-
toolkit: graphWriteToolkit,
|
|
56
|
-
})
|
|
57
|
-
.pipe(Effect.provide(makeGraphWriterHandler(graphWriteToolkit)));
|
|
58
|
-
log.info('response', { response });
|
|
59
|
-
},
|
|
60
|
-
Effect.provide(TestLayer),
|
|
61
|
-
TestHelpers.runIf(process.env.ANTHROPIC_API_KEY),
|
|
62
|
-
),
|
|
63
|
-
);
|
|
64
|
-
});
|
package/src/research/graph.ts
DELETED
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { AiTool, AiToolkit } from '@effect/ai';
|
|
6
|
-
import { Context, Effect, identity, Option, Schema, SchemaAST } from 'effect';
|
|
7
|
-
|
|
8
|
-
import type { Obj, Relation } from '@dxos/echo';
|
|
9
|
-
import { Filter, Query } from '@dxos/echo';
|
|
10
|
-
import { type EchoDatabase, type Queue } from '@dxos/echo-db';
|
|
11
|
-
import { isEncodedReference } from '@dxos/echo-protocol';
|
|
12
|
-
import {
|
|
13
|
-
create,
|
|
14
|
-
EntityKind,
|
|
15
|
-
getEntityKind,
|
|
16
|
-
getSchemaDXN,
|
|
17
|
-
getSchemaTypename,
|
|
18
|
-
getTypeAnnotation,
|
|
19
|
-
getTypeIdentifierAnnotation,
|
|
20
|
-
ObjectId,
|
|
21
|
-
ReferenceAnnotationId,
|
|
22
|
-
RelationSourceDXNId,
|
|
23
|
-
RelationSourceId,
|
|
24
|
-
RelationTargetDXNId,
|
|
25
|
-
RelationTargetId,
|
|
26
|
-
type BaseObject,
|
|
27
|
-
} from '@dxos/echo-schema';
|
|
28
|
-
import { mapAst } from '@dxos/effect';
|
|
29
|
-
import { ContextQueueService, DatabaseService } from '@dxos/functions';
|
|
30
|
-
import { DXN } from '@dxos/keys';
|
|
31
|
-
import { log } from '@dxos/log';
|
|
32
|
-
import { deepMapValues, isNonNullable } from '@dxos/util';
|
|
33
|
-
|
|
34
|
-
// TODO(burdon): Unify with the graph schema.
|
|
35
|
-
export const Subgraph = Schema.Struct({
|
|
36
|
-
/** Objects and relations. */
|
|
37
|
-
objects: Schema.Array(Schema.Any),
|
|
38
|
-
});
|
|
39
|
-
export interface Subgraph extends Schema.Schema.Type<typeof Subgraph> {}
|
|
40
|
-
|
|
41
|
-
export type RelatedSchema = {
|
|
42
|
-
schema: Schema.Schema.AnyNoContext;
|
|
43
|
-
kind: 'reference' | 'relation';
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Find all schemas that are related to the given schema.
|
|
48
|
-
*
|
|
49
|
-
* @param db
|
|
50
|
-
* @param schema
|
|
51
|
-
* @returns
|
|
52
|
-
*/
|
|
53
|
-
export const findRelatedSchema = async (
|
|
54
|
-
db: EchoDatabase,
|
|
55
|
-
anchor: Schema.Schema.AnyNoContext,
|
|
56
|
-
): Promise<RelatedSchema[]> => {
|
|
57
|
-
// TODO(dmaretskyi): Query stored schemas.
|
|
58
|
-
const allSchemas = [...db.graph.schemaRegistry.schemas];
|
|
59
|
-
|
|
60
|
-
// TODO(dmaretskyi): Also do references.
|
|
61
|
-
return allSchemas
|
|
62
|
-
.filter((schema) => {
|
|
63
|
-
if (getTypeAnnotation(schema)?.kind !== EntityKind.Relation) {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
isSchemaAddressableByDxn(anchor, DXN.parse(getTypeAnnotation(schema)!.sourceSchema!)) ||
|
|
69
|
-
isSchemaAddressableByDxn(anchor, DXN.parse(getTypeAnnotation(schema)!.targetSchema!))
|
|
70
|
-
);
|
|
71
|
-
})
|
|
72
|
-
.map(
|
|
73
|
-
(schema): RelatedSchema => ({
|
|
74
|
-
schema,
|
|
75
|
-
kind: 'relation',
|
|
76
|
-
}),
|
|
77
|
-
);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Non-strict DXN comparison.
|
|
82
|
-
* Returns true if the DXN could be resolved to the schema.
|
|
83
|
-
*/
|
|
84
|
-
const isSchemaAddressableByDxn = (schema: Schema.Schema.AnyNoContext, dxn: DXN): boolean => {
|
|
85
|
-
if (getTypeIdentifierAnnotation(schema) === dxn.toString()) {
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const t = dxn.asTypeDXN();
|
|
90
|
-
if (t) {
|
|
91
|
-
return t.type === getSchemaTypename(schema);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return false;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Perform vector search in the local database.
|
|
99
|
-
*/
|
|
100
|
-
// TODO(dmaretskyi): Rename `GraphReadToolkit`.
|
|
101
|
-
export class LocalSearchToolkit extends AiToolkit.make(
|
|
102
|
-
AiTool.make('search_local_search', {
|
|
103
|
-
description: 'Search the local database for information using a vector index',
|
|
104
|
-
parameters: {
|
|
105
|
-
query: Schema.String.annotations({
|
|
106
|
-
description: 'The query to search for. Could be a question or a topic or a set of keywords.',
|
|
107
|
-
}),
|
|
108
|
-
},
|
|
109
|
-
success: Schema.Unknown,
|
|
110
|
-
failure: Schema.Never,
|
|
111
|
-
}).addRequirement<DatabaseService>(),
|
|
112
|
-
) {}
|
|
113
|
-
|
|
114
|
-
export const LocalSearchHandler = LocalSearchToolkit.toLayer({
|
|
115
|
-
search_local_search: Effect.fn(function* ({ query }) {
|
|
116
|
-
const { objects } = yield* DatabaseService.runQuery(Query.select(Filter.text(query, { type: 'vector' })));
|
|
117
|
-
const results = [...objects];
|
|
118
|
-
|
|
119
|
-
const contextQueue = yield* Effect.serviceOption(ContextQueueService);
|
|
120
|
-
if (Option.isSome(contextQueue)) {
|
|
121
|
-
const queueObjects = yield* Effect.promise(() => contextQueue.value.contextQueue.queryObjects());
|
|
122
|
-
// TODO(dmaretskyi): Text search on the queue.
|
|
123
|
-
results.push(...queueObjects);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return `
|
|
127
|
-
<local_context>
|
|
128
|
-
${JSON.stringify(results, null, 2)}
|
|
129
|
-
</local_context>
|
|
130
|
-
`;
|
|
131
|
-
}),
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Attached as an annotation to the writer tool.
|
|
136
|
-
*/
|
|
137
|
-
class GraphWriterSchema extends Context.Tag('GraphWriterSchema')<
|
|
138
|
-
GraphWriterSchema,
|
|
139
|
-
{ schema: Schema.Schema.AnyNoContext[] }
|
|
140
|
-
>() {}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Forms typed objects that can be written to the graph database.
|
|
144
|
-
*/
|
|
145
|
-
export const makeGraphWriterToolkit = ({ schema }: { schema: Schema.Schema.AnyNoContext[] }) => {
|
|
146
|
-
return AiToolkit.make(
|
|
147
|
-
AiTool.make('graph_writer', {
|
|
148
|
-
description: 'Write to the local graph database',
|
|
149
|
-
parameters: createExtractionSchema(schema).fields,
|
|
150
|
-
success: Schema.Unknown,
|
|
151
|
-
failure: Schema.Never,
|
|
152
|
-
})
|
|
153
|
-
.addRequirement<DatabaseService>()
|
|
154
|
-
.annotateContext(Context.make(GraphWriterSchema, { schema })),
|
|
155
|
-
);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
export const makeGraphWriterHandler = (toolkit: ReturnType<typeof makeGraphWriterToolkit>) => {
|
|
159
|
-
const { schema } = Context.get(
|
|
160
|
-
toolkit.tools.graph_writer.annotations as Context.Context<GraphWriterSchema>,
|
|
161
|
-
GraphWriterSchema,
|
|
162
|
-
);
|
|
163
|
-
return toolkit.toLayer({
|
|
164
|
-
graph_writer: Effect.fn(function* (input) {
|
|
165
|
-
const { db } = yield* DatabaseService;
|
|
166
|
-
const contextQueue = yield* Effect.serviceOption(ContextQueueService);
|
|
167
|
-
const data = yield* Effect.promise(() =>
|
|
168
|
-
sanitizeObjects(schema, input as any, db, contextQueue.pipe(Option.getOrUndefined)?.contextQueue),
|
|
169
|
-
);
|
|
170
|
-
return data as Obj.Any[];
|
|
171
|
-
}),
|
|
172
|
-
});
|
|
173
|
-
};
|
|
174
|
-
/**
|
|
175
|
-
* Create a schema for structured data extraction.
|
|
176
|
-
*/
|
|
177
|
-
export const createExtractionSchema = (types: Schema.Schema.AnyNoContext[]) => {
|
|
178
|
-
return Schema.Struct({
|
|
179
|
-
...Object.fromEntries(
|
|
180
|
-
types.map(preprocessSchema).map((schema, index) => [
|
|
181
|
-
`objects_${getSanitizedSchemaName(types[index])}`,
|
|
182
|
-
Schema.optional(Schema.Array(schema)).annotations({
|
|
183
|
-
description: `The objects of type: ${getSchemaDXN(types[index])?.asTypeDXN()!.type}. ${SchemaAST.getDescriptionAnnotation(types[index].ast).pipe(Option.getOrElse(() => ''))}`,
|
|
184
|
-
}),
|
|
185
|
-
]),
|
|
186
|
-
),
|
|
187
|
-
});
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
export const getSanitizedSchemaName = (schema: Schema.Schema.AnyNoContext) => {
|
|
191
|
-
return getSchemaDXN(schema)!
|
|
192
|
-
.asTypeDXN()!
|
|
193
|
-
.type.replaceAll(/[^a-zA-Z0-9]+/g, '_');
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
export const sanitizeObjects = async (
|
|
197
|
-
types: Schema.Schema.AnyNoContext[],
|
|
198
|
-
data: Record<string, readonly unknown[]>,
|
|
199
|
-
db: EchoDatabase,
|
|
200
|
-
queue?: Queue,
|
|
201
|
-
): Promise<BaseObject[]> => {
|
|
202
|
-
const entries = types
|
|
203
|
-
.map(
|
|
204
|
-
(type) =>
|
|
205
|
-
data[`objects_${getSanitizedSchemaName(type)}`]?.map((object: any) => ({
|
|
206
|
-
data: object,
|
|
207
|
-
schema: type,
|
|
208
|
-
})) ?? [],
|
|
209
|
-
)
|
|
210
|
-
.flat();
|
|
211
|
-
|
|
212
|
-
const idMap = new Map<string, string>();
|
|
213
|
-
const existingIds = new Set<ObjectId>();
|
|
214
|
-
const enitties = new Map<ObjectId, Obj.Any | Relation.Any>();
|
|
215
|
-
|
|
216
|
-
const resolveId = (id: string): DXN | undefined => {
|
|
217
|
-
if (ObjectId.isValid(id)) {
|
|
218
|
-
existingIds.add(id);
|
|
219
|
-
return DXN.fromLocalObjectId(id);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const mappedId = idMap.get(id);
|
|
223
|
-
if (mappedId) {
|
|
224
|
-
return DXN.fromLocalObjectId(mappedId);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return undefined;
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const res = entries
|
|
231
|
-
.map((entry) => {
|
|
232
|
-
// This entry mutates existing object.
|
|
233
|
-
if (ObjectId.isValid(entry.data.id)) {
|
|
234
|
-
return entry;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
idMap.set(entry.data.id, ObjectId.random());
|
|
238
|
-
entry.data.id = idMap.get(entry.data.id);
|
|
239
|
-
return entry;
|
|
240
|
-
})
|
|
241
|
-
.map((entry) => {
|
|
242
|
-
const data = deepMapValues(entry.data, (value, recurse) => {
|
|
243
|
-
if (isEncodedReference(value)) {
|
|
244
|
-
const ref = value['/'];
|
|
245
|
-
const id = resolveId(ref);
|
|
246
|
-
|
|
247
|
-
if (id) {
|
|
248
|
-
// Link to an existing object.
|
|
249
|
-
return { '/': id.toString() };
|
|
250
|
-
} else {
|
|
251
|
-
// Search URIs?
|
|
252
|
-
return { '/': `search:?q=${encodeURIComponent(ref)}` };
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return recurse(value);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
if (getEntityKind(entry.schema) === 'relation') {
|
|
260
|
-
const sourceDxn = resolveId(data.source);
|
|
261
|
-
if (!sourceDxn) {
|
|
262
|
-
log.warn('source not found', { source: data.source });
|
|
263
|
-
}
|
|
264
|
-
const targetDxn = resolveId(data.target);
|
|
265
|
-
if (!targetDxn) {
|
|
266
|
-
log.warn('target not found', { target: data.target });
|
|
267
|
-
}
|
|
268
|
-
delete data.source;
|
|
269
|
-
delete data.target;
|
|
270
|
-
data[RelationSourceDXNId] = sourceDxn;
|
|
271
|
-
data[RelationTargetDXNId] = targetDxn;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
data,
|
|
276
|
-
schema: entry.schema,
|
|
277
|
-
};
|
|
278
|
-
})
|
|
279
|
-
.filter((object) => !existingIds.has(object.data.id)); // TODO(dmaretskyi): This dissallows updating existing objects.
|
|
280
|
-
|
|
281
|
-
// TODO(dmaretskyi): Use ref resolver.
|
|
282
|
-
const { objects: dbObjects } = await db.query(Query.select(Filter.ids(...existingIds))).run();
|
|
283
|
-
const queueObjects = (await queue?.getObjectsById([...existingIds])) ?? [];
|
|
284
|
-
const objects = [...dbObjects, ...queueObjects].filter(isNonNullable);
|
|
285
|
-
|
|
286
|
-
// TODO(dmaretskyi): Returns everything if IDs are empty!
|
|
287
|
-
log.info('objects', { dbObjects, queueObjects, existingIds });
|
|
288
|
-
const missing = Array.from(existingIds).filter((id) => !objects.some((object) => object.id === id));
|
|
289
|
-
if (missing.length > 0) {
|
|
290
|
-
throw new Error(`Object IDs do not point to existing objects: ${missing.join(', ')}`);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return res.flatMap(({ data, schema }) => {
|
|
294
|
-
let skip = false;
|
|
295
|
-
if (RelationSourceDXNId in data) {
|
|
296
|
-
const id = (data[RelationSourceDXNId] as DXN).asEchoDXN()?.echoId;
|
|
297
|
-
const obj = objects.find((object) => object.id === id) ?? enitties.get(id!);
|
|
298
|
-
if (obj) {
|
|
299
|
-
delete data[RelationSourceDXNId];
|
|
300
|
-
data[RelationSourceId] = obj;
|
|
301
|
-
} else {
|
|
302
|
-
skip = true;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if (RelationTargetDXNId in data) {
|
|
306
|
-
const id = (data[RelationTargetDXNId] as DXN).asEchoDXN()?.echoId;
|
|
307
|
-
const obj = objects.find((object) => object.id === id) ?? enitties.get(id!);
|
|
308
|
-
if (obj) {
|
|
309
|
-
delete data[RelationTargetDXNId];
|
|
310
|
-
data[RelationTargetId] = obj;
|
|
311
|
-
} else {
|
|
312
|
-
skip = true;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
if (!skip) {
|
|
316
|
-
const obj = create(schema, data);
|
|
317
|
-
enitties.set(obj.id, obj);
|
|
318
|
-
return [obj];
|
|
319
|
-
}
|
|
320
|
-
return [];
|
|
321
|
-
});
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
const SoftRef = Schema.Struct({
|
|
325
|
-
'/': Schema.String,
|
|
326
|
-
}).annotations({
|
|
327
|
-
description: 'Reference to another object.',
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
const preprocessSchema = (schema: Schema.Schema.AnyNoContext) => {
|
|
331
|
-
const isRelationSchema = getEntityKind(schema) === 'relation';
|
|
332
|
-
|
|
333
|
-
const go = (ast: SchemaAST.AST, visited = new Set<SchemaAST.AST>()): SchemaAST.AST => {
|
|
334
|
-
if (visited.has(ast)) {
|
|
335
|
-
// Already visited this node, prevent infinite recursion.
|
|
336
|
-
return ast;
|
|
337
|
-
}
|
|
338
|
-
visited.add(ast);
|
|
339
|
-
|
|
340
|
-
if (SchemaAST.getAnnotation(ast, ReferenceAnnotationId).pipe(Option.isSome)) {
|
|
341
|
-
return SoftRef.ast;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return mapAst(ast, (child) => go(child, visited));
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
return Schema.make<any, any, never>(mapAst(schema.ast, (ast) => go(ast))).pipe(
|
|
348
|
-
Schema.omit('id'),
|
|
349
|
-
Schema.extend(
|
|
350
|
-
Schema.Struct({
|
|
351
|
-
id: Schema.String.annotations({
|
|
352
|
-
description: 'The id of this object. Come up with a unique id based on your judgement.',
|
|
353
|
-
}),
|
|
354
|
-
}),
|
|
355
|
-
),
|
|
356
|
-
isRelationSchema
|
|
357
|
-
? Schema.extend(
|
|
358
|
-
Schema.Struct({
|
|
359
|
-
source: Schema.String.annotations({
|
|
360
|
-
description: 'The id of the source object for this relation.',
|
|
361
|
-
}),
|
|
362
|
-
target: Schema.String.annotations({
|
|
363
|
-
description: 'The id of the target object for this relation.',
|
|
364
|
-
}),
|
|
365
|
-
}),
|
|
366
|
-
)
|
|
367
|
-
: identity<Schema.Schema.AnyNoContext>,
|
|
368
|
-
);
|
|
369
|
-
};
|