@dxos/assistant 0.8.4-main.f9ba587 → 0.8.4-main.fcfe5033a5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/lib/neutral/chunk-2TBLG3XY.mjs +1793 -0
- package/dist/lib/neutral/chunk-2TBLG3XY.mjs.map +7 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/neutral/extraction/index.mjs +219 -0
- package/dist/lib/neutral/extraction/index.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +537 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/testing/index.mjs +111 -0
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/conversation/context.d.ts +155 -0
- package/dist/types/src/conversation/context.d.ts.map +1 -0
- package/dist/types/src/conversation/context.test.d.ts +2 -0
- package/dist/types/src/conversation/context.test.d.ts.map +1 -0
- package/dist/types/src/conversation/index.d.ts +2 -1
- package/dist/types/src/conversation/index.d.ts.map +1 -1
- package/dist/types/src/conversation/session.d.ts +78 -0
- package/dist/types/src/conversation/session.d.ts.map +1 -0
- package/dist/types/src/conversation/session.test.d.ts.map +1 -0
- package/dist/types/src/diff.d.ts +17 -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 +67 -0
- package/dist/types/src/errors.d.ts.map +1 -0
- package/dist/types/src/execution-graph.d.ts +85 -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-ner-function/index.d.ts +0 -1
- package/dist/types/src/extraction/extraction-ner-function/index.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts +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 +1066 -215
- package/dist/types/src/extraction/extraction.d.ts.map +1 -1
- package/dist/types/src/extraction/index.d.ts +0 -1
- package/dist/types/src/extraction/index.d.ts.map +1 -1
- package/dist/types/src/extraction/quotes.d.ts +3 -3
- package/dist/types/src/extraction/quotes.d.ts.map +1 -1
- package/dist/types/src/functions/index.d.ts +2 -0
- package/dist/types/src/functions/index.d.ts.map +1 -0
- package/dist/types/src/functions/services.d.ts +17 -0
- package/dist/types/src/functions/services.d.ts.map +1 -0
- package/dist/types/src/functions.test.d.ts +2 -0
- package/dist/types/src/functions.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +7 -5
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/service/AgentService.d.ts +75 -0
- package/dist/types/src/service/AgentService.d.ts.map +1 -0
- package/dist/types/src/service/AgentService.test.d.ts +2 -0
- package/dist/types/src/service/AgentService.test.d.ts.map +1 -0
- package/dist/types/src/service/agent-process.d.ts +21 -0
- package/dist/types/src/service/agent-process.d.ts.map +1 -0
- package/dist/types/src/service/index.d.ts +3 -0
- package/dist/types/src/service/index.d.ts.map +1 -0
- package/dist/types/src/session/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 +16 -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 +5 -1
- package/dist/types/src/session/index.d.ts.map +1 -1
- package/dist/types/src/session/observer.d.ts +23 -0
- package/dist/types/src/session/observer.d.ts.map +1 -0
- package/dist/types/src/session/request.d.ts +106 -0
- package/dist/types/src/session/request.d.ts.map +1 -0
- package/dist/types/src/session/request.test.d.ts +2 -0
- package/dist/types/src/session/request.test.d.ts.map +1 -0
- package/dist/types/src/session/session-ollama.test.d.ts +2 -0
- package/dist/types/src/session/session-ollama.test.d.ts.map +1 -0
- package/dist/types/src/session/toolkit.d.ts +16 -0
- package/dist/types/src/session/toolkit.d.ts.map +1 -0
- package/dist/types/src/session/version-pin.d.ts +5 -5
- package/dist/types/src/session/version-pin.d.ts.map +1 -1
- package/dist/types/src/templates/index.d.ts +2 -0
- package/dist/types/src/templates/index.d.ts.map +1 -0
- package/dist/types/src/templates/system.d.ts +17 -0
- package/dist/types/src/templates/system.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +1 -2
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/layer.d.ts +40 -0
- package/dist/types/src/testing/layer.d.ts.map +1 -0
- package/dist/types/src/tracing.d.ts +223 -0
- package/dist/types/src/tracing.d.ts.map +1 -0
- package/dist/types/src/types/artifact.d.ts +28 -0
- 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 +60 -29
- package/src/conversation/context.test.ts +52 -0
- package/src/conversation/context.ts +403 -0
- package/src/conversation/index.ts +2 -1
- package/src/conversation/session.test.ts +58 -0
- package/src/conversation/session.ts +300 -0
- package/src/diff.test.ts +74 -0
- package/src/diff.ts +93 -0
- package/src/errors.ts +15 -0
- package/src/execution-graph.test.ts +740 -0
- package/src/execution-graph.ts +509 -0
- package/src/extraction/extraction-ner-function/index.ts +0 -1
- package/src/extraction/extraction-ner-function/named-entity-recognition.test.ts +12 -6
- package/src/extraction/extraction-ner-function/named-entity-recognition.ts +3 -1
- package/src/extraction/extraction.ts +44 -15
- package/src/extraction/index.ts +0 -1
- package/src/extraction/quotes.test.ts +3 -3
- package/src/extraction/quotes.ts +6 -7
- package/src/functions/index.ts +5 -0
- package/src/functions/services.ts +214 -0
- package/src/functions.conversations.json +1 -0
- package/src/functions.test.ts +88 -0
- package/src/index.ts +7 -5
- package/src/service/AgentService.conversations.json +1 -0
- package/src/service/AgentService.test.ts +169 -0
- package/src/service/AgentService.ts +184 -0
- package/src/service/agent-process.ts +364 -0
- package/src/service/index.ts +6 -0
- package/src/session/artifact-diff.ts +41 -0
- package/src/session/error-handling.ts +41 -0
- package/src/session/format.test.ts +132 -0
- package/src/session/format.ts +149 -0
- package/src/session/index.ts +5 -1
- package/src/session/observer.ts +62 -0
- package/src/session/request.conversations.json +1 -0
- package/src/session/request.test.ts +195 -0
- package/src/session/request.ts +344 -0
- package/src/session/session-ollama.test.ts +81 -0
- package/src/session/toolkit.ts +47 -0
- package/src/session/version-pin.ts +12 -12
- package/src/templates/index.ts +5 -0
- package/src/templates/instructions/format.tpl +57 -0
- package/src/templates/instructions/system.tpl +85 -0
- package/src/templates/system.ts +42 -0
- package/src/testing/index.ts +2 -8
- package/src/testing/layer.ts +236 -0
- package/src/tracing.ts +43 -0
- package/src/types/artifact.ts +89 -0
- package/src/types/index.ts +5 -0
- package/src/typings.d.ts +5 -0
- package/dist/lib/browser/index.mjs +0 -2019
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -2021
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/types/bin/chat.d.ts +0 -2
- package/dist/types/bin/chat.d.ts.map +0 -1
- package/dist/types/src/blueprint/blueprint.d.ts +0 -41
- package/dist/types/src/blueprint/blueprint.d.ts.map +0 -1
- package/dist/types/src/blueprint/index.d.ts +0 -3
- package/dist/types/src/blueprint/index.d.ts.map +0 -1
- package/dist/types/src/blueprint/registry.d.ts +0 -7
- package/dist/types/src/blueprint/registry.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 -38
- 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/conversation/conversation.d.ts +0 -44
- package/dist/types/src/conversation/conversation.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +0 -4
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts +0 -2
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-llm-function/index.d.ts +0 -2
- package/dist/types/src/extraction/extraction-llm-function/index.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +0 -208
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts +0 -2
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts.map +0 -1
- package/dist/types/src/research/exa.d.ts +0 -7
- package/dist/types/src/research/exa.d.ts.map +0 -1
- package/dist/types/src/research/graph.d.ts +0 -41
- 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/session.d.ts +0 -283
- package/dist/types/src/session/session.d.ts.map +0 -1
- package/dist/types/src/session/session.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/summarization/summarization.test.d.ts +0 -2
- package/dist/types/src/summarization/summarization.test.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/types.d.ts +0 -3
- package/dist/types/src/types.d.ts.map +0 -1
- package/src/blueprint/blueprint.ts +0 -75
- package/src/blueprint/index.ts +0 -6
- package/src/blueprint/registry.ts +0 -15
- package/src/context/binder.ts +0 -101
- package/src/context/binding.ts +0 -31
- package/src/context/index.ts +0 -6
- package/src/conversation/conversation.ts +0 -103
- package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +0 -95
- package/src/extraction/extraction-llm-function/extraction-llm-function.ts +0 -66
- package/src/extraction/extraction-llm-function/index.ts +0 -5
- package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +0 -115
- package/src/extraction/extraction-ner-function/extraction-ner-function.ts +0 -54
- package/src/research/exa.ts +0 -100
- package/src/research/graph.test.ts +0 -15
- package/src/research/graph.ts +0 -337
- package/src/research/index.ts +0 -7
- package/src/research/instructions-research.tpl +0 -97
- package/src/research/research.test.ts +0 -190
- package/src/research/research.ts +0 -90
- package/src/session/session.test.ts +0 -275
- package/src/session/session.ts +0 -582
- package/src/session/version-pin.test.ts +0 -26
- package/src/summarization/summarization.test.ts +0 -221
- 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/typedefs.d.ts +0 -8
- package/src/types.ts +0 -8
- /package/dist/types/src/{session → conversation}/session.test.d.ts +0 -0
- /package/src/extraction/extraction-llm-function/{instructions.tpl → transcription-instructions.tpl} +0 -0
|
@@ -0,0 +1,1793 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-J5LGTIGS.mjs";
|
|
4
|
+
|
|
5
|
+
// src/conversation/context.ts
|
|
6
|
+
import { Atom, Registry } from "@effect-atom/atom-react";
|
|
7
|
+
import * as EArray from "effect/Array";
|
|
8
|
+
import * as Context from "effect/Context";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import * as Function from "effect/Function";
|
|
11
|
+
import * as Runtime from "effect/Runtime";
|
|
12
|
+
import * as Schema from "effect/Schema";
|
|
13
|
+
import { Blueprint } from "@dxos/blueprints";
|
|
14
|
+
import { Resource } from "@dxos/context";
|
|
15
|
+
import { DXN, Feed, Obj, Query, Ref, Type } from "@dxos/echo";
|
|
16
|
+
import { assertArgument } from "@dxos/invariant";
|
|
17
|
+
import { log } from "@dxos/log";
|
|
18
|
+
import { ComplexSet, isNonNullable } from "@dxos/util";
|
|
19
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/core/assistant/src/conversation/context.ts";
|
|
20
|
+
var ContextBinding = Schema.Struct({
|
|
21
|
+
blueprints: Schema.Struct({
|
|
22
|
+
added: Schema.Array(Ref.Ref(Blueprint.Blueprint)),
|
|
23
|
+
removed: Schema.Array(Ref.Ref(Blueprint.Blueprint))
|
|
24
|
+
}),
|
|
25
|
+
objects: Schema.Struct({
|
|
26
|
+
added: Schema.Array(Ref.Ref(Obj.Unknown)),
|
|
27
|
+
removed: Schema.Array(Ref.Ref(Obj.Unknown))
|
|
28
|
+
})
|
|
29
|
+
}).pipe(Type.object({
|
|
30
|
+
typename: "org.dxos.type.contextBinding",
|
|
31
|
+
version: "0.1.0"
|
|
32
|
+
}));
|
|
33
|
+
var Bindings = class {
|
|
34
|
+
blueprints = new ComplexSet((ref) => ref.dxn.toString());
|
|
35
|
+
// TODO(burdon): Some DXNs have the Space prefix so only compare the object ID.
|
|
36
|
+
objects = new ComplexSet((ref) => ref.dxn.asEchoDXN()?.echoId);
|
|
37
|
+
toJSON() {
|
|
38
|
+
return {
|
|
39
|
+
blueprints: EArray.fromIterable(this.blueprints).map((ref) => ref.dxn.toString()),
|
|
40
|
+
objects: EArray.fromIterable(this.objects).map((ref) => ref.dxn.toString())
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var AiContextBinder = class extends Resource {
|
|
45
|
+
_blueprints = Atom.make([]).pipe(Atom.keepAlive);
|
|
46
|
+
_objects = Atom.make([]).pipe(Atom.keepAlive);
|
|
47
|
+
_registry;
|
|
48
|
+
_feed;
|
|
49
|
+
_runtime;
|
|
50
|
+
#bindingsQuery;
|
|
51
|
+
constructor(options) {
|
|
52
|
+
super();
|
|
53
|
+
assertArgument(options.feed, "options.feed", "Feed is required");
|
|
54
|
+
assertArgument(options.runtime, "options.runtime", "Feed runtime is required");
|
|
55
|
+
this._feed = options.feed;
|
|
56
|
+
this._runtime = options.runtime;
|
|
57
|
+
this._registry = options.registry ?? Registry.make();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Returns the blueprints atom for subscription.
|
|
61
|
+
*/
|
|
62
|
+
get blueprints() {
|
|
63
|
+
return this._blueprints;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns the objects atom for subscription.
|
|
67
|
+
*/
|
|
68
|
+
get objects() {
|
|
69
|
+
return this._objects;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Gets the current blueprints value.
|
|
73
|
+
*/
|
|
74
|
+
getBlueprints() {
|
|
75
|
+
return this._registry.get(this._blueprints);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Gets the current objects value.
|
|
79
|
+
*/
|
|
80
|
+
getObjects() {
|
|
81
|
+
return this._registry.get(this._objects);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Subscribe to changes in blueprints.
|
|
85
|
+
*/
|
|
86
|
+
subscribeBlueprints(cb) {
|
|
87
|
+
return this._registry.subscribe(this._blueprints, () => cb(this._registry.get(this._blueprints)));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Subscribe to changes in objects.
|
|
91
|
+
*/
|
|
92
|
+
subscribeObjects(cb) {
|
|
93
|
+
return this._registry.subscribe(this._objects, () => cb(this._registry.get(this._objects)));
|
|
94
|
+
}
|
|
95
|
+
async _open() {
|
|
96
|
+
this.#bindingsQuery = await Runtime.runPromise(this._runtime)(Feed.query(this._feed, Query.type(ContextBinding)));
|
|
97
|
+
const initialResults = await this.#bindingsQuery.run();
|
|
98
|
+
await this._updateBindings(initialResults);
|
|
99
|
+
this._ctx.onDispose(this.#bindingsQuery.subscribe(async () => {
|
|
100
|
+
await this._updateBindings(this.#bindingsQuery.results);
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Re-reads bindings from the feed to pick up changes made by other processes.
|
|
105
|
+
*/
|
|
106
|
+
async sync() {
|
|
107
|
+
if (this.#bindingsQuery) {
|
|
108
|
+
const results = await this.#bindingsQuery.run();
|
|
109
|
+
log("sync", {
|
|
110
|
+
bindingItems: results.length
|
|
111
|
+
}, {
|
|
112
|
+
F: __dxlog_file,
|
|
113
|
+
L: 152,
|
|
114
|
+
S: this,
|
|
115
|
+
C: (f, a) => f(...a)
|
|
116
|
+
});
|
|
117
|
+
await this._updateBindings(results);
|
|
118
|
+
log("sync complete", {
|
|
119
|
+
blueprints: this._registry.get(this._blueprints).length,
|
|
120
|
+
blueprintKeys: this._registry.get(this._blueprints).map((bp) => bp.key)
|
|
121
|
+
}, {
|
|
122
|
+
F: __dxlog_file,
|
|
123
|
+
L: 154,
|
|
124
|
+
S: this,
|
|
125
|
+
C: (f, a) => f(...a)
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async _updateBindings(items) {
|
|
130
|
+
if (items.length === 0) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const bindings = this._reduce(items);
|
|
134
|
+
log("_updateBindings", {
|
|
135
|
+
items: items.length,
|
|
136
|
+
blueprintRefs: [
|
|
137
|
+
...bindings.blueprints
|
|
138
|
+
].map((ref) => ({
|
|
139
|
+
dxn: ref.dxn.toString(),
|
|
140
|
+
available: ref.isAvailable
|
|
141
|
+
}))
|
|
142
|
+
}, {
|
|
143
|
+
F: __dxlog_file,
|
|
144
|
+
L: 169,
|
|
145
|
+
S: this,
|
|
146
|
+
C: (f, a) => f(...a)
|
|
147
|
+
});
|
|
148
|
+
const currentBlueprints = this._registry.get(this._blueprints);
|
|
149
|
+
const currentObjects = this._registry.get(this._objects);
|
|
150
|
+
const resolvedBlueprints = await this._resolve(bindings.blueprints, currentBlueprints);
|
|
151
|
+
const resolvedObjects = await this._resolve(bindings.objects, currentObjects);
|
|
152
|
+
log("_updateBindings resolved", {
|
|
153
|
+
resolvedBlueprints: resolvedBlueprints.length,
|
|
154
|
+
resolvedBlueprintKeys: resolvedBlueprints.map((bp) => bp.key)
|
|
155
|
+
}, {
|
|
156
|
+
F: __dxlog_file,
|
|
157
|
+
L: 180,
|
|
158
|
+
S: this,
|
|
159
|
+
C: (f, a) => f(...a)
|
|
160
|
+
});
|
|
161
|
+
const reducedBlueprintDxns = new ComplexSet(DXN.hash, [
|
|
162
|
+
...bindings.blueprints
|
|
163
|
+
].map((ref) => ref.dxn));
|
|
164
|
+
const reducedObjectDxns = new ComplexSet(DXN.hash, [
|
|
165
|
+
...bindings.objects
|
|
166
|
+
].map((ref) => ref.dxn));
|
|
167
|
+
const filteredBlueprints = currentBlueprints.filter((obj) => reducedBlueprintDxns.has(Obj.getDXN(obj)));
|
|
168
|
+
const filteredObjects = currentObjects.filter((obj) => reducedObjectDxns.has(Obj.getDXN(obj)));
|
|
169
|
+
const mergedBlueprints = this._mergeInto(filteredBlueprints, resolvedBlueprints);
|
|
170
|
+
const mergedObjects = this._mergeInto(filteredObjects, resolvedObjects);
|
|
171
|
+
this._registry.set(this._blueprints, mergedBlueprints);
|
|
172
|
+
this._registry.set(this._objects, mergedObjects);
|
|
173
|
+
log("updated bindings", {
|
|
174
|
+
blueprints: mergedBlueprints.length,
|
|
175
|
+
objects: mergedObjects.length
|
|
176
|
+
}, {
|
|
177
|
+
F: __dxlog_file,
|
|
178
|
+
L: 203,
|
|
179
|
+
S: this,
|
|
180
|
+
C: (f, a) => f(...a)
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
async _close() {
|
|
184
|
+
this._registry.set(this._blueprints, []);
|
|
185
|
+
this._registry.set(this._objects, []);
|
|
186
|
+
}
|
|
187
|
+
async bind({ blueprints, objects }) {
|
|
188
|
+
const currentBlueprints = this._registry.get(this._blueprints);
|
|
189
|
+
const currentObjects = this._registry.get(this._objects);
|
|
190
|
+
const { added: addedBlueprints, next: nextBlueprints } = this._processBindings(blueprints, currentBlueprints);
|
|
191
|
+
const { added: addedObjects, next: nextObjects } = this._processBindings(objects, currentObjects);
|
|
192
|
+
if (!addedBlueprints.length && !addedObjects.length) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this._registry.set(this._blueprints, nextBlueprints);
|
|
196
|
+
this._registry.set(this._objects, nextObjects);
|
|
197
|
+
log("bind", {
|
|
198
|
+
blueprints: addedBlueprints.length,
|
|
199
|
+
objects: addedObjects.length
|
|
200
|
+
}, {
|
|
201
|
+
F: __dxlog_file,
|
|
202
|
+
L: 229,
|
|
203
|
+
S: this,
|
|
204
|
+
C: (f, a) => f(...a)
|
|
205
|
+
});
|
|
206
|
+
await Runtime.runPromise(this._runtime)(Feed.append(this._feed, [
|
|
207
|
+
Obj.make(ContextBinding, {
|
|
208
|
+
blueprints: {
|
|
209
|
+
added: addedBlueprints,
|
|
210
|
+
removed: []
|
|
211
|
+
},
|
|
212
|
+
objects: {
|
|
213
|
+
added: addedObjects,
|
|
214
|
+
removed: []
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
]));
|
|
218
|
+
}
|
|
219
|
+
async unbind({ blueprints, objects }) {
|
|
220
|
+
if (!blueprints?.length && !objects?.length) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const removedBlueprintDxns = (blueprints ?? []).map((ref) => ref.dxn);
|
|
224
|
+
const removedObjectDxns = (objects ?? []).map((ref) => ref.dxn);
|
|
225
|
+
if (removedBlueprintDxns.length > 0) {
|
|
226
|
+
const current = this._registry.get(this._blueprints);
|
|
227
|
+
this._registry.set(this._blueprints, current.filter((obj) => !removedBlueprintDxns.some((dxn) => DXN.equalsEchoId(Obj.getDXN(obj), dxn))));
|
|
228
|
+
}
|
|
229
|
+
if (removedObjectDxns.length > 0) {
|
|
230
|
+
const current = this._registry.get(this._objects);
|
|
231
|
+
this._registry.set(this._objects, current.filter((obj) => !removedObjectDxns.some((dxn) => DXN.equalsEchoId(Obj.getDXN(obj), dxn))));
|
|
232
|
+
}
|
|
233
|
+
log("unbind", {
|
|
234
|
+
blueprints: blueprints?.length,
|
|
235
|
+
objects: objects?.length
|
|
236
|
+
}, {
|
|
237
|
+
F: __dxlog_file,
|
|
238
|
+
L: 269,
|
|
239
|
+
S: this,
|
|
240
|
+
C: (f, a) => f(...a)
|
|
241
|
+
});
|
|
242
|
+
await Runtime.runPromise(this._runtime)(Feed.append(this._feed, [
|
|
243
|
+
Obj.make(ContextBinding, {
|
|
244
|
+
blueprints: {
|
|
245
|
+
added: [],
|
|
246
|
+
removed: blueprints ?? []
|
|
247
|
+
},
|
|
248
|
+
objects: {
|
|
249
|
+
added: [],
|
|
250
|
+
removed: objects ?? []
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
]));
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Process bindings to filter duplicates and determine next state.
|
|
257
|
+
*/
|
|
258
|
+
_processBindings(refs, current) {
|
|
259
|
+
const next = [
|
|
260
|
+
...current
|
|
261
|
+
];
|
|
262
|
+
const added = [];
|
|
263
|
+
if (!refs?.length) {
|
|
264
|
+
return {
|
|
265
|
+
added,
|
|
266
|
+
next
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
const seen = new Set(current.map((obj) => Obj.getDXN(obj).toString()));
|
|
270
|
+
for (const ref of refs) {
|
|
271
|
+
const dxn = ref.dxn.toString();
|
|
272
|
+
if (!seen.has(dxn)) {
|
|
273
|
+
seen.add(dxn);
|
|
274
|
+
added.push(ref);
|
|
275
|
+
if (ref.isAvailable) {
|
|
276
|
+
const target = ref.target;
|
|
277
|
+
if (target) {
|
|
278
|
+
next.push(target);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
added,
|
|
285
|
+
next
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Reduce results into sets of blueprints and objects.
|
|
290
|
+
*/
|
|
291
|
+
_reduce(items) {
|
|
292
|
+
return Function.pipe(items, EArray.reduce(new Bindings(), (context, { blueprints, objects }) => {
|
|
293
|
+
blueprints.added.forEach((ref) => context.blueprints.add(ref));
|
|
294
|
+
blueprints.removed.forEach((ref) => context.blueprints.delete(ref));
|
|
295
|
+
objects.added.forEach((ref) => context.objects.add(ref));
|
|
296
|
+
objects.removed.forEach((ref) => {
|
|
297
|
+
for (const obj of context.objects) {
|
|
298
|
+
if (DXN.equalsEchoId(obj.dxn, ref.dxn)) {
|
|
299
|
+
context.objects.delete(obj);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
return context;
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Merge resolved items into the current set, adding only items not already present (by DXN).
|
|
308
|
+
*/
|
|
309
|
+
_mergeInto(current, resolved) {
|
|
310
|
+
const seen = new Set(current.map((obj) => Obj.getDXN(obj).toString()));
|
|
311
|
+
const merged = [
|
|
312
|
+
...current
|
|
313
|
+
];
|
|
314
|
+
for (const obj of resolved) {
|
|
315
|
+
const dxn = Obj.getDXN(obj).toString();
|
|
316
|
+
if (!seen.has(dxn)) {
|
|
317
|
+
seen.add(dxn);
|
|
318
|
+
merged.push(obj);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return merged;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Resolve references to objects, loading them first if needed and falling back to existing objects.
|
|
325
|
+
*/
|
|
326
|
+
async _resolve(refs, current) {
|
|
327
|
+
const refArray = [
|
|
328
|
+
...refs
|
|
329
|
+
];
|
|
330
|
+
await Promise.all(refArray.map((ref) => ref.tryLoad()));
|
|
331
|
+
return refArray.map((ref) => {
|
|
332
|
+
let target;
|
|
333
|
+
if (ref.isAvailable) {
|
|
334
|
+
target = ref.target;
|
|
335
|
+
}
|
|
336
|
+
return target ?? current.find((obj) => Obj.getDXN(obj).toString() === ref.dxn.toString());
|
|
337
|
+
}).filter(isNonNullable);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
var AiContextService = class _AiContextService extends Context.Tag("@dxos/assistant/AiContextService")() {
|
|
341
|
+
static bindContext = ({ blueprints, objects }) => Effect.gen(function* () {
|
|
342
|
+
const { binder } = yield* _AiContextService;
|
|
343
|
+
yield* Effect.promise(() => binder.bind({
|
|
344
|
+
blueprints,
|
|
345
|
+
objects
|
|
346
|
+
}));
|
|
347
|
+
});
|
|
348
|
+
static findObjects = (type) => {
|
|
349
|
+
return Effect.gen(function* () {
|
|
350
|
+
const { binder } = yield* _AiContextService;
|
|
351
|
+
return binder.getObjects().filter(Obj.instanceOf(type));
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
// src/types/artifact.ts
|
|
357
|
+
import * as Schema2 from "effect/Schema";
|
|
358
|
+
import { Obj as Obj2, Ref as Ref2 } from "@dxos/echo";
|
|
359
|
+
import { Database } from "@dxos/echo";
|
|
360
|
+
import { EncodedReference } from "@dxos/echo-protocol";
|
|
361
|
+
import { DXN as DXN2, LOCAL_SPACE_TAG } from "@dxos/keys";
|
|
362
|
+
import { trim } from "@dxos/util";
|
|
363
|
+
var createArtifactElement = (id) => `<artifact id=${id} />`;
|
|
364
|
+
var ArtifactId = class extends Schema2.String.annotations({
|
|
365
|
+
// TODO(dmaretskyi): This section gets overriden.
|
|
366
|
+
description: trim`
|
|
367
|
+
The ID of the referenced object. Formats accepted:
|
|
368
|
+
- DXN (dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4). DXNs can be prepended with an @ symbol for compatibility with in-text references.
|
|
369
|
+
- space ID, object ID tuple (spaceID:objectID)
|
|
370
|
+
- Only object ID that is assumed to be in the current space (01KG7R1ZXWFMWQ4DA1Q6TN1DG4)
|
|
371
|
+
`,
|
|
372
|
+
examples: [
|
|
373
|
+
"dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4",
|
|
374
|
+
"@dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4",
|
|
375
|
+
"BM3FSHFOMJCHCG5QW7JTVKGYABD2GAA7G:01KG7R1ZXWFMWQ4DA1Q6TN1DG4",
|
|
376
|
+
"01KG7R1ZXWFMWQ4DA1Q6TN1DG4"
|
|
377
|
+
]
|
|
378
|
+
}) {
|
|
379
|
+
static toDXN(reference, owningSpaceId) {
|
|
380
|
+
if (reference.startsWith("@dxn:")) {
|
|
381
|
+
return DXN2.parse(reference.slice(1));
|
|
382
|
+
} else if (reference.startsWith("dxn:")) {
|
|
383
|
+
return DXN2.parse(reference);
|
|
384
|
+
} else if (/^[A-Z0-9]+:[A-Z0-9]+$/.test(reference)) {
|
|
385
|
+
const [spaceId, objectId] = reference.split(":");
|
|
386
|
+
return new DXN2(DXN2.kind.ECHO, [
|
|
387
|
+
spaceId === owningSpaceId ? LOCAL_SPACE_TAG : spaceId,
|
|
388
|
+
objectId
|
|
389
|
+
]);
|
|
390
|
+
} else if (/^[A-Z0-9]+$/.test(reference)) {
|
|
391
|
+
return DXN2.fromLocalObjectId(reference);
|
|
392
|
+
} else {
|
|
393
|
+
throw new Error(`Unable to parse object reference: ${reference}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Resolves an artifact ID to an object.
|
|
398
|
+
*/
|
|
399
|
+
static resolve(schema, ref) {
|
|
400
|
+
const dxn = ArtifactId.toDXN(ref);
|
|
401
|
+
return Database.resolve(dxn, schema);
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
var RefFromLLM = Schema2.transform(ArtifactId, Ref2.Ref(Obj2.Unknown), {
|
|
405
|
+
decode: (fromA, fromI) => EncodedReference.fromDXN(ArtifactId.toDXN(fromA)),
|
|
406
|
+
encode: (toI, toA) => EncodedReference.toDXN(toI).toString(),
|
|
407
|
+
strict: false
|
|
408
|
+
}).annotations({
|
|
409
|
+
description: ArtifactId.ast.annotations.description
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// src/functions/services.ts
|
|
413
|
+
import * as Tool from "@effect/ai/Tool";
|
|
414
|
+
import * as Context2 from "effect/Context";
|
|
415
|
+
import * as Effect2 from "effect/Effect";
|
|
416
|
+
import * as Layer from "effect/Layer";
|
|
417
|
+
import * as Option from "effect/Option";
|
|
418
|
+
import * as Record from "effect/Record";
|
|
419
|
+
import * as Schema3 from "effect/Schema";
|
|
420
|
+
import * as SchemaAST from "effect/SchemaAST";
|
|
421
|
+
import { AiToolNotFoundError, ToolExecutionService, ToolResolverService } from "@dxos/ai";
|
|
422
|
+
import { OpaqueToolkit } from "@dxos/ai";
|
|
423
|
+
import { todo } from "@dxos/debug";
|
|
424
|
+
import { Ref as Ref3 } from "@dxos/echo";
|
|
425
|
+
import { invariant } from "@dxos/invariant";
|
|
426
|
+
import { Operation, OperationRegistry } from "@dxos/operation";
|
|
427
|
+
var __dxlog_file2 = "/__w/dxos/dxos/packages/core/assistant/src/functions/services.ts";
|
|
428
|
+
var makeToolResolverFromOperations = ({ toolkit: extraToolkit = OpaqueToolkit.empty } = {}) => {
|
|
429
|
+
return Layer.effect(ToolResolverService, Effect2.gen(function* () {
|
|
430
|
+
const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;
|
|
431
|
+
const operationRegistry = yield* OperationRegistry.Service;
|
|
432
|
+
return {
|
|
433
|
+
resolve: (id) => Effect2.gen(function* () {
|
|
434
|
+
const toolkit = OpaqueToolkit.merge(extraToolkit, toolkitProvider.getToolkit());
|
|
435
|
+
const tool = toolkit.toolkit.tools[id];
|
|
436
|
+
if (tool) {
|
|
437
|
+
return tool;
|
|
438
|
+
}
|
|
439
|
+
return yield* operationRegistry.resolve(id).pipe(Effect2.flatMap(Option.match({
|
|
440
|
+
onSome: (_) => Effect2.succeed(projectFunctionToTool(_)),
|
|
441
|
+
onNone: () => Effect2.fail(new AiToolNotFoundError(id))
|
|
442
|
+
})));
|
|
443
|
+
})
|
|
444
|
+
};
|
|
445
|
+
}));
|
|
446
|
+
};
|
|
447
|
+
var makeToolExecutionService = (opts) => Layer.effect(ToolExecutionService, Effect2.gen(function* () {
|
|
448
|
+
const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;
|
|
449
|
+
const toolkit = toolkitProvider.getToolkit();
|
|
450
|
+
const toolkitHandler = yield* toolkit.toolkit.pipe(Effect2.provide(toolkit.layer));
|
|
451
|
+
invariant(isHandlerLike(toolkitHandler), void 0, {
|
|
452
|
+
F: __dxlog_file2,
|
|
453
|
+
L: 70,
|
|
454
|
+
S: this,
|
|
455
|
+
A: [
|
|
456
|
+
"isHandlerLike(toolkitHandler)",
|
|
457
|
+
""
|
|
458
|
+
]
|
|
459
|
+
});
|
|
460
|
+
return {
|
|
461
|
+
handlersFor: (toolkit2) => {
|
|
462
|
+
const makeHandler = (tool) => {
|
|
463
|
+
return Effect2.fn(`toolFunctionHandler ${tool.name}`)(function* (input) {
|
|
464
|
+
if (toolkitHandler.tools[tool.name]) {
|
|
465
|
+
if (Tool.isProviderDefined(tool)) {
|
|
466
|
+
throw new Error("Attempted to call a provider-defined tool");
|
|
467
|
+
}
|
|
468
|
+
const { result } = yield* toolkitHandler.handle(tool.name, input);
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
471
|
+
return yield* opts.invoke(tool, input).pipe(Effect2.catchAllDefect((defect) => Effect2.fail(defect)));
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
return toolkit2.of(Record.map(toolkit2.tools, (tool, _name) => Tool.isUserDefined(tool) ? makeHandler(tool) : null));
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}));
|
|
478
|
+
var makeToolExecutionServiceFromOperationInvoker = () => {
|
|
479
|
+
return Layer.unwrapEffect(Effect2.gen(function* () {
|
|
480
|
+
const operationInvoker = yield* Operation.Service;
|
|
481
|
+
return makeToolExecutionService({
|
|
482
|
+
invoke: (tool, input) => Effect2.gen(function* () {
|
|
483
|
+
const operationDef = getOperationFromTool(tool).pipe(Option.getOrThrow);
|
|
484
|
+
return yield* operationInvoker.invoke(operationDef, input).pipe(Effect2.orDie);
|
|
485
|
+
})
|
|
486
|
+
});
|
|
487
|
+
}));
|
|
488
|
+
};
|
|
489
|
+
var ToolExecutionServices = Layer.mergeAll(makeToolResolverFromOperations(), makeToolExecutionServiceFromOperationInvoker());
|
|
490
|
+
var FunctionToolAnnotation = class extends Context2.Tag("@dxos/assistant/FunctionToolAnnotation")() {
|
|
491
|
+
};
|
|
492
|
+
var getOperationFromTool = (tool) => {
|
|
493
|
+
return Context2.getOption(FunctionToolAnnotation)(tool.annotations).pipe(Option.map(({ definition }) => definition));
|
|
494
|
+
};
|
|
495
|
+
var toolCache = /* @__PURE__ */ new WeakMap();
|
|
496
|
+
var projectFunctionToTool = (fn3) => {
|
|
497
|
+
if (toolCache.has(fn3)) {
|
|
498
|
+
return toolCache.get(fn3);
|
|
499
|
+
}
|
|
500
|
+
const tool = Tool.make(makeToolName(fn3.meta.name ?? fn3.meta.key), {
|
|
501
|
+
description: fn3.meta.description,
|
|
502
|
+
parameters: createStructFieldsFromSchema(fn3.input),
|
|
503
|
+
failure: Schema3.Any
|
|
504
|
+
}).annotate(FunctionToolAnnotation, {
|
|
505
|
+
definition: fn3
|
|
506
|
+
});
|
|
507
|
+
toolCache.set(fn3, tool);
|
|
508
|
+
return tool;
|
|
509
|
+
};
|
|
510
|
+
var makeToolName = (name) => {
|
|
511
|
+
const toolName = name.toLowerCase().replace(/[^a-zA-Z0-9]/g, "-");
|
|
512
|
+
invariant(toolName.match(/^[a-z_][a-z0-9-_]*$/), void 0, {
|
|
513
|
+
F: __dxlog_file2,
|
|
514
|
+
L: 157,
|
|
515
|
+
S: void 0,
|
|
516
|
+
A: [
|
|
517
|
+
"toolName.match(/^[a-z_][a-z0-9-_]*$/)",
|
|
518
|
+
""
|
|
519
|
+
]
|
|
520
|
+
});
|
|
521
|
+
return toolName;
|
|
522
|
+
};
|
|
523
|
+
var createStructFieldsFromSchema = (schema) => {
|
|
524
|
+
switch (schema.ast._tag) {
|
|
525
|
+
case "TypeLiteral":
|
|
526
|
+
return Object.fromEntries(schema.ast.propertySignatures.map((prop) => [
|
|
527
|
+
prop.name,
|
|
528
|
+
Schema3.make(mapSchemaTypeForLLM(prop.type))
|
|
529
|
+
]));
|
|
530
|
+
case "VoidKeyword":
|
|
531
|
+
return {};
|
|
532
|
+
default:
|
|
533
|
+
return todo(`Unsupported schema AST: ${schema.ast._tag}`);
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
var mapSchemaTypeForLLM = (ast) => {
|
|
537
|
+
if (Ref3.isRefType(ast)) {
|
|
538
|
+
const description = ast.annotations.description ? ast.annotations.description + "\n" + RefFromLLM.ast.annotations.description : RefFromLLM.ast.annotations.description;
|
|
539
|
+
return RefFromLLM.annotations({
|
|
540
|
+
description
|
|
541
|
+
}).ast;
|
|
542
|
+
} else if (SchemaAST.isTupleType(ast)) {
|
|
543
|
+
return new SchemaAST.TupleType(ast.elements.map((t) => new SchemaAST.OptionalType(mapSchemaTypeForLLM(t.type), t.isOptional, t.annotations)), ast.rest.map((t) => new SchemaAST.Type(mapSchemaTypeForLLM(t.type), t.annotations)), ast.isReadonly, ast.annotations);
|
|
544
|
+
} else if (SchemaAST.isTypeLiteral(ast)) {
|
|
545
|
+
return new SchemaAST.TypeLiteral(ast.propertySignatures.map((p) => new SchemaAST.PropertySignature(p.name, mapSchemaTypeForLLM(p.type), p.isOptional, p.isReadonly, p.annotations)), ast.indexSignatures, ast.annotations);
|
|
546
|
+
}
|
|
547
|
+
return ast;
|
|
548
|
+
};
|
|
549
|
+
var isHandlerLike = (value2) => {
|
|
550
|
+
return typeof value2.tools === "object" && typeof value2.handle === "function";
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
// src/session/artifact-diff.ts
|
|
554
|
+
import * as Context3 from "effect/Context";
|
|
555
|
+
var ArtifactDiffResolver = class extends Context3.Tag("@dxos/assistant/ArtifactDiffResolver")() {
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
// src/session/observer.ts
|
|
559
|
+
import * as Effect3 from "effect/Effect";
|
|
560
|
+
import * as Function2 from "effect/Function";
|
|
561
|
+
var GenerationObserver = Object.freeze({
|
|
562
|
+
make: ({ onBegin = Function2.constant(Effect3.void), onPart = Function2.constant(Effect3.void), onBlock = Function2.constant(Effect3.void), onEnd = Function2.constant(Effect3.void), onMessage = Function2.constant(Effect3.void) } = {}) => ({
|
|
563
|
+
onBegin,
|
|
564
|
+
onPart,
|
|
565
|
+
onBlock,
|
|
566
|
+
onEnd,
|
|
567
|
+
onMessage
|
|
568
|
+
}),
|
|
569
|
+
noop: () => GenerationObserver.make(),
|
|
570
|
+
/**
|
|
571
|
+
* Debug printer to be used in unit-tests and browser devtools.
|
|
572
|
+
*/
|
|
573
|
+
fromPrinter: (printer) => GenerationObserver.make({
|
|
574
|
+
onBlock: (block) => Effect3.sync(() => {
|
|
575
|
+
if (block.pending) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
printer.printContentBlock(block);
|
|
579
|
+
}),
|
|
580
|
+
onMessage: (message) => Effect3.sync(() => {
|
|
581
|
+
if (message.sender.role === "assistant") {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
printer.printMessage(message);
|
|
585
|
+
})
|
|
586
|
+
})
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
// src/tracing.ts
|
|
590
|
+
import * as Schema4 from "effect/Schema";
|
|
591
|
+
import { Obj as Obj3 } from "@dxos/echo";
|
|
592
|
+
import { Trace } from "@dxos/functions";
|
|
593
|
+
import { ContentBlock, Actor } from "@dxos/types";
|
|
594
|
+
var PartialBlock = Trace.EventType("assistant.partialBlock", {
|
|
595
|
+
schema: Schema4.Struct({
|
|
596
|
+
messageId: Obj3.ID,
|
|
597
|
+
role: Actor.Role,
|
|
598
|
+
block: ContentBlock.Any
|
|
599
|
+
}),
|
|
600
|
+
isEphemeral: true
|
|
601
|
+
});
|
|
602
|
+
var CompleteBlock = Trace.EventType("assistant.completeBlock", {
|
|
603
|
+
schema: Schema4.Struct({
|
|
604
|
+
messageId: Obj3.ID,
|
|
605
|
+
role: Actor.Role,
|
|
606
|
+
block: ContentBlock.Any
|
|
607
|
+
}),
|
|
608
|
+
isEphemeral: false
|
|
609
|
+
});
|
|
610
|
+
var AgentRequestBegin = Trace.EventType("assistant.agentRequestBegin", {
|
|
611
|
+
schema: Schema4.Struct({}),
|
|
612
|
+
isEphemeral: false
|
|
613
|
+
});
|
|
614
|
+
var AgentRequestEnd = Trace.EventType("assistant.agentRequestEnd", {
|
|
615
|
+
schema: Schema4.Struct({}),
|
|
616
|
+
isEphemeral: false
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// src/errors.ts
|
|
620
|
+
import { BaseError } from "@dxos/errors";
|
|
621
|
+
var AiModelError = class extends BaseError.extend("AiModelError", "AI model error") {
|
|
622
|
+
};
|
|
623
|
+
var AiAssistantError = class extends BaseError.extend("AiAssistantError", "AI assistant error") {
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// src/session/format.ts
|
|
627
|
+
import * as Effect4 from "effect/Effect";
|
|
628
|
+
import * as Function3 from "effect/Function";
|
|
629
|
+
import * as Option2 from "effect/Option";
|
|
630
|
+
import { Template } from "@dxos/blueprints";
|
|
631
|
+
import { Obj as Obj4 } from "@dxos/echo";
|
|
632
|
+
import { ObjectVersion } from "@dxos/echo-db";
|
|
633
|
+
import { log as log2 } from "@dxos/log";
|
|
634
|
+
import { Message } from "@dxos/types";
|
|
635
|
+
import { trim as trim2 } from "@dxos/util";
|
|
636
|
+
var __dxlog_file3 = "/__w/dxos/dxos/packages/core/assistant/src/session/format.ts";
|
|
637
|
+
var formatSystemPrompt = ({ system, blueprints = [], objects = [] }) => Effect4.gen(function* () {
|
|
638
|
+
const blueprintDefs = yield* Function3.pipe(blueprints, Effect4.forEach((blueprint) => Effect4.succeed(blueprint.instructions)), Effect4.flatMap(Effect4.forEach((template) => Effect4.gen(function* () {
|
|
639
|
+
return trim2`
|
|
640
|
+
<blueprint>
|
|
641
|
+
${yield* Template.processTemplate(template)}
|
|
642
|
+
</blueprint>
|
|
643
|
+
`;
|
|
644
|
+
}))), Effect4.map((blueprints2) => blueprints2.length > 0 ? [
|
|
645
|
+
"## Blueprints Definitions",
|
|
646
|
+
...blueprints2
|
|
647
|
+
].join("\n\n") : void 0));
|
|
648
|
+
const objectDefs = yield* Function3.pipe(objects, Effect4.forEach((object) => Effect4.succeed(trim2`
|
|
649
|
+
<object>
|
|
650
|
+
<dxn>${Obj4.getDXN(object)}</dxn>
|
|
651
|
+
<typename>${Obj4.getTypename(object)}</typename>
|
|
652
|
+
</object>
|
|
653
|
+
`)), Effect4.map((objects2) => objects2.length > 0 ? [
|
|
654
|
+
"## Context Objects",
|
|
655
|
+
...objects2
|
|
656
|
+
].join("\n\n") : void 0));
|
|
657
|
+
return yield* Function3.pipe(Effect4.succeed([
|
|
658
|
+
system,
|
|
659
|
+
blueprintDefs,
|
|
660
|
+
objectDefs
|
|
661
|
+
].filter((def) => def !== void 0)), Effect4.map((parts) => parts.join("\n\n")));
|
|
662
|
+
}).pipe(Effect4.withSpan("formatSystemPrompt"));
|
|
663
|
+
var formatUserPrompt = ({ prompt, history = [] }) => Effect4.gen(function* () {
|
|
664
|
+
const blocks = [];
|
|
665
|
+
const artifactDiffResolver = yield* Effect4.serviceOption(ArtifactDiffResolver);
|
|
666
|
+
if (Option2.isSome(artifactDiffResolver)) {
|
|
667
|
+
const versions = gatherObjectVersions(history);
|
|
668
|
+
const artifactDiff = yield* Effect4.tryPromise({
|
|
669
|
+
try: () => artifactDiffResolver.value.resolve([
|
|
670
|
+
...versions.entries()
|
|
671
|
+
].map(([id, version]) => ({
|
|
672
|
+
id,
|
|
673
|
+
lastVersion: version
|
|
674
|
+
}))),
|
|
675
|
+
catch: AiAssistantError.wrap({
|
|
676
|
+
message: "Artifact diff resolution error"
|
|
677
|
+
})
|
|
678
|
+
});
|
|
679
|
+
log2("version", {
|
|
680
|
+
artifactDiff,
|
|
681
|
+
versions
|
|
682
|
+
}, {
|
|
683
|
+
F: __dxlog_file3,
|
|
684
|
+
L: 100,
|
|
685
|
+
S: this,
|
|
686
|
+
C: (f, a) => f(...a)
|
|
687
|
+
});
|
|
688
|
+
for (const [id, { version }] of [
|
|
689
|
+
...artifactDiff.entries()
|
|
690
|
+
]) {
|
|
691
|
+
if (ObjectVersion.equals(version, versions.get(id))) {
|
|
692
|
+
artifactDiff.delete(id);
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
blocks.push({
|
|
696
|
+
_tag: "anchor",
|
|
697
|
+
objectId: id,
|
|
698
|
+
version
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
if (artifactDiff.size > 0) {
|
|
702
|
+
blocks.push(createArtifactUpdateBlock(artifactDiff));
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return Obj4.make(Message.Message, {
|
|
706
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
707
|
+
sender: {
|
|
708
|
+
role: "user"
|
|
709
|
+
},
|
|
710
|
+
blocks: [
|
|
711
|
+
...blocks,
|
|
712
|
+
{
|
|
713
|
+
_tag: "text",
|
|
714
|
+
text: prompt
|
|
715
|
+
}
|
|
716
|
+
]
|
|
717
|
+
});
|
|
718
|
+
}).pipe(Effect4.withSpan("formatUserPrompt"));
|
|
719
|
+
var gatherObjectVersions = (messages) => {
|
|
720
|
+
const artifactIds = /* @__PURE__ */ new Map();
|
|
721
|
+
for (const message of messages) {
|
|
722
|
+
for (const block of message.blocks) {
|
|
723
|
+
if (block._tag === "anchor") {
|
|
724
|
+
artifactIds.set(block.objectId, block.version);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return artifactIds;
|
|
729
|
+
};
|
|
730
|
+
var createArtifactUpdateBlock = (artifactDiff) => {
|
|
731
|
+
return {
|
|
732
|
+
_tag: "text",
|
|
733
|
+
// TODO(dmaretskyi): Does this need to be a special content-block?
|
|
734
|
+
disposition: "artifact-update",
|
|
735
|
+
text: trim2`
|
|
736
|
+
The following artifacts have been updated since the last message:
|
|
737
|
+
${[
|
|
738
|
+
...artifactDiff.entries()
|
|
739
|
+
].map(([id, { diff }]) => `<changed-artifact id="${id}">${diff ? `
|
|
740
|
+
${diff}` : ""}</changed-artifact>`).join("\n")}
|
|
741
|
+
`
|
|
742
|
+
};
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
// src/session/request.ts
|
|
746
|
+
import * as LanguageModel from "@effect/ai/LanguageModel";
|
|
747
|
+
import * as Array2 from "effect/Array";
|
|
748
|
+
import * as Chunk from "effect/Chunk";
|
|
749
|
+
import * as Effect5 from "effect/Effect";
|
|
750
|
+
import { pipe as pipe3 } from "effect/Function";
|
|
751
|
+
import * as Option3 from "effect/Option";
|
|
752
|
+
import * as Stream from "effect/Stream";
|
|
753
|
+
import { AiParser, AiPreprocessor, AiSummarizer, callTool, withoutToolCallParising } from "@dxos/ai";
|
|
754
|
+
import { Obj as Obj5 } from "@dxos/echo";
|
|
755
|
+
import { Trace as Trace2 } from "@dxos/functions";
|
|
756
|
+
import { log as log3 } from "@dxos/log";
|
|
757
|
+
import { ContentBlock as ContentBlock2, Message as Message2 } from "@dxos/types";
|
|
758
|
+
var __dxlog_file4 = "/__w/dxos/dxos/packages/core/assistant/src/session/request.ts";
|
|
759
|
+
var AiRequest = class {
|
|
760
|
+
_options;
|
|
761
|
+
/** Prevents concurrent execution of session. */
|
|
762
|
+
_semaphore = Effect5.runSync(Effect5.makeSemaphore(1));
|
|
763
|
+
_observer;
|
|
764
|
+
_onOutput;
|
|
765
|
+
/** Message history from queue. */
|
|
766
|
+
// TODO(burdon): Evolve into supporting a git-like graph of messages.
|
|
767
|
+
_history = [];
|
|
768
|
+
/** Pending messages for this session (incl. the current prompt). */
|
|
769
|
+
_pending = [];
|
|
770
|
+
_started = 0;
|
|
771
|
+
_ended = 0;
|
|
772
|
+
_toolCalls = 0;
|
|
773
|
+
constructor(_options = {}) {
|
|
774
|
+
this._options = _options;
|
|
775
|
+
this._observer = _options.observer ?? GenerationObserver.noop();
|
|
776
|
+
this._onOutput = _options.onOutput ?? (() => Effect5.void);
|
|
777
|
+
}
|
|
778
|
+
get duration() {
|
|
779
|
+
return this._ended - this._started;
|
|
780
|
+
}
|
|
781
|
+
get toolCalls() {
|
|
782
|
+
return this._toolCalls;
|
|
783
|
+
}
|
|
784
|
+
get pending() {
|
|
785
|
+
return this._pending;
|
|
786
|
+
}
|
|
787
|
+
_submitMessage = (message) => Effect5.gen(this, function* () {
|
|
788
|
+
this._pending.push(message);
|
|
789
|
+
yield* this._observer.onMessage(message);
|
|
790
|
+
for (const block of message.blocks) {
|
|
791
|
+
yield* Trace2.write(CompleteBlock, {
|
|
792
|
+
messageId: message.id,
|
|
793
|
+
role: message.sender.role,
|
|
794
|
+
block
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
yield* this._onOutput(message);
|
|
798
|
+
return message;
|
|
799
|
+
});
|
|
800
|
+
getToolCalls = () => pipe3([
|
|
801
|
+
...this._history,
|
|
802
|
+
...this._pending
|
|
803
|
+
], Array2.reverse, Array2.takeWhile((_) => _.sender.role === "assistant"), Array2.flatMap((_) => _.blocks.filter(ContentBlock2.is("toolCall")).map((block) => ({
|
|
804
|
+
block,
|
|
805
|
+
message: _
|
|
806
|
+
}))), Array2.filter((_) => !_.block.providerExecuted), Array2.reverse);
|
|
807
|
+
/**
|
|
808
|
+
* Initialize a session: set up history, perform summarization if needed, and submit the user prompt.
|
|
809
|
+
* Must be called before `runTurn()`.
|
|
810
|
+
*/
|
|
811
|
+
begin = ({ prompt, system, history = [], blueprints = [], objects = [] }) => Effect5.gen(this, function* () {
|
|
812
|
+
this._started = Date.now();
|
|
813
|
+
this._history = [
|
|
814
|
+
...history
|
|
815
|
+
];
|
|
816
|
+
this._pending = [];
|
|
817
|
+
const systemPrompt = yield* formatSystemPrompt({
|
|
818
|
+
system,
|
|
819
|
+
blueprints,
|
|
820
|
+
objects
|
|
821
|
+
}).pipe(Effect5.orDie);
|
|
822
|
+
if (this._options.summarizationThreshold !== void 0) {
|
|
823
|
+
const tokenCount = yield* AiPreprocessor.estimateTokens(yield* AiPreprocessor.preprocessPrompt([
|
|
824
|
+
...this._history
|
|
825
|
+
], {
|
|
826
|
+
system: systemPrompt
|
|
827
|
+
}));
|
|
828
|
+
if (tokenCount > this._options.summarizationThreshold) {
|
|
829
|
+
const summary = yield* AiSummarizer.summarize([
|
|
830
|
+
...this._history
|
|
831
|
+
]);
|
|
832
|
+
yield* this._submitMessage(summary);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
yield* this._submitMessage(yield* formatUserPrompt({
|
|
836
|
+
prompt,
|
|
837
|
+
history
|
|
838
|
+
}));
|
|
839
|
+
}).pipe(Effect5.withSpan("AiRequest.begin"));
|
|
840
|
+
/**
|
|
841
|
+
* Execute a single turn: one LLM generation followed by tool execution.
|
|
842
|
+
* The toolkit and system prompt can be updated between turns to reflect context changes (e.g. dynamically enabled blueprints).
|
|
843
|
+
*/
|
|
844
|
+
runAgentTurn = ({ system, toolkit: opaqueToolkit }) => Effect5.gen(this, function* () {
|
|
845
|
+
log3.info("request", {
|
|
846
|
+
system: {
|
|
847
|
+
snippet: createSnippet(system),
|
|
848
|
+
length: system.length
|
|
849
|
+
},
|
|
850
|
+
pending: this._pending.length,
|
|
851
|
+
history: this._history.length
|
|
852
|
+
}, {
|
|
853
|
+
F: __dxlog_file4,
|
|
854
|
+
L: 204,
|
|
855
|
+
S: this,
|
|
856
|
+
C: (f, a) => f(...a)
|
|
857
|
+
});
|
|
858
|
+
const prompt = yield* AiPreprocessor.preprocessPrompt([
|
|
859
|
+
...this._history,
|
|
860
|
+
...this._pending
|
|
861
|
+
], {
|
|
862
|
+
system,
|
|
863
|
+
cacheControl: "ephemeral"
|
|
864
|
+
});
|
|
865
|
+
const toolkit = opaqueToolkit ? yield* opaqueToolkit.handlers : void 0;
|
|
866
|
+
const observer = this._observer;
|
|
867
|
+
let currentMessageId = null;
|
|
868
|
+
const messages = yield* LanguageModel.streamText({
|
|
869
|
+
prompt,
|
|
870
|
+
toolkit,
|
|
871
|
+
disableToolCallResolution: true
|
|
872
|
+
}).pipe(withoutToolCallParising, AiParser.parseResponse({
|
|
873
|
+
emitPartial: true,
|
|
874
|
+
onBegin: () => observer.onBegin(),
|
|
875
|
+
onBlock: (block) => observer.onBlock(block),
|
|
876
|
+
onPart: (part) => observer.onPart(part),
|
|
877
|
+
onEnd: (summary) => observer.onEnd(summary)
|
|
878
|
+
}), Stream.mapEffect((block) => Effect5.gen(this, function* () {
|
|
879
|
+
if (block.pending) {
|
|
880
|
+
currentMessageId ??= Obj5.ID.random();
|
|
881
|
+
log3("emit ephemeral message", {
|
|
882
|
+
id: currentMessageId,
|
|
883
|
+
type: block._tag
|
|
884
|
+
}, {
|
|
885
|
+
F: __dxlog_file4,
|
|
886
|
+
L: 238,
|
|
887
|
+
S: this,
|
|
888
|
+
C: (f, a) => f(...a)
|
|
889
|
+
});
|
|
890
|
+
yield* Trace2.write(PartialBlock, {
|
|
891
|
+
messageId: currentMessageId,
|
|
892
|
+
role: "assistant",
|
|
893
|
+
block
|
|
894
|
+
});
|
|
895
|
+
return Option3.none();
|
|
896
|
+
} else {
|
|
897
|
+
currentMessageId ??= Obj5.ID.random();
|
|
898
|
+
const id = currentMessageId;
|
|
899
|
+
currentMessageId = null;
|
|
900
|
+
log3("emit complete message", {
|
|
901
|
+
id,
|
|
902
|
+
type: block._tag
|
|
903
|
+
}, {
|
|
904
|
+
F: __dxlog_file4,
|
|
905
|
+
L: 249,
|
|
906
|
+
S: this,
|
|
907
|
+
C: (f, a) => f(...a)
|
|
908
|
+
});
|
|
909
|
+
const message = Obj5.make(Message2.Message, {
|
|
910
|
+
id,
|
|
911
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
912
|
+
sender: {
|
|
913
|
+
role: "assistant"
|
|
914
|
+
},
|
|
915
|
+
blocks: [
|
|
916
|
+
block
|
|
917
|
+
]
|
|
918
|
+
});
|
|
919
|
+
return Option3.some(yield* this._submitMessage(message));
|
|
920
|
+
}
|
|
921
|
+
}), {
|
|
922
|
+
concurrency: 1,
|
|
923
|
+
unordered: false
|
|
924
|
+
}), Stream.filterMap((_) => _), Stream.runCollect, Effect5.map(Chunk.toArray));
|
|
925
|
+
log3.info("messages", {
|
|
926
|
+
messages
|
|
927
|
+
}, {
|
|
928
|
+
F: __dxlog_file4,
|
|
929
|
+
L: 265,
|
|
930
|
+
S: this,
|
|
931
|
+
C: (f, a) => f(...a)
|
|
932
|
+
});
|
|
933
|
+
const toolCalls = this.getToolCalls();
|
|
934
|
+
if (toolCalls.length === 0) {
|
|
935
|
+
this._ended = Date.now();
|
|
936
|
+
return {
|
|
937
|
+
messages,
|
|
938
|
+
done: true
|
|
939
|
+
};
|
|
940
|
+
} else if (!toolkit) {
|
|
941
|
+
throw new Error("No toolkit provided");
|
|
942
|
+
}
|
|
943
|
+
return {
|
|
944
|
+
messages,
|
|
945
|
+
done: false
|
|
946
|
+
};
|
|
947
|
+
}).pipe(Effect5.withSpan("AiRequest.runAgentTurn"));
|
|
948
|
+
runTools = ({ toolkit: opaqueToolkit }) => Effect5.gen(this, function* () {
|
|
949
|
+
const toolkit = opaqueToolkit ? yield* opaqueToolkit.handlers : void 0;
|
|
950
|
+
const toolCalls = this.getToolCalls();
|
|
951
|
+
const toolResults = yield* Effect5.forEach(toolCalls, ({ block, message }) => {
|
|
952
|
+
if (!toolkit) {
|
|
953
|
+
throw new Error("No toolkit provided");
|
|
954
|
+
}
|
|
955
|
+
return callTool(toolkit, block);
|
|
956
|
+
});
|
|
957
|
+
yield* this._submitMessage(Obj5.make(Message2.Message, {
|
|
958
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
959
|
+
sender: {
|
|
960
|
+
role: "tool"
|
|
961
|
+
},
|
|
962
|
+
blocks: toolResults
|
|
963
|
+
}));
|
|
964
|
+
this._toolCalls += toolResults.length;
|
|
965
|
+
}).pipe(Effect5.withSpan("AiRequest.runTools"));
|
|
966
|
+
/**
|
|
967
|
+
* Run a full conversation turn loop. Equivalent to calling `begin()` then `runTurn()` in a loop.
|
|
968
|
+
*/
|
|
969
|
+
run = ({ prompt, system: systemTemplate, history = [], objects = [], blueprints = [], toolkit }) => Effect5.gen(this, function* () {
|
|
970
|
+
yield* this.begin({
|
|
971
|
+
prompt,
|
|
972
|
+
system: systemTemplate,
|
|
973
|
+
history,
|
|
974
|
+
objects,
|
|
975
|
+
blueprints
|
|
976
|
+
});
|
|
977
|
+
const system = yield* formatSystemPrompt({
|
|
978
|
+
system: systemTemplate,
|
|
979
|
+
blueprints,
|
|
980
|
+
objects
|
|
981
|
+
}).pipe(Effect5.orDie);
|
|
982
|
+
do {
|
|
983
|
+
const { done } = yield* this.runAgentTurn({
|
|
984
|
+
system,
|
|
985
|
+
toolkit
|
|
986
|
+
});
|
|
987
|
+
if (done) {
|
|
988
|
+
break;
|
|
989
|
+
}
|
|
990
|
+
yield* this.runTools({
|
|
991
|
+
toolkit
|
|
992
|
+
});
|
|
993
|
+
} while (true);
|
|
994
|
+
log3("done", {
|
|
995
|
+
pending: this._pending.length,
|
|
996
|
+
duration: this.duration,
|
|
997
|
+
tools: this._toolCalls
|
|
998
|
+
}, {
|
|
999
|
+
F: __dxlog_file4,
|
|
1000
|
+
L: 329,
|
|
1001
|
+
S: this,
|
|
1002
|
+
C: (f, a) => f(...a)
|
|
1003
|
+
});
|
|
1004
|
+
return this._pending;
|
|
1005
|
+
}).pipe(this._semaphore.withPermits(1), Effect5.withSpan("AiRequest.run"));
|
|
1006
|
+
};
|
|
1007
|
+
var createSnippet = (text, len = 32) => text.length <= len * 2 ? text : [
|
|
1008
|
+
text.slice(0, len),
|
|
1009
|
+
"...",
|
|
1010
|
+
text.slice(-len)
|
|
1011
|
+
].join("");
|
|
1012
|
+
|
|
1013
|
+
// src/session/toolkit.ts
|
|
1014
|
+
import * as Toolkit from "@effect/ai/Toolkit";
|
|
1015
|
+
import * as Effect6 from "effect/Effect";
|
|
1016
|
+
import * as Layer2 from "effect/Layer";
|
|
1017
|
+
import { OpaqueToolkit as OpaqueToolkit2, ToolExecutionService as ToolExecutionService2, ToolResolverService as ToolResolverService2 } from "@dxos/ai";
|
|
1018
|
+
import { isTruthy } from "@dxos/util";
|
|
1019
|
+
var createToolkit = ({ toolkit: toolkitProp, blueprints = [], opaqueToolkits = [] }) => Effect6.gen(function* () {
|
|
1020
|
+
const blueprintToolkit = yield* ToolResolverService2.resolveToolkit(blueprints.flatMap(({ tools }) => tools));
|
|
1021
|
+
const blueprintToolHandler = yield* blueprintToolkit.toContext(ToolExecutionService2.handlersFor(blueprintToolkit));
|
|
1022
|
+
const opaqueToolkit = OpaqueToolkit2.merge(...opaqueToolkits);
|
|
1023
|
+
const toolkitDefs = [
|
|
1024
|
+
toolkitProp?.toolkit,
|
|
1025
|
+
blueprintToolkit,
|
|
1026
|
+
opaqueToolkit.toolkit
|
|
1027
|
+
].filter(isTruthy);
|
|
1028
|
+
const mergedToolkit = Toolkit.merge(...toolkitDefs);
|
|
1029
|
+
const combinedHandlerLayer = Layer2.mergeAll(Layer2.succeedContext(blueprintToolHandler), toolkitProp?.layer ?? OpaqueToolkit2.empty.layer, opaqueToolkit.layer);
|
|
1030
|
+
return OpaqueToolkit2.make(mergedToolkit, combinedHandlerLayer);
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
// src/session/version-pin.ts
|
|
1034
|
+
import * as Schema5 from "effect/Schema";
|
|
1035
|
+
import { getVersion } from "@dxos/echo-db";
|
|
1036
|
+
import { ObjectId } from "@dxos/keys";
|
|
1037
|
+
var ObjectVersionSchema = Schema5.Unknown;
|
|
1038
|
+
var VersionPinSchema = Schema5.Struct({
|
|
1039
|
+
// TODO(dmaretskyi): Use Ref when those support encoding.
|
|
1040
|
+
objectId: ObjectId,
|
|
1041
|
+
// TODO(dmaretskyi): Could be opaque.
|
|
1042
|
+
version: ObjectVersionSchema
|
|
1043
|
+
});
|
|
1044
|
+
var VersionPin = class extends VersionPinSchema {
|
|
1045
|
+
static DISPOSITION = "version-pin";
|
|
1046
|
+
static fromObject(object) {
|
|
1047
|
+
return VersionPin.make({
|
|
1048
|
+
objectId: object.id,
|
|
1049
|
+
version: getVersion(object)
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
static createBlock(pin) {
|
|
1053
|
+
return {
|
|
1054
|
+
_tag: "json",
|
|
1055
|
+
disposition: VersionPin.DISPOSITION,
|
|
1056
|
+
data: JSON.stringify(pin)
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
// src/conversation/session.ts
|
|
1062
|
+
import * as Array3 from "effect/Array";
|
|
1063
|
+
import * as Context4 from "effect/Context";
|
|
1064
|
+
import * as Effect7 from "effect/Effect";
|
|
1065
|
+
import * as Either from "effect/Either";
|
|
1066
|
+
import { pipe as pipe4 } from "effect/Function";
|
|
1067
|
+
import * as Layer3 from "effect/Layer";
|
|
1068
|
+
import * as Record2 from "effect/Record";
|
|
1069
|
+
import * as Runtime2 from "effect/Runtime";
|
|
1070
|
+
import { Resource as Resource2 } from "@dxos/context";
|
|
1071
|
+
import { Database as Database2, Feed as Feed2, Filter, Obj as Obj6 } from "@dxos/echo";
|
|
1072
|
+
import { acquireReleaseResource } from "@dxos/effect";
|
|
1073
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1074
|
+
import { log as log4 } from "@dxos/log";
|
|
1075
|
+
import { McpToolkit } from "@dxos/mcp-client";
|
|
1076
|
+
import { Operation as Operation2 } from "@dxos/operation";
|
|
1077
|
+
import { Message as Message3 } from "@dxos/types";
|
|
1078
|
+
var __dxlog_file5 = "/__w/dxos/dxos/packages/core/assistant/src/conversation/session.ts";
|
|
1079
|
+
var SUMMARY_THRESHOLD = 8e4;
|
|
1080
|
+
var AiSession = class extends Resource2 {
|
|
1081
|
+
/**
|
|
1082
|
+
* Blueprints and objects bound to the conversation.
|
|
1083
|
+
*/
|
|
1084
|
+
_binder;
|
|
1085
|
+
_feed;
|
|
1086
|
+
_runtime;
|
|
1087
|
+
constructor(options) {
|
|
1088
|
+
super();
|
|
1089
|
+
this._feed = options.feed;
|
|
1090
|
+
this._runtime = options.runtime;
|
|
1091
|
+
invariant2(this._feed, void 0, {
|
|
1092
|
+
F: __dxlog_file5,
|
|
1093
|
+
L: 83,
|
|
1094
|
+
S: this,
|
|
1095
|
+
A: [
|
|
1096
|
+
"this._feed",
|
|
1097
|
+
""
|
|
1098
|
+
]
|
|
1099
|
+
});
|
|
1100
|
+
invariant2(this._runtime, void 0, {
|
|
1101
|
+
F: __dxlog_file5,
|
|
1102
|
+
L: 84,
|
|
1103
|
+
S: this,
|
|
1104
|
+
A: [
|
|
1105
|
+
"this._runtime",
|
|
1106
|
+
""
|
|
1107
|
+
]
|
|
1108
|
+
});
|
|
1109
|
+
this._binder = new AiContextBinder({
|
|
1110
|
+
feed: this._feed,
|
|
1111
|
+
runtime: this._runtime,
|
|
1112
|
+
registry: options.registry
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
async _open() {
|
|
1116
|
+
await this._binder.open(this._ctx);
|
|
1117
|
+
}
|
|
1118
|
+
get feed() {
|
|
1119
|
+
return this._feed;
|
|
1120
|
+
}
|
|
1121
|
+
get context() {
|
|
1122
|
+
return this._binder;
|
|
1123
|
+
}
|
|
1124
|
+
async getHistory() {
|
|
1125
|
+
const queryResult = await Runtime2.runPromise(this._runtime)(Feed2.query(this._feed, Filter.type(Message3.Message)));
|
|
1126
|
+
const items = await queryResult.run();
|
|
1127
|
+
return items.filter(Obj6.instanceOf(Message3.Message));
|
|
1128
|
+
}
|
|
1129
|
+
getTools() {
|
|
1130
|
+
return Effect7.gen(this, function* () {
|
|
1131
|
+
const toolkit = yield* createToolkit({
|
|
1132
|
+
blueprints: this.context.getBlueprints()
|
|
1133
|
+
});
|
|
1134
|
+
return toolkit.toolkit.tools;
|
|
1135
|
+
}).pipe(Effect7.orDie);
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Not provided by default, since users might want to override them.
|
|
1139
|
+
*/
|
|
1140
|
+
makeToolExecutionServices() {
|
|
1141
|
+
return ToolExecutionServices.pipe(Layer3.provide(Operation2.withInvocationOptions({
|
|
1142
|
+
conversation: Obj6.getDXN(this._feed).toString()
|
|
1143
|
+
})));
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Creates a new cancelable request effect.
|
|
1147
|
+
*/
|
|
1148
|
+
createRequest(params) {
|
|
1149
|
+
return Effect7.gen(this, function* () {
|
|
1150
|
+
const history = yield* Effect7.promise(() => this.getHistory());
|
|
1151
|
+
const blueprints = this.context.getBlueprints();
|
|
1152
|
+
const objects = this.context.getObjects();
|
|
1153
|
+
log4("run", {
|
|
1154
|
+
history: history.length,
|
|
1155
|
+
blueprints: blueprints.length,
|
|
1156
|
+
objects: objects.length
|
|
1157
|
+
}, {
|
|
1158
|
+
F: __dxlog_file5,
|
|
1159
|
+
L: 140,
|
|
1160
|
+
S: this,
|
|
1161
|
+
C: (f, a) => f(...a)
|
|
1162
|
+
});
|
|
1163
|
+
const request = new AiRequest({
|
|
1164
|
+
summarizationThreshold: SUMMARY_THRESHOLD,
|
|
1165
|
+
observer: params.observer,
|
|
1166
|
+
onOutput: (message) => Effect7.promise(() => Runtime2.runPromise(this._runtime)(Feed2.append(this._feed, [
|
|
1167
|
+
message
|
|
1168
|
+
])))
|
|
1169
|
+
});
|
|
1170
|
+
yield* request.begin({
|
|
1171
|
+
history,
|
|
1172
|
+
blueprints,
|
|
1173
|
+
objects,
|
|
1174
|
+
prompt: params.prompt,
|
|
1175
|
+
system: params.system
|
|
1176
|
+
});
|
|
1177
|
+
do {
|
|
1178
|
+
yield* Effect7.promise(() => this.context.sync());
|
|
1179
|
+
const currentBlueprints = this.context.getBlueprints();
|
|
1180
|
+
const mcps = yield* connectMcpServers(currentBlueprints, params.mcpServers);
|
|
1181
|
+
const toolkit = yield* createToolkit({
|
|
1182
|
+
toolkit: params.toolkit,
|
|
1183
|
+
blueprints: currentBlueprints,
|
|
1184
|
+
opaqueToolkits: mcps
|
|
1185
|
+
});
|
|
1186
|
+
log4("toolkit", {
|
|
1187
|
+
tools: Record2.keys(toolkit.toolkit.tools)
|
|
1188
|
+
}, {
|
|
1189
|
+
F: __dxlog_file5,
|
|
1190
|
+
L: 172,
|
|
1191
|
+
S: this,
|
|
1192
|
+
C: (f, a) => f(...a)
|
|
1193
|
+
});
|
|
1194
|
+
const system = yield* formatSystemPrompt({
|
|
1195
|
+
system: params.system,
|
|
1196
|
+
blueprints: currentBlueprints,
|
|
1197
|
+
objects: this.context.getObjects()
|
|
1198
|
+
}).pipe(Effect7.orDie);
|
|
1199
|
+
const { done } = yield* request.runAgentTurn({
|
|
1200
|
+
system,
|
|
1201
|
+
toolkit
|
|
1202
|
+
});
|
|
1203
|
+
if (done) {
|
|
1204
|
+
break;
|
|
1205
|
+
}
|
|
1206
|
+
yield* request.runTools({
|
|
1207
|
+
toolkit
|
|
1208
|
+
});
|
|
1209
|
+
} while (true);
|
|
1210
|
+
log4("result", {
|
|
1211
|
+
messages: request.pending.length,
|
|
1212
|
+
duration: request.duration,
|
|
1213
|
+
toolCalls: request.toolCalls
|
|
1214
|
+
}, {
|
|
1215
|
+
F: __dxlog_file5,
|
|
1216
|
+
L: 187,
|
|
1217
|
+
S: this,
|
|
1218
|
+
C: (f, a) => f(...a)
|
|
1219
|
+
});
|
|
1220
|
+
return [
|
|
1221
|
+
...request.pending
|
|
1222
|
+
];
|
|
1223
|
+
}).pipe(Effect7.provide(Layer3.mergeAll(Layer3.succeed(AiContextService, {
|
|
1224
|
+
binder: this.context
|
|
1225
|
+
}), Layer3.succeed(AiSessionService, this), Operation2.withInvocationOptions({
|
|
1226
|
+
conversation: Obj6.getDXN(this._feed).toString()
|
|
1227
|
+
}))), Effect7.withSpan("AiSession.createRequest"));
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
var AiSessionService = class _AiSessionService extends Context4.Tag("@dxos/assistant/AiSessionService")() {
|
|
1231
|
+
/**
|
|
1232
|
+
* Create a new session layer from options.
|
|
1233
|
+
*/
|
|
1234
|
+
static layer = (options) => aiContextFromSession.pipe(Layer3.provideMerge(Layer3.scoped(_AiSessionService, Effect7.gen(function* () {
|
|
1235
|
+
const session = yield* acquireReleaseResource(() => new AiSession(options));
|
|
1236
|
+
return session;
|
|
1237
|
+
}))));
|
|
1238
|
+
/**
|
|
1239
|
+
* Create a new session with a new feed.
|
|
1240
|
+
*/
|
|
1241
|
+
static layerNewFeed = (options) => Layer3.unwrapScoped(Effect7.gen(function* () {
|
|
1242
|
+
const feed = Feed2.make();
|
|
1243
|
+
yield* Database2.add(feed);
|
|
1244
|
+
const runtime4 = yield* Effect7.runtime();
|
|
1245
|
+
return _AiSessionService.layer({
|
|
1246
|
+
...options,
|
|
1247
|
+
feed,
|
|
1248
|
+
runtime: runtime4
|
|
1249
|
+
});
|
|
1250
|
+
}));
|
|
1251
|
+
/**
|
|
1252
|
+
* Run a prompt in the current session.
|
|
1253
|
+
*/
|
|
1254
|
+
static run = (params) => Effect7.gen(function* () {
|
|
1255
|
+
const session = yield* _AiSessionService;
|
|
1256
|
+
return yield* session.createRequest(params);
|
|
1257
|
+
});
|
|
1258
|
+
};
|
|
1259
|
+
var aiContextFromSession = Layer3.effect(AiContextService, Effect7.gen(function* () {
|
|
1260
|
+
const session = yield* AiSessionService;
|
|
1261
|
+
return {
|
|
1262
|
+
binder: session.context
|
|
1263
|
+
};
|
|
1264
|
+
}));
|
|
1265
|
+
var connectMcpServers = (blueprints, spaceMcpServers = []) => {
|
|
1266
|
+
const blueprintServers = pipe4(blueprints, Array3.flatMap((_) => _.mcpServers ?? []), Array3.map(({ url, protocol }) => ({
|
|
1267
|
+
url,
|
|
1268
|
+
kind: protocol
|
|
1269
|
+
})));
|
|
1270
|
+
const spaceServers = spaceMcpServers.map(({ url, protocol, apiKey }) => ({
|
|
1271
|
+
url,
|
|
1272
|
+
kind: protocol,
|
|
1273
|
+
apiKey
|
|
1274
|
+
}));
|
|
1275
|
+
const allServers = [
|
|
1276
|
+
...blueprintServers,
|
|
1277
|
+
...spaceServers
|
|
1278
|
+
];
|
|
1279
|
+
return pipe4(allServers, Effect7.forEach((options) => McpToolkit.make(options).pipe(
|
|
1280
|
+
// NOTE: Type-inference fails here without explicit void return.
|
|
1281
|
+
Effect7.tap((toolkit) => log4.info("Connected to MCP server", {
|
|
1282
|
+
url: options.url,
|
|
1283
|
+
tools: Object.keys(toolkit.toolkit.tools).length
|
|
1284
|
+
}, {
|
|
1285
|
+
F: __dxlog_file5,
|
|
1286
|
+
L: 292,
|
|
1287
|
+
S: void 0,
|
|
1288
|
+
C: (f, a) => f(...a)
|
|
1289
|
+
})),
|
|
1290
|
+
Effect7.tapDefect((error) => Effect7.sync(() => log4.warn("Failed to connect to MCP server", {
|
|
1291
|
+
error
|
|
1292
|
+
}, {
|
|
1293
|
+
F: __dxlog_file5,
|
|
1294
|
+
L: 294,
|
|
1295
|
+
S: void 0,
|
|
1296
|
+
C: (f, a) => f(...a)
|
|
1297
|
+
}))),
|
|
1298
|
+
Effect7.either
|
|
1299
|
+
)), Effect7.map(Array3.filterMap((_) => Either.getRight(_))));
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
// src/service/agent-process.ts
|
|
1303
|
+
import * as Tool2 from "@effect/ai/Tool";
|
|
1304
|
+
import * as Toolkit2 from "@effect/ai/Toolkit";
|
|
1305
|
+
import * as Cause from "effect/Cause";
|
|
1306
|
+
import * as Duration from "effect/Duration";
|
|
1307
|
+
import * as Effect8 from "effect/Effect";
|
|
1308
|
+
import * as Exit from "effect/Exit";
|
|
1309
|
+
import * as Layer4 from "effect/Layer";
|
|
1310
|
+
import * as Match from "effect/Match";
|
|
1311
|
+
import * as Option4 from "effect/Option";
|
|
1312
|
+
import * as Schema6 from "effect/Schema";
|
|
1313
|
+
import { AiService, OpaqueToolkit as OpaqueToolkit3 } from "@dxos/ai";
|
|
1314
|
+
import { Database as Database3, DXN as DXN3, Feed as Feed3, Obj as Obj7 } from "@dxos/echo";
|
|
1315
|
+
import { acquireReleaseResource as acquireReleaseResource2 } from "@dxos/effect";
|
|
1316
|
+
import { Trace as Trace3 } from "@dxos/functions";
|
|
1317
|
+
import { Process, ProcessManager, StorageService } from "@dxos/functions-runtime";
|
|
1318
|
+
import { log as log5 } from "@dxos/log";
|
|
1319
|
+
import { Operation as Operation3, OperationRegistry as OperationRegistry2 } from "@dxos/operation";
|
|
1320
|
+
import { trim as trim3 } from "@dxos/util";
|
|
1321
|
+
var __dxlog_file6 = "/__w/dxos/dxos/packages/core/assistant/src/service/agent-process.ts";
|
|
1322
|
+
var AGENT_PROCESS_KEY = "org.dxos.testing.process.agent";
|
|
1323
|
+
var AgentProcess = (options) => Process.make({
|
|
1324
|
+
key: AGENT_PROCESS_KEY,
|
|
1325
|
+
input: Schema6.String,
|
|
1326
|
+
output: Schema6.Void,
|
|
1327
|
+
services: [
|
|
1328
|
+
Database3.Service,
|
|
1329
|
+
OpaqueToolkit3.OpaqueToolkitProvider,
|
|
1330
|
+
Operation3.Service,
|
|
1331
|
+
OperationRegistry2.Service,
|
|
1332
|
+
StorageService.StorageService,
|
|
1333
|
+
Feed3.FeedService,
|
|
1334
|
+
ProcessManager.ProcessOperationInvoker.Service,
|
|
1335
|
+
AiService.AiService
|
|
1336
|
+
]
|
|
1337
|
+
}, (ctx) => Effect8.gen(function* () {
|
|
1338
|
+
const feedDxn = ctx.params.target;
|
|
1339
|
+
if (feedDxn == null) {
|
|
1340
|
+
return yield* Effect8.die(new Error("Agent executable requires spawn options.target set to a queue DXN."));
|
|
1341
|
+
}
|
|
1342
|
+
const feed = yield* Database3.resolve(DXN3.parse(feedDxn), Feed3.Feed).pipe(Effect8.orDie);
|
|
1343
|
+
const runtime4 = yield* Effect8.runtime();
|
|
1344
|
+
const session = yield* acquireReleaseResource2(() => new AiSession({
|
|
1345
|
+
feed,
|
|
1346
|
+
runtime: runtime4
|
|
1347
|
+
}));
|
|
1348
|
+
let inputQueue = [
|
|
1349
|
+
...yield* AgentEventsKey.get
|
|
1350
|
+
];
|
|
1351
|
+
const storageService = yield* StorageService.StorageService;
|
|
1352
|
+
const toolCallManager = new ToolCallManager(storageService);
|
|
1353
|
+
yield* toolCallManager.load();
|
|
1354
|
+
return {
|
|
1355
|
+
onInput: Effect8.fnUntraced(function* (prompt) {
|
|
1356
|
+
log5("agent onInput received", {
|
|
1357
|
+
promptLength: prompt.length,
|
|
1358
|
+
backlog: inputQueue.length
|
|
1359
|
+
}, {
|
|
1360
|
+
F: __dxlog_file6,
|
|
1361
|
+
L: 78,
|
|
1362
|
+
S: this,
|
|
1363
|
+
C: (f, a) => f(...a)
|
|
1364
|
+
});
|
|
1365
|
+
inputQueue.push({
|
|
1366
|
+
_tag: "prompt",
|
|
1367
|
+
content: prompt
|
|
1368
|
+
});
|
|
1369
|
+
log5("agent onInput persisting queue", {
|
|
1370
|
+
depth: inputQueue.length
|
|
1371
|
+
}, {
|
|
1372
|
+
F: __dxlog_file6,
|
|
1373
|
+
L: 80,
|
|
1374
|
+
S: this,
|
|
1375
|
+
C: (f, a) => f(...a)
|
|
1376
|
+
});
|
|
1377
|
+
yield* AgentEventsKey.set(inputQueue);
|
|
1378
|
+
log5("agent onInput persisted", {
|
|
1379
|
+
depth: inputQueue.length
|
|
1380
|
+
}, {
|
|
1381
|
+
F: __dxlog_file6,
|
|
1382
|
+
L: 82,
|
|
1383
|
+
S: this,
|
|
1384
|
+
C: (f, a) => f(...a)
|
|
1385
|
+
});
|
|
1386
|
+
ctx.setAlarm();
|
|
1387
|
+
log5("agent onInput alarm scheduled", void 0, {
|
|
1388
|
+
F: __dxlog_file6,
|
|
1389
|
+
L: 84,
|
|
1390
|
+
S: this,
|
|
1391
|
+
C: (f, a) => f(...a)
|
|
1392
|
+
});
|
|
1393
|
+
}),
|
|
1394
|
+
onAlarm: Effect8.fnUntraced(function* () {
|
|
1395
|
+
log5("agent onAlarm fired", {
|
|
1396
|
+
pending: inputQueue.length
|
|
1397
|
+
}, {
|
|
1398
|
+
F: __dxlog_file6,
|
|
1399
|
+
L: 88,
|
|
1400
|
+
S: this,
|
|
1401
|
+
C: (f, a) => f(...a)
|
|
1402
|
+
});
|
|
1403
|
+
const item = inputQueue.shift();
|
|
1404
|
+
if (!item) {
|
|
1405
|
+
log5("agent onAlarm empty queue", {}, {
|
|
1406
|
+
F: __dxlog_file6,
|
|
1407
|
+
L: 91,
|
|
1408
|
+
S: this,
|
|
1409
|
+
C: (f, a) => f(...a)
|
|
1410
|
+
});
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
if (item._tag === "tool_result" && toolCallManager.isReported(item.pid)) {
|
|
1414
|
+
log5.info("skip tool result that was reported synchronously", {
|
|
1415
|
+
pid: item.pid
|
|
1416
|
+
}, {
|
|
1417
|
+
F: __dxlog_file6,
|
|
1418
|
+
L: 96,
|
|
1419
|
+
S: this,
|
|
1420
|
+
C: (f, a) => f(...a)
|
|
1421
|
+
});
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
log5("agent onAlarm handling", {
|
|
1425
|
+
tag: item._tag
|
|
1426
|
+
}, {
|
|
1427
|
+
F: __dxlog_file6,
|
|
1428
|
+
L: 101,
|
|
1429
|
+
S: this,
|
|
1430
|
+
C: (f, a) => f(...a)
|
|
1431
|
+
});
|
|
1432
|
+
const prompt = Match.value(item).pipe(Match.tag("prompt", (item2) => item2.content), Match.tag("tool_result", (item2) => item2.isError ? toolErrorResponse(item2.pid, item2.result) : toolResultResponse(item2.pid, item2.result)), Match.exhaustive);
|
|
1433
|
+
log5("begin request", {
|
|
1434
|
+
prompt
|
|
1435
|
+
}, {
|
|
1436
|
+
F: __dxlog_file6,
|
|
1437
|
+
L: 113,
|
|
1438
|
+
S: this,
|
|
1439
|
+
C: (f, a) => f(...a)
|
|
1440
|
+
});
|
|
1441
|
+
yield* Trace3.write(AgentRequestBegin, {});
|
|
1442
|
+
yield* session.createRequest({
|
|
1443
|
+
prompt,
|
|
1444
|
+
// TODO(dmaretskyi): Polling currently broken, agent relies on completion notifications being delivered.
|
|
1445
|
+
// toolkit: AsynchronousExectionToolkit,
|
|
1446
|
+
system: options.systemPrompt,
|
|
1447
|
+
mcpServers: options.getMcpServers?.()
|
|
1448
|
+
}).pipe(Effect8.ensuring(Trace3.write(AgentRequestEnd, {})));
|
|
1449
|
+
log5("end request", void 0, {
|
|
1450
|
+
F: __dxlog_file6,
|
|
1451
|
+
L: 124,
|
|
1452
|
+
S: this,
|
|
1453
|
+
C: (f, a) => f(...a)
|
|
1454
|
+
});
|
|
1455
|
+
yield* AgentEventsKey.set(inputQueue);
|
|
1456
|
+
if (inputQueue.length > 0) {
|
|
1457
|
+
ctx.setAlarm();
|
|
1458
|
+
}
|
|
1459
|
+
}, Effect8.orDie, Effect8.provide(Layer4.mergeAll(makeToolResolverFromOperations(), ToolExecutionService3({
|
|
1460
|
+
toolCallManager,
|
|
1461
|
+
feed
|
|
1462
|
+
}), AsynchronousExectionToolkitLayer, AiService.model(options.model ?? "@anthropic/claude-opus-4-6")).pipe(Layer4.orDie))),
|
|
1463
|
+
onChildEvent: Effect8.fnUntraced(function* (event) {
|
|
1464
|
+
log5("childEvent", {
|
|
1465
|
+
event
|
|
1466
|
+
}, {
|
|
1467
|
+
F: __dxlog_file6,
|
|
1468
|
+
L: 144,
|
|
1469
|
+
S: this,
|
|
1470
|
+
C: (f, a) => f(...a)
|
|
1471
|
+
});
|
|
1472
|
+
if (event._tag === "exited") {
|
|
1473
|
+
if (!toolCallManager.isToolCall(event.pid)) {
|
|
1474
|
+
log5.verbose("childEvent ignored non-tool call", {
|
|
1475
|
+
pid: event.pid
|
|
1476
|
+
}, {
|
|
1477
|
+
F: __dxlog_file6,
|
|
1478
|
+
L: 147,
|
|
1479
|
+
S: this,
|
|
1480
|
+
C: (f, a) => f(...a)
|
|
1481
|
+
});
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
const operationInvoker = yield* ProcessManager.ProcessOperationInvoker.Service;
|
|
1485
|
+
const fiber = yield* operationInvoker.attachFiber(event.pid).pipe(Effect8.orDie);
|
|
1486
|
+
const result = yield* fiber.await.pipe(Effect8.orDie).pipe(Effect8.map(Exit.match({
|
|
1487
|
+
onSuccess: (value2) => ({
|
|
1488
|
+
_tag: "tool_result",
|
|
1489
|
+
pid: event.pid,
|
|
1490
|
+
result: value2,
|
|
1491
|
+
isError: false
|
|
1492
|
+
}),
|
|
1493
|
+
onFailure: (cause) => ({
|
|
1494
|
+
_tag: "tool_result",
|
|
1495
|
+
pid: event.pid,
|
|
1496
|
+
result: Cause.pretty(cause),
|
|
1497
|
+
isError: true
|
|
1498
|
+
})
|
|
1499
|
+
})));
|
|
1500
|
+
inputQueue.push(result);
|
|
1501
|
+
log5("agent onChildEvent persisted tool result", {
|
|
1502
|
+
depth: inputQueue.length,
|
|
1503
|
+
childPid: event.pid
|
|
1504
|
+
}, {
|
|
1505
|
+
F: __dxlog_file6,
|
|
1506
|
+
L: 172,
|
|
1507
|
+
S: this,
|
|
1508
|
+
C: (f, a) => f(...a)
|
|
1509
|
+
});
|
|
1510
|
+
yield* AgentEventsKey.set(inputQueue);
|
|
1511
|
+
ctx.setAlarm();
|
|
1512
|
+
log5("agent onChildEvent alarm scheduled", {
|
|
1513
|
+
depth: inputQueue.length
|
|
1514
|
+
}, {
|
|
1515
|
+
F: __dxlog_file6,
|
|
1516
|
+
L: 175,
|
|
1517
|
+
S: this,
|
|
1518
|
+
C: (f, a) => f(...a)
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
})
|
|
1522
|
+
};
|
|
1523
|
+
}));
|
|
1524
|
+
var AgentEvent = Schema6.Union(Schema6.TaggedStruct("prompt", {
|
|
1525
|
+
content: Schema6.String
|
|
1526
|
+
}), Schema6.TaggedStruct("tool_result", {
|
|
1527
|
+
pid: Process.ID,
|
|
1528
|
+
result: Schema6.Unknown,
|
|
1529
|
+
isError: Schema6.Boolean
|
|
1530
|
+
}));
|
|
1531
|
+
var AgentEventsKey = StorageService.key(Schema6.parseJson(Schema6.Array(AgentEvent).pipe(Schema6.mutable)), "inputQueue").pipe(StorageService.withDefault(() => []));
|
|
1532
|
+
var ToolCallState = Schema6.Struct({
|
|
1533
|
+
activeCalls: Schema6.Array(Schema6.Struct({
|
|
1534
|
+
pid: Process.ID,
|
|
1535
|
+
// Whether the result was reported to the agent.
|
|
1536
|
+
reported: Schema6.Boolean
|
|
1537
|
+
}).pipe(Schema6.mutable)).pipe(Schema6.mutable)
|
|
1538
|
+
});
|
|
1539
|
+
var ToolCallStateKey = StorageService.key(Schema6.parseJson(ToolCallState.pipe(Schema6.mutable)), "toolCallState").pipe(StorageService.withDefault(() => ({
|
|
1540
|
+
activeCalls: []
|
|
1541
|
+
})));
|
|
1542
|
+
var ToolCallManager = class {
|
|
1543
|
+
#storageService;
|
|
1544
|
+
#state = {
|
|
1545
|
+
activeCalls: []
|
|
1546
|
+
};
|
|
1547
|
+
constructor(storageService) {
|
|
1548
|
+
this.#storageService = storageService;
|
|
1549
|
+
}
|
|
1550
|
+
load() {
|
|
1551
|
+
return Effect8.gen(this, function* () {
|
|
1552
|
+
this.#state = yield* ToolCallStateKey.get;
|
|
1553
|
+
}).pipe(Effect8.provideService(StorageService.StorageService, this.#storageService));
|
|
1554
|
+
}
|
|
1555
|
+
beginCall(pid) {
|
|
1556
|
+
return Effect8.gen(this, function* () {
|
|
1557
|
+
this.#state.activeCalls.push({
|
|
1558
|
+
pid,
|
|
1559
|
+
reported: false
|
|
1560
|
+
});
|
|
1561
|
+
yield* ToolCallStateKey.set(this.#state);
|
|
1562
|
+
}).pipe(Effect8.provideService(StorageService.StorageService, this.#storageService));
|
|
1563
|
+
}
|
|
1564
|
+
markAsReported(pid) {
|
|
1565
|
+
return Effect8.gen(this, function* () {
|
|
1566
|
+
const call = this.#state.activeCalls.find((call2) => call2.pid === pid);
|
|
1567
|
+
if (!call) {
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
call.reported = true;
|
|
1571
|
+
yield* ToolCallStateKey.set(this.#state);
|
|
1572
|
+
}).pipe(Effect8.provideService(StorageService.StorageService, this.#storageService));
|
|
1573
|
+
}
|
|
1574
|
+
isToolCall(pid) {
|
|
1575
|
+
return this.#state.activeCalls.some((call) => call.pid === pid);
|
|
1576
|
+
}
|
|
1577
|
+
isReported(pid) {
|
|
1578
|
+
return this.#state.activeCalls.some((call) => call.pid === pid && call.reported);
|
|
1579
|
+
}
|
|
1580
|
+
};
|
|
1581
|
+
var ToolExecutionService3 = ({ backgroundThreshold = Duration.seconds(1), toolCallManager, feed }) => Layer4.unwrapEffect(Effect8.gen(function* () {
|
|
1582
|
+
const operationInvoker = yield* ProcessManager.ProcessOperationInvoker.Service;
|
|
1583
|
+
return makeToolExecutionService({
|
|
1584
|
+
invoke: (tool, input) => Effect8.gen(function* () {
|
|
1585
|
+
const operationDef = getOperationFromTool(tool).pipe(Option4.getOrThrow);
|
|
1586
|
+
log5("invoking operation", {
|
|
1587
|
+
operationDef,
|
|
1588
|
+
input
|
|
1589
|
+
}, {
|
|
1590
|
+
F: __dxlog_file6,
|
|
1591
|
+
L: 279,
|
|
1592
|
+
S: this,
|
|
1593
|
+
C: (f, a) => f(...a)
|
|
1594
|
+
});
|
|
1595
|
+
const fiber = yield* operationInvoker.invokeFiber(operationDef, input, {
|
|
1596
|
+
environment: {
|
|
1597
|
+
conversation: Obj7.getDXN(feed).toString()
|
|
1598
|
+
},
|
|
1599
|
+
traceMeta: {
|
|
1600
|
+
conversationId: feed.id
|
|
1601
|
+
}
|
|
1602
|
+
});
|
|
1603
|
+
toolCallManager.beginCall(fiber.pid);
|
|
1604
|
+
log5("invoked operation", {
|
|
1605
|
+
operationDef,
|
|
1606
|
+
input,
|
|
1607
|
+
fiber
|
|
1608
|
+
}, {
|
|
1609
|
+
F: __dxlog_file6,
|
|
1610
|
+
L: 289,
|
|
1611
|
+
S: this,
|
|
1612
|
+
C: (f, a) => f(...a)
|
|
1613
|
+
});
|
|
1614
|
+
const result = yield* fiber.await.pipe(Effect8.tap(() => toolCallManager.markAsReported(fiber.pid)), Effect8.timeout(backgroundThreshold), Effect8.catchTag("TimeoutException", () => Effect8.succeed(toolIsRunningInBackgroundResponse(fiber.pid))));
|
|
1615
|
+
log5("result", {
|
|
1616
|
+
result
|
|
1617
|
+
}, {
|
|
1618
|
+
F: __dxlog_file6,
|
|
1619
|
+
L: 296,
|
|
1620
|
+
S: this,
|
|
1621
|
+
C: (f, a) => f(...a)
|
|
1622
|
+
});
|
|
1623
|
+
return result;
|
|
1624
|
+
})
|
|
1625
|
+
});
|
|
1626
|
+
}));
|
|
1627
|
+
var AsynchronousExectionToolkit = class extends Toolkit2.make(Tool2.make("poll-tools", {
|
|
1628
|
+
description: trim3`
|
|
1629
|
+
Poll tool calls running in the background.
|
|
1630
|
+
Set wait to true to wait for the tool call to complete before returning.
|
|
1631
|
+
Only set wait to true if you dont have other tasks to perform in parallel.
|
|
1632
|
+
Set an appropriate timeout to avoid waiting forever.
|
|
1633
|
+
You will also be notified about the job completion separatelly, so you do not always need to inspect the job if you dont need the result right now.
|
|
1634
|
+
`,
|
|
1635
|
+
parameters: {
|
|
1636
|
+
ids: Schema6.Array(Schema6.String).annotations({
|
|
1637
|
+
description: "The IDs of the jobs to inspect."
|
|
1638
|
+
}),
|
|
1639
|
+
wait: Schema6.optional(Schema6.Boolean).annotations({
|
|
1640
|
+
description: "Whether to wait for the job to complete before returning.",
|
|
1641
|
+
default: false
|
|
1642
|
+
}),
|
|
1643
|
+
timeout: Schema6.optional(Schema6.Number).annotations({
|
|
1644
|
+
description: "Maximum time to wait for the job to complete. If the job does not complete within the timeout, the current state is returned.",
|
|
1645
|
+
default: 1e4
|
|
1646
|
+
})
|
|
1647
|
+
}
|
|
1648
|
+
})) {
|
|
1649
|
+
};
|
|
1650
|
+
var AsynchronousExectionToolkitLayer = AsynchronousExectionToolkit.toLayer(Effect8.gen(function* () {
|
|
1651
|
+
const invoker = yield* ProcessManager.ProcessOperationInvoker.Service;
|
|
1652
|
+
return {
|
|
1653
|
+
"poll-tools": ({ ids, wait, timeout: timeout2 = 1e4 }) => Effect8.gen(function* () {
|
|
1654
|
+
return yield* Effect8.forEach(ids, (pid) => invoker.attachFiber(Process.ID.make(pid)).pipe(Effect8.flatMap((_) => _.await), Effect8.timeout(Duration.millis(timeout2)), Effect8.flatMap(Exit.match({
|
|
1655
|
+
onSuccess: (value2) => Effect8.succeed(toolResultResponse(pid, value2)),
|
|
1656
|
+
onFailure: (cause) => Effect8.succeed(toolErrorResponse(pid, Cause.pretty(cause)))
|
|
1657
|
+
})), Effect8.catchTag("ProcessNotFoundError", () => Effect8.succeed(`Process not found: ${pid}`)), Effect8.catchTag("TimeoutException", () => Effect8.succeed(`Process still running: ${pid}`))));
|
|
1658
|
+
})
|
|
1659
|
+
};
|
|
1660
|
+
}));
|
|
1661
|
+
var toolIsRunningInBackgroundResponse = (pid) => `Tool is running in the background id=${pid}; wait for the completion notification to get the result.`;
|
|
1662
|
+
var toolResultResponse = (pid, value2) => `<result pid=${pid}>${JSON.stringify(value2)}</result>`;
|
|
1663
|
+
var toolErrorResponse = (pid, cause) => `<error pid=${pid}>${cause}</error>`;
|
|
1664
|
+
|
|
1665
|
+
// src/service/AgentService.ts
|
|
1666
|
+
var AgentService_exports = {};
|
|
1667
|
+
__export(AgentService_exports, {
|
|
1668
|
+
AgentService: () => AgentService,
|
|
1669
|
+
createSession: () => createSession,
|
|
1670
|
+
getSession: () => getSession,
|
|
1671
|
+
layer: () => layer
|
|
1672
|
+
});
|
|
1673
|
+
import * as Context5 from "effect/Context";
|
|
1674
|
+
import * as Effect9 from "effect/Effect";
|
|
1675
|
+
import * as Layer5 from "effect/Layer";
|
|
1676
|
+
import { Blueprint as Blueprint2 } from "@dxos/blueprints";
|
|
1677
|
+
import { Database as Database4, Feed as Feed4, Obj as Obj8, Ref as Ref4 } from "@dxos/echo";
|
|
1678
|
+
import { acquireReleaseResource as acquireReleaseResource3 } from "@dxos/effect";
|
|
1679
|
+
import { ProcessManager as ProcessManager2 } from "@dxos/functions-runtime";
|
|
1680
|
+
var AgentService = class extends Context5.Tag("@dxos/assistant/AgentService")() {
|
|
1681
|
+
};
|
|
1682
|
+
var getSession = Effect9.serviceFunctionEffect(AgentService, (service) => service.getSession);
|
|
1683
|
+
var createSession = Effect9.fn("createSession")(function* (opts) {
|
|
1684
|
+
const blueprints = yield* Effect9.forEach(opts?.blueprints ?? [], (blueprint) => Blueprint2.upsert(blueprint.key).pipe(Effect9.map(Ref4.make)));
|
|
1685
|
+
const feed = yield* Database4.add(Feed4.make());
|
|
1686
|
+
const runtime4 = yield* Effect9.runtime();
|
|
1687
|
+
const binder = yield* acquireReleaseResource3(() => new AiContextBinder({
|
|
1688
|
+
feed,
|
|
1689
|
+
runtime: runtime4
|
|
1690
|
+
}));
|
|
1691
|
+
yield* Effect9.promise(() => binder.bind({
|
|
1692
|
+
blueprints,
|
|
1693
|
+
objects: opts?.context ?? []
|
|
1694
|
+
}));
|
|
1695
|
+
return yield* getSession(feed, {
|
|
1696
|
+
model: opts?.model
|
|
1697
|
+
});
|
|
1698
|
+
}, Effect9.scoped);
|
|
1699
|
+
var layer = (opts) => Layer5.effect(AgentService, Effect9.gen(function* () {
|
|
1700
|
+
const processManager = yield* ProcessManager2.ProcessManagerService;
|
|
1701
|
+
const sessionCache = /* @__PURE__ */ new Map();
|
|
1702
|
+
return {
|
|
1703
|
+
getSession: (feed, options) => Effect9.gen(function* () {
|
|
1704
|
+
const cached = sessionCache.get(feed.id);
|
|
1705
|
+
if (cached) {
|
|
1706
|
+
return cached;
|
|
1707
|
+
}
|
|
1708
|
+
const target = Obj8.getDXN(feed).toString();
|
|
1709
|
+
const executable = AgentProcess({
|
|
1710
|
+
systemPrompt: opts?.systemPrompt,
|
|
1711
|
+
model: options?.model ?? opts?.model,
|
|
1712
|
+
getMcpServers: opts?.getMcpServers
|
|
1713
|
+
});
|
|
1714
|
+
const processes = yield* processManager.list({
|
|
1715
|
+
target,
|
|
1716
|
+
key: executable.key
|
|
1717
|
+
});
|
|
1718
|
+
let handle;
|
|
1719
|
+
if (processes.length > 0) {
|
|
1720
|
+
handle = processes[0];
|
|
1721
|
+
} else {
|
|
1722
|
+
handle = yield* processManager.spawn(executable, {
|
|
1723
|
+
name: "agent",
|
|
1724
|
+
target,
|
|
1725
|
+
traceMeta: {
|
|
1726
|
+
conversationId: feed.id
|
|
1727
|
+
}
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
const session = makeSession(handle, feed);
|
|
1731
|
+
sessionCache.set(feed.id, session);
|
|
1732
|
+
return session;
|
|
1733
|
+
})
|
|
1734
|
+
};
|
|
1735
|
+
}));
|
|
1736
|
+
var makeSession = (process, feed) => ({
|
|
1737
|
+
feed,
|
|
1738
|
+
submitPrompt: (prompt) => process.submitInput(prompt),
|
|
1739
|
+
waitForCompletion: () => process.runToCompletion(),
|
|
1740
|
+
subscribeEphemeral: () => process.subscribeEphemeral(),
|
|
1741
|
+
addContext: (context) => Effect9.gen(function* () {
|
|
1742
|
+
const runtime4 = yield* Effect9.runtime();
|
|
1743
|
+
const binder = yield* acquireReleaseResource3(() => new AiContextBinder({
|
|
1744
|
+
feed,
|
|
1745
|
+
runtime: runtime4
|
|
1746
|
+
}));
|
|
1747
|
+
yield* Effect9.promise(() => binder.bind({
|
|
1748
|
+
blueprints: [],
|
|
1749
|
+
objects: context
|
|
1750
|
+
}));
|
|
1751
|
+
}).pipe(Effect9.scoped),
|
|
1752
|
+
getContext: () => Effect9.gen(function* () {
|
|
1753
|
+
const runtime4 = yield* Effect9.runtime();
|
|
1754
|
+
const binder = yield* acquireReleaseResource3(() => new AiContextBinder({
|
|
1755
|
+
feed,
|
|
1756
|
+
runtime: runtime4
|
|
1757
|
+
}));
|
|
1758
|
+
return binder.getObjects().map((object) => Ref4.make(object));
|
|
1759
|
+
}).pipe(Effect9.scoped)
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
export {
|
|
1763
|
+
ContextBinding,
|
|
1764
|
+
Bindings,
|
|
1765
|
+
AiContextBinder,
|
|
1766
|
+
AiContextService,
|
|
1767
|
+
createArtifactElement,
|
|
1768
|
+
ArtifactId,
|
|
1769
|
+
RefFromLLM,
|
|
1770
|
+
makeToolResolverFromOperations,
|
|
1771
|
+
makeToolExecutionService,
|
|
1772
|
+
makeToolExecutionServiceFromOperationInvoker,
|
|
1773
|
+
ToolExecutionServices,
|
|
1774
|
+
getOperationFromTool,
|
|
1775
|
+
ArtifactDiffResolver,
|
|
1776
|
+
GenerationObserver,
|
|
1777
|
+
PartialBlock,
|
|
1778
|
+
CompleteBlock,
|
|
1779
|
+
AgentRequestBegin,
|
|
1780
|
+
AgentRequestEnd,
|
|
1781
|
+
AiModelError,
|
|
1782
|
+
AiAssistantError,
|
|
1783
|
+
formatSystemPrompt,
|
|
1784
|
+
formatUserPrompt,
|
|
1785
|
+
AiRequest,
|
|
1786
|
+
createToolkit,
|
|
1787
|
+
VersionPin,
|
|
1788
|
+
AiSession,
|
|
1789
|
+
AiSessionService,
|
|
1790
|
+
AGENT_PROCESS_KEY,
|
|
1791
|
+
AgentService_exports
|
|
1792
|
+
};
|
|
1793
|
+
//# sourceMappingURL=chunk-2TBLG3XY.mjs.map
|