@dxos/assistant-toolkit 0.8.4-main.ae835ea → 0.8.4-main.e8ec1fe
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/index.mjs +891 -595
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +890 -595
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/blueprints/research/research-blueprint.d.ts.map +1 -1
- package/dist/types/src/blueprints/websearch/websearch-toolkit.d.ts +1 -0
- package/dist/types/src/blueprints/websearch/websearch-toolkit.d.ts.map +1 -1
- package/dist/types/src/crud/graph.d.ts.map +1 -0
- package/dist/types/src/crud/graph.test.d.ts.map +1 -0
- package/dist/types/src/crud/index.d.ts +2 -0
- package/dist/types/src/crud/index.d.ts.map +1 -0
- package/dist/types/src/functions/agent/prompt.d.ts +3 -5
- package/dist/types/src/functions/agent/prompt.d.ts.map +1 -1
- package/dist/types/src/functions/discord/fetch-messages.d.ts +1 -1
- package/dist/types/src/functions/discord/index.d.ts +1 -1
- package/dist/types/src/functions/discord/index.d.ts.map +1 -1
- package/dist/types/src/functions/document/index.d.ts +3 -2
- package/dist/types/src/functions/document/index.d.ts.map +1 -1
- package/dist/types/src/functions/document/read.d.ts +1 -1
- package/dist/types/src/functions/document/update.d.ts +1 -1
- package/dist/types/src/functions/entity-extraction/entity-extraction.d.ts +2 -2
- package/dist/types/src/functions/entity-extraction/entity-extraction.d.ts.map +1 -1
- package/dist/types/src/functions/entity-extraction/index.d.ts +2 -2
- package/dist/types/src/functions/entity-extraction/index.d.ts.map +1 -1
- package/dist/types/src/functions/exa/exa.d.ts +1 -1
- package/dist/types/src/functions/exa/mock.d.ts +1 -1
- package/dist/types/src/functions/github/fetch-prs.d.ts +1 -1
- package/dist/types/src/functions/linear/index.d.ts +1 -1
- package/dist/types/src/functions/linear/index.d.ts.map +1 -1
- package/dist/types/src/functions/linear/sync-issues.d.ts +1 -1
- package/dist/types/src/functions/research/document-create.d.ts +9 -0
- package/dist/types/src/functions/research/document-create.d.ts.map +1 -0
- package/dist/types/src/functions/research/index.d.ts +8 -6
- package/dist/types/src/functions/research/index.d.ts.map +1 -1
- package/dist/types/src/functions/research/research.d.ts +4 -3
- package/dist/types/src/functions/research/research.d.ts.map +1 -1
- package/dist/types/src/functions/research/types.d.ts +2 -380
- package/dist/types/src/functions/research/types.d.ts.map +1 -1
- package/dist/types/src/functions/tasks/index.d.ts +2 -2
- package/dist/types/src/functions/tasks/index.d.ts.map +1 -1
- package/dist/types/src/functions/tasks/read.d.ts +1 -1
- package/dist/types/src/functions/tasks/update.d.ts +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/toolkits/AssistantToolkit.d.ts +17 -0
- package/dist/types/src/toolkits/AssistantToolkit.d.ts.map +1 -0
- package/dist/types/src/toolkits/AssistantToolkit.test.d.ts +2 -0
- package/dist/types/src/toolkits/AssistantToolkit.test.d.ts.map +1 -0
- package/dist/types/src/toolkits/SystemToolkit.d.ts +67 -0
- package/dist/types/src/toolkits/SystemToolkit.d.ts.map +1 -0
- package/dist/types/src/toolkits/index.d.ts +3 -0
- package/dist/types/src/toolkits/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -22
- package/src/blueprints/design/design-blueprint.test.ts +9 -14
- package/src/blueprints/design/design-blueprint.ts +2 -2
- package/src/blueprints/discord/discord-blueprint.ts +2 -2
- package/src/blueprints/linear/linear-blueprint.ts +2 -2
- package/src/blueprints/planning/planning-blueprint.test.ts +11 -16
- package/src/blueprints/planning/planning-blueprint.ts +2 -2
- package/src/blueprints/research/research-blueprint.ts +23 -15
- package/src/blueprints/websearch/websearch-blueprint.ts +2 -2
- package/src/{functions/research → crud}/graph.test.ts +2 -2
- package/src/crud/index.ts +5 -0
- package/src/experimental/feed.test.ts +11 -8
- package/src/functions/agent/prompt.ts +25 -12
- package/src/functions/discord/fetch-messages.test.ts +5 -6
- package/src/functions/discord/fetch-messages.ts +10 -9
- package/src/functions/document/index.ts +1 -0
- package/src/functions/entity-extraction/entity-extraction.conversations.json +1 -1
- package/src/functions/entity-extraction/entity-extraction.test.ts +11 -19
- package/src/functions/entity-extraction/entity-extraction.ts +15 -13
- package/src/functions/linear/linear.test.ts +13 -16
- package/src/functions/linear/sync-issues.ts +7 -7
- package/src/functions/research/{create-document.ts → document-create.ts} +32 -26
- package/src/functions/research/index.ts +1 -2
- package/src/functions/research/{instructions-research.tpl → research-instructions.tpl} +14 -6
- package/src/functions/research/research.conversations.json +1 -10714
- package/src/functions/research/research.test.ts +87 -148
- package/src/functions/research/research.ts +84 -49
- package/src/functions/research/types.ts +14 -12
- package/src/index.ts +2 -0
- package/src/toolkits/AssistantToolkit.conversations.json +1 -0
- package/src/toolkits/AssistantToolkit.test.ts +88 -0
- package/src/toolkits/AssistantToolkit.ts +47 -0
- package/src/toolkits/SystemToolkit.ts +231 -0
- package/src/toolkits/index.ts +6 -0
- package/dist/types/src/functions/research/create-document.d.ts +0 -7
- package/dist/types/src/functions/research/create-document.d.ts.map +0 -1
- package/dist/types/src/functions/research/graph.d.ts.map +0 -1
- package/dist/types/src/functions/research/graph.test.d.ts.map +0 -1
- /package/dist/types/src/{functions/research → crud}/graph.d.ts +0 -0
- /package/dist/types/src/{functions/research → crud}/graph.test.d.ts +0 -0
- /package/src/{functions/research → crud}/graph.ts +0 -0
|
@@ -9,7 +9,7 @@ import * as Effect from 'effect/Effect';
|
|
|
9
9
|
import * as Layer from 'effect/Layer';
|
|
10
10
|
|
|
11
11
|
import { AiService, ConsolePrinter, MemoizedAiService } from '@dxos/ai';
|
|
12
|
-
import {
|
|
12
|
+
import { TestAiService } from '@dxos/ai/testing';
|
|
13
13
|
import {
|
|
14
14
|
AiConversation,
|
|
15
15
|
type ContextBinding,
|
|
@@ -18,25 +18,26 @@ import {
|
|
|
18
18
|
makeToolResolverFromFunctions,
|
|
19
19
|
} from '@dxos/assistant';
|
|
20
20
|
import { Blueprint } from '@dxos/blueprints';
|
|
21
|
-
import { Obj,
|
|
21
|
+
import { Filter, Obj, Query, Ref } from '@dxos/echo';
|
|
22
22
|
import { TestHelpers, acquireReleaseResource } from '@dxos/effect';
|
|
23
23
|
import {
|
|
24
|
-
ComputeEventLogger,
|
|
25
24
|
CredentialsService,
|
|
26
25
|
DatabaseService,
|
|
27
26
|
FunctionInvocationService,
|
|
28
27
|
QueueService,
|
|
29
28
|
TracingService,
|
|
30
29
|
} from '@dxos/functions';
|
|
31
|
-
import { TestDatabaseLayer } from '@dxos/functions/testing';
|
|
30
|
+
import { FunctionInvocationServiceLayerTest, TestDatabaseLayer } from '@dxos/functions-runtime/testing';
|
|
31
|
+
import { invariant } from '@dxos/invariant';
|
|
32
32
|
import { ObjectId } from '@dxos/keys';
|
|
33
|
-
import {
|
|
33
|
+
import { MarkdownBlueprint, MarkdownFunction } from '@dxos/plugin-markdown/toolkit';
|
|
34
|
+
import { Markdown } from '@dxos/plugin-markdown/types';
|
|
35
|
+
import { HasSubject, type Message, Organization } from '@dxos/types';
|
|
34
36
|
|
|
35
37
|
import { ResearchBlueprint } from '../../blueprints';
|
|
36
38
|
import { testToolkit } from '../../blueprints/testing';
|
|
37
39
|
|
|
38
|
-
import createDocument from './create
|
|
39
|
-
import { createExtractionSchema, getSanitizedSchemaName } from './graph';
|
|
40
|
+
import { default as createDocument } from './document-create';
|
|
40
41
|
import { default as research } from './research';
|
|
41
42
|
import { ResearchGraph, queryResearchGraph } from './research-graph';
|
|
42
43
|
import { ResearchDataTypes } from './types';
|
|
@@ -45,41 +46,45 @@ ObjectId.dangerouslyDisableRandomness();
|
|
|
45
46
|
|
|
46
47
|
const TestLayer = Layer.mergeAll(
|
|
47
48
|
AiService.model('@anthropic/claude-opus-4-0'),
|
|
48
|
-
makeToolResolverFromFunctions(
|
|
49
|
+
makeToolResolverFromFunctions(
|
|
50
|
+
[research, createDocument, MarkdownFunction.create, MarkdownFunction.open, MarkdownFunction.update],
|
|
51
|
+
testToolkit,
|
|
52
|
+
),
|
|
49
53
|
makeToolExecutionServiceFromFunctions(testToolkit, testToolkit.toLayer({}) as any),
|
|
50
|
-
ComputeEventLogger.layerFromTracing,
|
|
51
54
|
).pipe(
|
|
52
|
-
Layer.provideMerge(
|
|
55
|
+
Layer.provideMerge(
|
|
56
|
+
FunctionInvocationServiceLayerTest({
|
|
57
|
+
functions: [research, createDocument, MarkdownFunction.create, MarkdownFunction.open, MarkdownFunction.update],
|
|
58
|
+
}),
|
|
59
|
+
),
|
|
53
60
|
Layer.provideMerge(
|
|
54
61
|
Layer.mergeAll(
|
|
55
|
-
|
|
62
|
+
TestAiService(),
|
|
56
63
|
TestDatabaseLayer({
|
|
64
|
+
spaceKey: 'fixed',
|
|
57
65
|
indexing: { vector: true },
|
|
58
|
-
types: [...ResearchDataTypes, ResearchGraph, Blueprint.Blueprint],
|
|
66
|
+
types: [...ResearchDataTypes, ResearchGraph, Blueprint.Blueprint, Markdown.Document, HasSubject.HasSubject],
|
|
59
67
|
}),
|
|
60
|
-
CredentialsService.configuredLayer([
|
|
68
|
+
CredentialsService.configuredLayer([]),
|
|
61
69
|
TracingService.layerNoop,
|
|
62
70
|
),
|
|
63
71
|
),
|
|
64
72
|
);
|
|
65
73
|
|
|
66
|
-
|
|
67
|
-
describe.skip('Research', () => {
|
|
74
|
+
describe('Research', () => {
|
|
68
75
|
it.effect(
|
|
69
76
|
'call a function to generate a research report',
|
|
70
77
|
Effect.fnUntraced(
|
|
71
78
|
function* (_) {
|
|
72
79
|
yield* DatabaseService.add(
|
|
73
|
-
Obj.make(
|
|
74
|
-
name: '
|
|
75
|
-
website: 'https://
|
|
80
|
+
Obj.make(Organization.Organization, {
|
|
81
|
+
name: 'BlueYard',
|
|
82
|
+
website: 'https://blueyard.com',
|
|
76
83
|
}),
|
|
77
84
|
);
|
|
78
85
|
yield* DatabaseService.flush({ indexes: true });
|
|
79
|
-
|
|
80
86
|
const result = yield* FunctionInvocationService.invokeFunction(research, {
|
|
81
|
-
query: 'Founders and
|
|
82
|
-
mockSearch: false,
|
|
87
|
+
query: 'Founders and portfolio of BlueYard.',
|
|
83
88
|
});
|
|
84
89
|
|
|
85
90
|
console.log(inspect(result, { depth: null, colors: true }));
|
|
@@ -87,154 +92,88 @@ describe.skip('Research', () => {
|
|
|
87
92
|
|
|
88
93
|
yield* DatabaseService.flush({ indexes: true });
|
|
89
94
|
const researchGraph = yield* queryResearchGraph();
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
if (researchGraph) {
|
|
96
|
+
const data = yield* DatabaseService.load(researchGraph.queue).pipe(
|
|
97
|
+
Effect.flatMap((queue) => Effect.promise(() => queue.queryObjects())),
|
|
98
|
+
);
|
|
99
|
+
console.log(inspect(data, { depth: null, colors: true }));
|
|
100
|
+
}
|
|
94
101
|
},
|
|
95
102
|
Effect.provide(TestLayer),
|
|
96
103
|
TestHelpers.provideTestContext,
|
|
97
104
|
),
|
|
98
|
-
MemoizedAiService.isGenerationEnabled() ? 240_000 :
|
|
105
|
+
MemoizedAiService.isGenerationEnabled() ? 240_000 : 30_000,
|
|
99
106
|
);
|
|
100
107
|
|
|
101
108
|
it.scoped(
|
|
102
|
-
'research
|
|
109
|
+
'create and update research report',
|
|
103
110
|
Effect.fnUntraced(
|
|
104
111
|
function* (_) {
|
|
105
|
-
const
|
|
112
|
+
const organization = yield* DatabaseService.add(
|
|
113
|
+
Obj.make(Organization.Organization, {
|
|
114
|
+
name: 'BlueYard',
|
|
115
|
+
website: 'https://blueyard.com',
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const queue = yield* QueueService.createQueue<Message.Message | ContextBinding>();
|
|
106
120
|
const conversation = yield* acquireReleaseResource(() => new AiConversation(queue));
|
|
107
121
|
|
|
108
|
-
const org = Obj.make(DataType.Organization, { name: 'Airbnb', website: 'https://www.airbnb.com/' });
|
|
109
|
-
yield* DatabaseService.add(org);
|
|
110
122
|
yield* DatabaseService.flush({ indexes: true });
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
yield* Effect.promise(() =>
|
|
123
|
+
const researchBlueprint = yield* DatabaseService.add(Obj.clone(ResearchBlueprint));
|
|
124
|
+
const markdownBlueprint = yield* DatabaseService.add(Obj.clone(MarkdownBlueprint));
|
|
125
|
+
yield* Effect.promise(() =>
|
|
126
|
+
conversation.context.bind({
|
|
127
|
+
blueprints: [Ref.make(researchBlueprint), Ref.make(markdownBlueprint)],
|
|
128
|
+
objects: [Ref.make(organization)],
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
114
131
|
|
|
115
132
|
const observer = GenerationObserver.fromPrinter(new ConsolePrinter());
|
|
133
|
+
|
|
116
134
|
yield* conversation.createRequest({
|
|
117
135
|
observer,
|
|
118
|
-
prompt: `
|
|
136
|
+
prompt: `Create a research summary about ${organization.name}.`,
|
|
119
137
|
});
|
|
138
|
+
{
|
|
139
|
+
const { objects: docs } = yield* DatabaseService.runQuery(
|
|
140
|
+
Query.select(Filter.ids(organization.id)).targetOf(HasSubject.HasSubject).source(),
|
|
141
|
+
);
|
|
142
|
+
if (docs.length !== 1) {
|
|
143
|
+
throw new Error(`Expected 1 research document; got ${docs.length}: ${docs.map((_) => _.name)}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const doc = docs[0];
|
|
147
|
+
invariant(Obj.instanceOf(Markdown.Document, doc));
|
|
148
|
+
console.log({
|
|
149
|
+
name: doc.name,
|
|
150
|
+
content: yield* DatabaseService.load(doc.content).pipe(Effect.map((_) => _.content)),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
120
153
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
125
|
-
|
|
154
|
+
yield* conversation.createRequest({
|
|
155
|
+
observer,
|
|
156
|
+
prompt: 'Add a section about their portfolio.',
|
|
157
|
+
});
|
|
158
|
+
{
|
|
159
|
+
const { objects: docs } = yield* DatabaseService.runQuery(
|
|
160
|
+
Query.select(Filter.ids(organization.id)).targetOf(HasSubject.HasSubject).source(),
|
|
161
|
+
);
|
|
162
|
+
if (docs.length !== 1) {
|
|
163
|
+
throw new Error(`Expected 1 research document; got ${docs.length}: ${docs.map((_) => _.name)}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const doc = docs[0];
|
|
167
|
+
invariant(Obj.instanceOf(Markdown.Document, doc));
|
|
168
|
+
console.log({
|
|
169
|
+
name: doc.name,
|
|
170
|
+
content: yield* DatabaseService.load(doc.content).pipe(Effect.map((_) => _.content)),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
126
173
|
},
|
|
127
174
|
Effect.provide(TestLayer),
|
|
128
175
|
TestHelpers.provideTestContext,
|
|
129
176
|
),
|
|
130
|
-
MemoizedAiService.isGenerationEnabled() ? 240_000 :
|
|
177
|
+
MemoizedAiService.isGenerationEnabled() ? 240_000 : 30_000,
|
|
131
178
|
);
|
|
132
179
|
});
|
|
133
|
-
|
|
134
|
-
describe('misc', () => {
|
|
135
|
-
it('createExtractionSchema', () => {
|
|
136
|
-
const _schema = createExtractionSchema(ResearchDataTypes);
|
|
137
|
-
// log.info('schema', { schema });
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('getSanitizedSchemaName', () => {
|
|
141
|
-
const _names = ResearchDataTypes.map(getSanitizedSchemaName);
|
|
142
|
-
// log.info('names', { names }) ;
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('getTypeAnnotation', () => {
|
|
146
|
-
for (const schema of ResearchDataTypes) {
|
|
147
|
-
const _dxn = Type.getDXN(schema);
|
|
148
|
-
// log.info('dxn', { schema, dxn });
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it.skip('sanitizeObjects', () => {
|
|
153
|
-
const _TEST_DATA = {
|
|
154
|
-
objects_dxos_org_type_Project: [
|
|
155
|
-
{
|
|
156
|
-
name: 'Reor',
|
|
157
|
-
description:
|
|
158
|
-
'A private & local AI personal knowledge management app focused on high entropy thinkers. Features include Q&A chat with full context of notes, automatic connection of ideas, semantic search, and local-first architecture with LLMs and vector databases.',
|
|
159
|
-
image: 'https://reorhomepage-2-cwy0zagzg-reor-team.vercel.app/opengraph-image.jpg',
|
|
160
|
-
id: 'reor-project-01',
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
name: 'Personal AI',
|
|
164
|
-
description:
|
|
165
|
-
'A platform for creating personal AI models with unlimited memory capabilities. Features include memory stacking, data uploads, and personalized language models that can be trained on individual knowledge bases.',
|
|
166
|
-
image: 'https://cdn.prod.website-files.com/5ff65c460ce39f5ec5681c6a/663d12aab1b425e1ad40d3a6_Memory-min.jpg',
|
|
167
|
-
id: 'personal-ai-project-01',
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
name: 'IKI AI',
|
|
171
|
-
description:
|
|
172
|
-
'An AI-native workspace for research, strategy, and creative work. Offers features like AI summarization, content analysis, and team knowledge sharing capabilities.',
|
|
173
|
-
image: 'https://framerusercontent.com/assets/cI6Uo7x4q0W3uxzOt2preXjv6aE.jpg',
|
|
174
|
-
id: 'iki-ai-project-01',
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
name: 'Forethink',
|
|
178
|
-
description:
|
|
179
|
-
'A privacy-first, on-device AI system that proactively links current activities to relevant knowledge. Features real-time learning across teams and local data processing.',
|
|
180
|
-
image: 'https://framerusercontent.com/assets/XDhN6tICnlElB134JWcWXOuXc.jpeg',
|
|
181
|
-
id: 'forethink-project-01',
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
name: 'Amurex',
|
|
185
|
-
description:
|
|
186
|
-
'An invisible AI companion that handles tasks, remembers important information, and supports workflow. Includes features for meeting automation, email categorization, and cross-platform search.',
|
|
187
|
-
image: 'https://amurex.ai/ogimages/og_amurex.jpg',
|
|
188
|
-
id: 'amurex-project-01',
|
|
189
|
-
},
|
|
190
|
-
],
|
|
191
|
-
objects_dxos_org_type_Organization: [
|
|
192
|
-
{
|
|
193
|
-
name: 'Reor',
|
|
194
|
-
description:
|
|
195
|
-
'A technology company focused on developing local-first AI solutions for personal knowledge management.',
|
|
196
|
-
status: 'active',
|
|
197
|
-
website: 'https://www.reorproject.org',
|
|
198
|
-
id: 'reor-org-01',
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
name: 'Personal.ai',
|
|
202
|
-
description: 'A company specializing in personal AI development and memory management solutions.',
|
|
203
|
-
status: 'active',
|
|
204
|
-
website: 'https://www.personal.ai',
|
|
205
|
-
id: 'personal-ai-org-01',
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
name: 'IKI AI',
|
|
209
|
-
description: 'A startup backed by 500 Global, developing AI-native workspaces for knowledge workers.',
|
|
210
|
-
status: 'active',
|
|
211
|
-
website: 'https://iki.ai',
|
|
212
|
-
id: 'iki-ai-org-01',
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
name: 'Forethink.ai',
|
|
216
|
-
description:
|
|
217
|
-
'A company focused on privacy-first AI solutions for knowledge management and team collaboration.',
|
|
218
|
-
status: 'active',
|
|
219
|
-
website: 'https://www.forethink.ai',
|
|
220
|
-
id: 'forethink-org-01',
|
|
221
|
-
},
|
|
222
|
-
],
|
|
223
|
-
objects_dxos_org_type_Text: [
|
|
224
|
-
{
|
|
225
|
-
content:
|
|
226
|
-
'Current trends in AI-powered Personal Knowledge Management (PKM) systems include: 1) Shift from document-based to graph-based systems for better knowledge connection, 2) Integration of large language models for enhanced content understanding and retrieval, 3) Focus on privacy-first and local-first architectures, 4) Emphasis on automatic knowledge connection and discovery, 5) Implementation of semantic search capabilities.',
|
|
227
|
-
id: 'pkm-trends-text-01',
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
content:
|
|
231
|
-
'Key features emerging across modern PKM tools include: automated content categorization, semantic analysis and linking, personalized AI models that learn from user interaction, integration with existing workflows, and robust privacy protection. These tools are increasingly focusing on proactive knowledge surfacing rather than passive storage.',
|
|
232
|
-
id: 'pkm-features-text-01',
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// const data = await sanitizeObjects(TYPES, TEST_DATA, db);
|
|
238
|
-
// log.info('data', { data });
|
|
239
|
-
});
|
|
240
|
-
});
|
|
@@ -6,8 +6,11 @@ import * as Toolkit from '@effect/ai/Toolkit';
|
|
|
6
6
|
import * as AnthropicTool from '@effect/ai-anthropic/AnthropicTool';
|
|
7
7
|
import * as Array from 'effect/Array';
|
|
8
8
|
import * as Effect from 'effect/Effect';
|
|
9
|
+
import * as Function from 'effect/Function';
|
|
9
10
|
import * as Layer from 'effect/Layer';
|
|
11
|
+
import * as Option from 'effect/Option';
|
|
10
12
|
import * as Schema from 'effect/Schema';
|
|
13
|
+
import * as String from 'effect/String';
|
|
11
14
|
|
|
12
15
|
import { AiService, ConsolePrinter } from '@dxos/ai';
|
|
13
16
|
import {
|
|
@@ -17,17 +20,18 @@ import {
|
|
|
17
20
|
makeToolExecutionServiceFromFunctions,
|
|
18
21
|
makeToolResolverFromFunctions,
|
|
19
22
|
} from '@dxos/assistant';
|
|
23
|
+
import { Template } from '@dxos/blueprints';
|
|
20
24
|
import { type DXN, Obj } from '@dxos/echo';
|
|
21
|
-
import { DatabaseService,
|
|
22
|
-
import {
|
|
25
|
+
import { DatabaseService, TracingService, defineFunction } from '@dxos/functions';
|
|
26
|
+
import { FunctionInvocationServiceLayerTestMocked } from '@dxos/functions-runtime/testing';
|
|
27
|
+
import { type Message, Person } from '@dxos/types';
|
|
23
28
|
import { trim } from '@dxos/util';
|
|
24
29
|
|
|
30
|
+
import { LocalSearchHandler, LocalSearchToolkit, makeGraphWriterHandler, makeGraphWriterToolkit } from '../../crud';
|
|
25
31
|
import { exaFunction, exaMockFunction } from '../exa';
|
|
26
32
|
|
|
27
|
-
import { LocalSearchHandler, LocalSearchToolkit, makeGraphWriterHandler, makeGraphWriterToolkit } from './graph';
|
|
28
|
-
// TODO(dmaretskyi): Vite build bug with instruction files with the same filename getting mixed-up.
|
|
29
|
-
import PROMPT from './instructions-research.tpl?raw';
|
|
30
33
|
import { contextQueueLayerFromResearchGraph } from './research-graph';
|
|
34
|
+
import PROMPT from './research-instructions.tpl?raw';
|
|
31
35
|
import { ResearchDataTypes } from './types';
|
|
32
36
|
|
|
33
37
|
/**
|
|
@@ -37,22 +41,21 @@ export default defineFunction({
|
|
|
37
41
|
key: 'dxos.org/function/research',
|
|
38
42
|
name: 'Research',
|
|
39
43
|
description: trim`
|
|
40
|
-
|
|
44
|
+
Search the web to research information about the given subject.
|
|
41
45
|
Inserts structured data into the research graph.
|
|
42
|
-
|
|
46
|
+
Creates a research summary and returns the objects created.
|
|
43
47
|
`,
|
|
44
48
|
inputSchema: Schema.Struct({
|
|
45
49
|
query: Schema.String.annotations({
|
|
46
50
|
description: trim`
|
|
47
|
-
The
|
|
48
|
-
If doing research on an object
|
|
51
|
+
The search query.
|
|
52
|
+
If doing research on an object then load it first and pass it as JSON.
|
|
49
53
|
`,
|
|
50
54
|
}),
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
instructions: Schema.optional(Schema.String).annotations({
|
|
53
57
|
description: trim`
|
|
54
58
|
The instructions for the research agent.
|
|
55
|
-
E.g., preference on fast responses or in-depth analysis, number of web searcher or the objects created.
|
|
56
59
|
`,
|
|
57
60
|
}),
|
|
58
61
|
|
|
@@ -61,20 +64,28 @@ export default defineFunction({
|
|
|
61
64
|
description: 'Whether to use the mock search tool.',
|
|
62
65
|
default: false,
|
|
63
66
|
}),
|
|
67
|
+
|
|
68
|
+
entityExtraction: Schema.optional(Schema.Boolean).annotations({
|
|
69
|
+
description: trim`
|
|
70
|
+
Whether to extract structured entities from the research.
|
|
71
|
+
Experimental feature only enable if user explicitly requests it.
|
|
72
|
+
`,
|
|
73
|
+
default: false,
|
|
74
|
+
}),
|
|
64
75
|
}),
|
|
65
76
|
outputSchema: Schema.Struct({
|
|
66
|
-
|
|
67
|
-
description: '
|
|
77
|
+
document: Schema.optional(Schema.String).annotations({
|
|
78
|
+
description: 'The generated research document.',
|
|
68
79
|
}),
|
|
69
80
|
objects: Schema.Array(Schema.Unknown).annotations({
|
|
70
|
-
description: '
|
|
81
|
+
description: 'Structured objects created during the research process.',
|
|
71
82
|
}),
|
|
72
83
|
}),
|
|
73
84
|
handler: Effect.fnUntraced(
|
|
74
|
-
function* ({ data: { query, mockSearch,
|
|
85
|
+
function* ({ data: { query, instructions, mockSearch = false, entityExtraction = false } }) {
|
|
75
86
|
if (mockSearch) {
|
|
76
87
|
const mockPerson = yield* DatabaseService.add(
|
|
77
|
-
Obj.make(
|
|
88
|
+
Obj.make(Person.Person, {
|
|
78
89
|
preferredName: 'John Doe',
|
|
79
90
|
emails: [{ value: 'john.doe@example.com' }],
|
|
80
91
|
phoneNumbers: [{ value: '123-456-7890' }],
|
|
@@ -82,9 +93,8 @@ export default defineFunction({
|
|
|
82
93
|
);
|
|
83
94
|
|
|
84
95
|
return {
|
|
85
|
-
|
|
86
|
-
The research
|
|
87
|
-
Proceed as usual.
|
|
96
|
+
document: trim`
|
|
97
|
+
The research ran in test-mode and was mocked. Proceed as usual.
|
|
88
98
|
We reference John Doe to test reference: ${Obj.getDXN(mockPerson)}
|
|
89
99
|
`,
|
|
90
100
|
objects: [Obj.toJSON(mockPerson)],
|
|
@@ -92,49 +102,47 @@ export default defineFunction({
|
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
yield* DatabaseService.flush({ indexes: true });
|
|
95
|
-
yield* TracingService.emitStatus({ message: '
|
|
105
|
+
yield* TracingService.emitStatus({ message: 'Starting research...' });
|
|
96
106
|
|
|
97
|
-
const objectDXNs: DXN[] = [];
|
|
98
|
-
const GraphWriterToolkit = makeGraphWriterToolkit({ schema: ResearchDataTypes });
|
|
99
|
-
const GraphWriterHandler = makeGraphWriterHandler(GraphWriterToolkit, {
|
|
100
|
-
onAppend: (dxns) => objectDXNs.push(...dxns),
|
|
101
|
-
});
|
|
102
107
|
const NativeWebSearch = Toolkit.make(AnthropicTool.WebSearch_20250305({}));
|
|
103
108
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
115
|
-
|
|
109
|
+
let toolkit: Toolkit.Any = NativeWebSearch;
|
|
110
|
+
let handlers: Layer.Layer<any, any> = Layer.empty as any;
|
|
111
|
+
|
|
112
|
+
const objectDXNs: DXN[] = [];
|
|
113
|
+
if (entityExtraction) {
|
|
114
|
+
const GraphWriterToolkit = makeGraphWriterToolkit({ schema: ResearchDataTypes });
|
|
115
|
+
const GraphWriterHandler = makeGraphWriterHandler(GraphWriterToolkit, {
|
|
116
|
+
onAppend: (dxns) => objectDXNs.push(...dxns),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
toolkit = Toolkit.merge(toolkit, LocalSearchToolkit, GraphWriterToolkit);
|
|
120
|
+
handlers = Layer.mergeAll(handlers, LocalSearchHandler, GraphWriterHandler).pipe(
|
|
121
|
+
Layer.provide(contextQueueLayerFromResearchGraph),
|
|
122
|
+
) as any;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const finishedToolkit = yield* createToolkit({
|
|
126
|
+
toolkit: toolkit as any,
|
|
127
|
+
}).pipe(Effect.provide(handlers));
|
|
116
128
|
|
|
117
129
|
const session = new AiSession();
|
|
118
130
|
const result = yield* session.run({
|
|
119
131
|
prompt: query,
|
|
120
|
-
system:
|
|
121
|
-
PROMPT
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
toolkit,
|
|
132
|
+
system: join(
|
|
133
|
+
Template.process(PROMPT, { entityExtraction }),
|
|
134
|
+
instructions && `<instructions>${instructions}</instructions>`,
|
|
135
|
+
),
|
|
136
|
+
toolkit: finishedToolkit,
|
|
126
137
|
observer: GenerationObserver.fromPrinter(new ConsolePrinter({ tag: 'research' })),
|
|
127
138
|
});
|
|
128
|
-
|
|
129
|
-
.at(-1)
|
|
130
|
-
?.blocks.filter((block) => block._tag === 'text')
|
|
131
|
-
.at(-1)?.text;
|
|
139
|
+
|
|
132
140
|
const objects = yield* Effect.forEach(objectDXNs, (dxn) => DatabaseService.resolve(dxn)).pipe(
|
|
133
141
|
Effect.map(Array.map((obj) => Obj.toJSON(obj))),
|
|
134
142
|
);
|
|
135
143
|
|
|
136
144
|
return {
|
|
137
|
-
|
|
145
|
+
document: extractLastTextBlock(result),
|
|
138
146
|
objects,
|
|
139
147
|
};
|
|
140
148
|
},
|
|
@@ -147,9 +155,36 @@ export default defineFunction({
|
|
|
147
155
|
).pipe(
|
|
148
156
|
Layer.provide(
|
|
149
157
|
// TODO(dmaretskyi): This should be provided by environment.
|
|
150
|
-
Layer.mergeAll(
|
|
158
|
+
Layer.mergeAll(FunctionInvocationServiceLayerTestMocked({ functions: [exaFunction, exaMockFunction] })),
|
|
151
159
|
),
|
|
152
160
|
),
|
|
153
161
|
),
|
|
154
162
|
),
|
|
155
163
|
});
|
|
164
|
+
|
|
165
|
+
// TODO(burdon): Factor out.
|
|
166
|
+
const join = (...strings: (string | undefined)[]) => strings.filter(Boolean).join('\n\n');
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Extracts the last text block from the result.
|
|
170
|
+
* Skips citations.
|
|
171
|
+
*/
|
|
172
|
+
// TODO(burdon): Factor out.
|
|
173
|
+
const extractLastTextBlock = (result: Message.Message[]) => {
|
|
174
|
+
return Function.pipe(
|
|
175
|
+
result,
|
|
176
|
+
Array.last,
|
|
177
|
+
Option.map(
|
|
178
|
+
Function.flow(
|
|
179
|
+
(_: Message.Message) => _.blocks,
|
|
180
|
+
Array.reverse,
|
|
181
|
+
Array.dropWhile((_: any) => _._tag === 'summary'),
|
|
182
|
+
Array.takeWhile((_: any) => _._tag === 'text'),
|
|
183
|
+
Array.reverse,
|
|
184
|
+
Array.map((_: any) => _.text),
|
|
185
|
+
Array.reduce('', String.concat),
|
|
186
|
+
),
|
|
187
|
+
),
|
|
188
|
+
Option.getOrElse(() => ''),
|
|
189
|
+
);
|
|
190
|
+
};
|
|
@@ -2,23 +2,25 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type Type } from '@dxos/echo';
|
|
6
|
+
import { Text } from '@dxos/schema';
|
|
7
|
+
import { Event, HasConnection, HasRelationship, LegacyOrganization, LegacyPerson, Project, Task } from '@dxos/types';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Data types for research.
|
|
9
11
|
*/
|
|
10
|
-
export const ResearchDataTypes = [
|
|
12
|
+
export const ResearchDataTypes: (Type.Obj.Any | Type.Relation.Any)[] = [
|
|
11
13
|
// Objects
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
DataType.Task,
|
|
17
|
-
DataType.Text,
|
|
14
|
+
Event.Event,
|
|
15
|
+
LegacyOrganization,
|
|
16
|
+
Task.Task,
|
|
17
|
+
Text.Text,
|
|
18
18
|
|
|
19
19
|
// Relations
|
|
20
|
-
// TODO(wittjosiah): Until views (e.g
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
// TODO(wittjosiah): Until views (e.g., Table) support relations this needs to be expressed via organization ref.
|
|
21
|
+
// Employer.Employer,
|
|
22
|
+
LegacyPerson,
|
|
23
|
+
Project.Project,
|
|
24
|
+
HasRelationship.HasRelationship,
|
|
25
|
+
HasConnection.HasConnection,
|
|
24
26
|
];
|