@dxos/assistant 0.8.4-main.67995b8 → 0.8.4-main.a4bbb77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/chunk-ILL2CN3G.mjs +374 -0
- package/dist/lib/browser/chunk-ILL2CN3G.mjs.map +7 -0
- package/dist/lib/browser/{chunk-6WRGDEMH.mjs → chunk-UB26N5UF.mjs} +2 -2
- package/dist/lib/browser/chunk-UB26N5UF.mjs.map +7 -0
- package/dist/lib/browser/extraction/index.mjs +32 -29
- package/dist/lib/browser/extraction/index.mjs.map +4 -4
- package/dist/lib/browser/index.mjs +682 -1035
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{named-entity-recognition-O46UTCLR.mjs → named-entity-recognition-QPYHML66.mjs} +2 -2
- package/dist/lib/node-esm/{chunk-3UU5V4FS.mjs → chunk-BCFXTNSK.mjs} +2 -2
- package/dist/lib/node-esm/chunk-BCFXTNSK.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-GVKA7VL3.mjs +376 -0
- package/dist/lib/node-esm/chunk-GVKA7VL3.mjs.map +7 -0
- package/dist/lib/node-esm/extraction/index.mjs +32 -29
- package/dist/lib/node-esm/extraction/index.mjs.map +4 -4
- package/dist/lib/node-esm/index.mjs +682 -1035
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{named-entity-recognition-W4VMRCH6.mjs → named-entity-recognition-2HDF46YX.mjs} +2 -2
- package/dist/types/src/{context/binding.d.ts → conversation/context.d.ts} +45 -3
- package/dist/types/src/conversation/context.d.ts.map +1 -0
- package/dist/types/src/conversation/conversation.d.ts +24 -21
- package/dist/types/src/conversation/conversation.d.ts.map +1 -1
- package/dist/types/src/conversation/index.d.ts +1 -0
- package/dist/types/src/conversation/index.d.ts.map +1 -1
- package/dist/types/src/diff.d.ts +15 -0
- package/dist/types/src/diff.d.ts.map +1 -0
- package/dist/types/src/diff.test.d.ts +2 -0
- package/dist/types/src/diff.test.d.ts.map +1 -0
- package/dist/types/src/errors.d.ts +46 -9
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/execution-graph.d.ts +82 -0
- package/dist/types/src/execution-graph.d.ts.map +1 -0
- package/dist/types/src/execution-graph.test.d.ts +2 -0
- package/dist/types/src/execution-graph.test.d.ts.map +1 -0
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +1 -1
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +94 -55
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction.d.ts +250 -216
- package/dist/types/src/extraction/extraction.d.ts.map +1 -1
- package/dist/types/src/functions/services.d.ts +15 -3
- package/dist/types/src/functions/services.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/session/artifact-diff.d.ts +27 -0
- package/dist/types/src/session/artifact-diff.d.ts.map +1 -0
- package/dist/types/src/session/error-handling.d.ts +4 -0
- package/dist/types/src/session/error-handling.d.ts.map +1 -0
- package/dist/types/src/session/format.d.ts +184 -0
- package/dist/types/src/session/format.d.ts.map +1 -0
- package/dist/types/src/session/format.test.d.ts +2 -0
- package/dist/types/src/session/format.test.d.ts.map +1 -0
- package/dist/types/src/session/index.d.ts +3 -0
- package/dist/types/src/session/index.d.ts.map +1 -1
- package/dist/types/src/session/observer.d.ts +22 -0
- package/dist/types/src/session/observer.d.ts.map +1 -0
- package/dist/types/src/session/session.d.ts +23 -236
- package/dist/types/src/session/session.d.ts.map +1 -1
- package/dist/types/src/session/toolkit.d.ts +14 -0
- package/dist/types/src/session/toolkit.d.ts.map +1 -0
- package/dist/types/src/session/version-pin.d.ts +2 -2
- package/dist/types/src/session/version-pin.d.ts.map +1 -1
- package/dist/types/src/templates/index.d.ts +1 -1
- package/dist/types/src/templates/system.d.ts +18 -0
- package/dist/types/src/templates/system.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +3 -0
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +34 -33
- package/src/conversation/context.ts +154 -0
- package/src/conversation/conversation.ts +76 -53
- package/src/conversation/index.ts +1 -0
- package/src/diff.test.ts +43 -0
- package/src/diff.ts +79 -0
- package/src/errors.ts +6 -1
- package/src/execution-graph.test.ts +738 -0
- package/src/execution-graph.ts +500 -0
- package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +8 -7
- package/src/extraction/extraction-llm-function/extraction-llm-function.ts +12 -10
- package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +9 -9
- package/src/extraction/extraction-ner-function/extraction-ner-function.ts +13 -6
- package/src/extraction/extraction-ner-function/named-entity-recognition.test.ts +2 -1
- package/src/extraction/extraction-ner-function/named-entity-recognition.ts +2 -0
- package/src/extraction/quotes.test.ts +3 -3
- package/src/extraction/quotes.ts +2 -2
- package/src/functions/services.ts +97 -43
- package/src/index.ts +3 -2
- package/src/session/artifact-diff.ts +41 -0
- package/src/session/error-handling.ts +40 -0
- package/src/session/format.test.ts +60 -0
- package/src/session/format.ts +139 -0
- package/src/session/index.ts +3 -0
- package/src/session/observer.ts +58 -0
- package/src/session/session.conversations.json +1 -0
- package/src/session/session.test.ts +33 -197
- package/src/session/session.ts +119 -249
- package/src/session/toolkit.ts +46 -0
- package/src/session/version-pin.ts +4 -4
- package/src/summarization/summarization.test.ts +9 -7
- package/src/templates/format.tpl +60 -0
- package/src/templates/index.ts +1 -1
- package/src/templates/system.tpl +48 -0
- package/src/templates/system.ts +41 -0
- package/src/types.ts +10 -5
- package/src/typings.d.ts +5 -0
- package/dist/lib/browser/chunk-6WRGDEMH.mjs.map +0 -7
- package/dist/lib/browser/chunk-SIXTNSGV.mjs +0 -250
- package/dist/lib/browser/chunk-SIXTNSGV.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-3UU5V4FS.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-FD5NXKDP.mjs +0 -252
- package/dist/lib/node-esm/chunk-FD5NXKDP.mjs.map +0 -7
- package/dist/types/bin/chat.d.ts +0 -2
- package/dist/types/bin/chat.d.ts.map +0 -1
- package/dist/types/src/context/binder.d.ts +0 -34
- package/dist/types/src/context/binder.d.ts.map +0 -1
- package/dist/types/src/context/binding.d.ts.map +0 -1
- package/dist/types/src/context/index.d.ts +0 -3
- package/dist/types/src/context/index.d.ts.map +0 -1
- package/dist/types/src/research/exa.d.ts +0 -12
- package/dist/types/src/research/exa.d.ts.map +0 -1
- package/dist/types/src/research/graph.d.ts +0 -51
- package/dist/types/src/research/graph.d.ts.map +0 -1
- package/dist/types/src/research/graph.test.d.ts +0 -2
- package/dist/types/src/research/graph.test.d.ts.map +0 -1
- package/dist/types/src/research/index.d.ts +0 -4
- package/dist/types/src/research/index.d.ts.map +0 -1
- package/dist/types/src/research/research.d.ts +0 -10
- package/dist/types/src/research/research.d.ts.map +0 -1
- package/dist/types/src/research/research.test.d.ts +0 -2
- package/dist/types/src/research/research.test.d.ts.map +0 -1
- package/dist/types/src/session/version-pin.test.d.ts +0 -2
- package/dist/types/src/session/version-pin.test.d.ts.map +0 -1
- package/dist/types/src/templates/prompt.d.ts +0 -30
- package/dist/types/src/templates/prompt.d.ts.map +0 -1
- package/dist/types/src/testing/exa-search-1748337321991.d.ts +0 -38
- package/dist/types/src/testing/exa-search-1748337321991.d.ts.map +0 -1
- package/dist/types/src/testing/exa-search-1748337331526.d.ts +0 -37
- package/dist/types/src/testing/exa-search-1748337331526.d.ts.map +0 -1
- package/dist/types/src/testing/exa-search-1748337344119.d.ts +0 -58
- package/dist/types/src/testing/exa-search-1748337344119.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -3
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/src/context/binder.ts +0 -103
- package/src/context/binding.ts +0 -32
- package/src/context/index.ts +0 -6
- package/src/research/exa.ts +0 -97
- package/src/research/graph.test.ts +0 -64
- package/src/research/graph.ts +0 -369
- package/src/research/index.ts +0 -7
- package/src/research/instructions-research.tpl +0 -98
- package/src/research/research.test.ts +0 -190
- package/src/research/research.ts +0 -87
- package/src/session/__snapshots__/session.test.ts.snap +0 -32
- package/src/session/version-pin.test.ts +0 -7
- package/src/templates/assistant.tpl +0 -111
- package/src/templates/prompt.ts +0 -45
- package/src/testing/exa-search-1748337321991.ts +0 -131
- package/src/testing/exa-search-1748337331526.ts +0 -144
- package/src/testing/exa-search-1748337344119.ts +0 -133
- package/src/testing/index.ts +0 -11
- package/src/typedefs.d.ts +0 -8
- /package/dist/lib/browser/{named-entity-recognition-O46UTCLR.mjs.map → named-entity-recognition-QPYHML66.mjs.map} +0 -0
- /package/dist/lib/node-esm/{named-entity-recognition-W4VMRCH6.mjs.map → named-entity-recognition-2HDF46YX.mjs.map} +0 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { AgentStatus } from '@dxos/ai';
|
|
6
|
+
import { Obj, type Ref } from '@dxos/echo';
|
|
7
|
+
import { MESSAGE_PROPERTY_TOOL_CALL_ID } from '@dxos/functions';
|
|
8
|
+
import { type ObjectId } from '@dxos/keys';
|
|
9
|
+
import { LogLevel } from '@dxos/log';
|
|
10
|
+
import { ContentBlock, DataType } from '@dxos/schema';
|
|
11
|
+
import { isNonNullable } from '@dxos/util';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Mercurial-style Commit.
|
|
15
|
+
*/
|
|
16
|
+
// TODO(wittjosiah): Reconcile with @dxos/react-ui-components.
|
|
17
|
+
export type Commit = {
|
|
18
|
+
id: string;
|
|
19
|
+
parents: string[];
|
|
20
|
+
branch: string;
|
|
21
|
+
icon?: string;
|
|
22
|
+
level?: LogLevel;
|
|
23
|
+
message: string;
|
|
24
|
+
timestamp?: Date;
|
|
25
|
+
tags?: string[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// TODO(burdon): Add colors?
|
|
29
|
+
enum IconType {
|
|
30
|
+
// General status.
|
|
31
|
+
SUCCESS = 'ph--check-circle--regular',
|
|
32
|
+
WARNING = 'ph--warning-circle--regular',
|
|
33
|
+
ERROR = 'ph--x-circle--regular',
|
|
34
|
+
FLAG = 'ph--flag--regular',
|
|
35
|
+
ROCKET = 'ph--rocket--regular',
|
|
36
|
+
TIMER = 'ph--timer--regular',
|
|
37
|
+
|
|
38
|
+
// Interactions.
|
|
39
|
+
USER = 'ph--user--regular',
|
|
40
|
+
USER_INTERACTION = 'ph--user-sound--regular',
|
|
41
|
+
AGENT = 'ph--robot--regular',
|
|
42
|
+
THINKING = 'ph--brain--regular',
|
|
43
|
+
LINK = 'ph--link--regular',
|
|
44
|
+
TOOL = 'ph--wrench--regular',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Models the execution graph based on a stream of events.
|
|
49
|
+
*/
|
|
50
|
+
export class ExecutionGraph {
|
|
51
|
+
// TODO(dmaretskyi): Evolve the internal state to support chats, functions, circuit workflows, etc.
|
|
52
|
+
private _commits: Commit[] = [];
|
|
53
|
+
private _branchNames = new Set<string>();
|
|
54
|
+
private _lastBlockId: string | undefined;
|
|
55
|
+
|
|
56
|
+
// Tool call tracking.
|
|
57
|
+
private _toolCallCommitIds = new Map<string, string>(); // toolCallId -> commitId
|
|
58
|
+
private _lastCommitByBranch = new Map<string, string>(); // branch -> last commitId
|
|
59
|
+
private _pendingToolResults = new Map<string, string>(); // toolCallId -> toolResultCommitId
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Adds events to the graph.
|
|
63
|
+
*/
|
|
64
|
+
addEvents(events: Obj.Any[]) {
|
|
65
|
+
const sortedEvents = this.sortObjectsByCreated(events);
|
|
66
|
+
for (const event of sortedEvents) {
|
|
67
|
+
if (Obj.instanceOf(DataType.Message, event)) {
|
|
68
|
+
this._processMessage(event);
|
|
69
|
+
} else if (Obj.instanceOf(AgentStatus, event)) {
|
|
70
|
+
this._processAgentStatus(event);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Processes a message event and creates commits for its blocks.
|
|
77
|
+
*/
|
|
78
|
+
private _processMessage(message: DataType.Message) {
|
|
79
|
+
const messageCommits = messageToCommits(
|
|
80
|
+
message,
|
|
81
|
+
this._lastBlockId,
|
|
82
|
+
this._toolCallCommitIds,
|
|
83
|
+
this._lastCommitByBranch,
|
|
84
|
+
);
|
|
85
|
+
this._commits.push(...messageCommits);
|
|
86
|
+
|
|
87
|
+
// Track branches.
|
|
88
|
+
messageCommits.forEach((commit) => this._branchNames.add(commit.branch));
|
|
89
|
+
|
|
90
|
+
// Track tool calls and update pending tool results.
|
|
91
|
+
this._trackToolCalls(messageCommits);
|
|
92
|
+
|
|
93
|
+
// Update last block ID for sequential chaining.
|
|
94
|
+
if (messageCommits.length > 0) {
|
|
95
|
+
this._lastBlockId = messageCommits[messageCommits.length - 1].id;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Processes an AgentStatus event.
|
|
101
|
+
*/
|
|
102
|
+
private _processAgentStatus(event: AgentStatus) {
|
|
103
|
+
const branch = this._getToolCallBranch(event.parentMessage, event.toolCallId);
|
|
104
|
+
this._branchNames.add(branch);
|
|
105
|
+
|
|
106
|
+
const agentStatusCommit: Commit = {
|
|
107
|
+
id: event.id,
|
|
108
|
+
branch,
|
|
109
|
+
message: event.message,
|
|
110
|
+
icon: IconType.FLAG,
|
|
111
|
+
parents: this._getAgentStatusParents(event),
|
|
112
|
+
timestamp: new Date(event.created),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
this._commits.push(agentStatusCommit);
|
|
116
|
+
|
|
117
|
+
// Update last block ID for sequential chaining.
|
|
118
|
+
this._lastBlockId = event.id;
|
|
119
|
+
|
|
120
|
+
// Update pending tool results to point to this AgentStatus.
|
|
121
|
+
if (event.toolCallId) {
|
|
122
|
+
this._lastCommitByBranch.set(branch, event.id);
|
|
123
|
+
this._updatePendingToolResults(event.toolCallId, event.id);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Stable sort: objects with 'created' field are sorted by it, others remain in place.
|
|
129
|
+
*/
|
|
130
|
+
private sortObjectsByCreated(objects: any[]) {
|
|
131
|
+
const sortedObjects = objects.slice();
|
|
132
|
+
// Find indices of objects with 'created'
|
|
133
|
+
const createdObjects: { idx: number; obj: any }[] = [];
|
|
134
|
+
sortedObjects.forEach((obj, idx) => {
|
|
135
|
+
if (obj && typeof obj.created === 'string') {
|
|
136
|
+
createdObjects.push({ idx, obj });
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
// Sort only the objects with 'created' by their date, stable
|
|
140
|
+
createdObjects.sort((a, b) => {
|
|
141
|
+
const aDate = new Date(a.obj.created).getTime();
|
|
142
|
+
const bDate = new Date(b.obj.created).getTime();
|
|
143
|
+
return aDate - bDate;
|
|
144
|
+
});
|
|
145
|
+
// Place sorted createdObjects back into their original positions
|
|
146
|
+
let createdIdx = 0;
|
|
147
|
+
return sortedObjects.map((obj, _idx) => {
|
|
148
|
+
if (obj && typeof obj.created === 'string') {
|
|
149
|
+
return createdObjects[createdIdx++].obj;
|
|
150
|
+
}
|
|
151
|
+
return obj;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Returns the current state of the graph.
|
|
157
|
+
*/
|
|
158
|
+
getGraph(lastRequest = false): { branches: string[]; commits: Commit[] } {
|
|
159
|
+
const idx = lastRequest ? this._commits.findLastIndex((c) => c.tags?.includes('user')) : -1;
|
|
160
|
+
const commits = idx === -1 ? this._commits : this._commits.slice(idx);
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
branches: Array.from(this._branchNames),
|
|
164
|
+
commits,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Tracks tool calls and handles pending tool result updates.
|
|
170
|
+
*/
|
|
171
|
+
private _trackToolCalls(commits: Commit[]) {
|
|
172
|
+
commits.forEach((commit) => {
|
|
173
|
+
if (this._isToolCallCommit(commit)) {
|
|
174
|
+
const toolCallId = this._extractToolCallId(commit.id);
|
|
175
|
+
if (toolCallId) {
|
|
176
|
+
this._toolCallCommitIds.set(toolCallId, commit.id);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (this._isToolResultCommit(commit)) {
|
|
181
|
+
const toolCallId = this._extractToolCallId(commit.id);
|
|
182
|
+
if (toolCallId) {
|
|
183
|
+
this._pendingToolResults.set(toolCallId, commit.id);
|
|
184
|
+
this._handleLateToolResultUpdate(toolCallId, commit.id);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this._lastCommitByBranch.set(commit.branch, commit.id);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Updates pending tool results to point to an AgentStatus event.
|
|
194
|
+
*/
|
|
195
|
+
private _updatePendingToolResults(toolCallId: string, agentStatusId: string) {
|
|
196
|
+
const toolResultId = this._pendingToolResults.get(toolCallId);
|
|
197
|
+
if (!toolResultId) return;
|
|
198
|
+
|
|
199
|
+
const toolResultCommit = this._commits.find((c) => c.id === toolResultId);
|
|
200
|
+
if (!toolResultCommit) return;
|
|
201
|
+
|
|
202
|
+
const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);
|
|
203
|
+
if (!toolCallCommitId) return;
|
|
204
|
+
|
|
205
|
+
// Replace tool call commit with AgentStatus commit.
|
|
206
|
+
const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);
|
|
207
|
+
if (toolCallIndex !== -1) {
|
|
208
|
+
toolResultCommit.parents[toolCallIndex] = agentStatusId;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this._pendingToolResults.delete(toolCallId);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Handles late updates when AgentStatus is processed before tool result.
|
|
216
|
+
*/
|
|
217
|
+
private _handleLateToolResultUpdate(toolCallId: string, toolResultId: string) {
|
|
218
|
+
const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);
|
|
219
|
+
if (!toolCallCommitId) return;
|
|
220
|
+
|
|
221
|
+
const messageId = toolCallCommitId.split('_toolCall_')[0];
|
|
222
|
+
const toolCallBranch = `${messageId}_${toolCallId}`;
|
|
223
|
+
const agentStatusCommitId = this._lastCommitByBranch.get(toolCallBranch);
|
|
224
|
+
|
|
225
|
+
if (agentStatusCommitId && agentStatusCommitId !== toolCallCommitId) {
|
|
226
|
+
const toolResultCommit = this._commits.find((c) => c.id === toolResultId);
|
|
227
|
+
if (toolResultCommit) {
|
|
228
|
+
const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);
|
|
229
|
+
if (toolCallIndex !== -1) {
|
|
230
|
+
toolResultCommit.parents[toolCallIndex] = agentStatusCommitId;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
this._pendingToolResults.delete(toolCallId);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Gets the branch name for a tool call.
|
|
239
|
+
*/
|
|
240
|
+
private _getToolCallBranch(parentMessage?: ObjectId, toolCallId?: string): string {
|
|
241
|
+
if (toolCallId) {
|
|
242
|
+
return toolCallId;
|
|
243
|
+
}
|
|
244
|
+
return parentMessage || 'main';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Gets the parents for an AgentStatus commit.
|
|
249
|
+
*/
|
|
250
|
+
private _getAgentStatusParents(event: AgentStatus): string[] {
|
|
251
|
+
if (event.parentMessage && event.toolCallId) {
|
|
252
|
+
return [getToolCallId(event.parentMessage, event.toolCallId)];
|
|
253
|
+
}
|
|
254
|
+
return [];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Checks if a commit is a tool call commit.
|
|
259
|
+
*/
|
|
260
|
+
private _isToolCallCommit(commit: Commit): boolean {
|
|
261
|
+
return commit.message.includes('Calling tool');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Checks if a commit is a tool result commit.
|
|
266
|
+
*/
|
|
267
|
+
private _isToolResultCommit(commit: Commit): boolean {
|
|
268
|
+
return commit.message.includes('Tool call succeeded') || commit.message.includes('Tool error:');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Extracts tool call ID from a commit ID.
|
|
273
|
+
*/
|
|
274
|
+
private _extractToolCallId(commitId: string): string | undefined {
|
|
275
|
+
const toolCallMatch = commitId.match(/_toolCall_(.+)$/);
|
|
276
|
+
const toolResultMatch = commitId.match(/_toolResult_(.+)$/);
|
|
277
|
+
return toolCallMatch?.[1] || toolResultMatch?.[1];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// TODO(burdon): Pass in AiToolProvider.
|
|
282
|
+
/**
|
|
283
|
+
* Creates commits for all blocks in a message.
|
|
284
|
+
*/
|
|
285
|
+
const messageToCommits = (
|
|
286
|
+
message: DataType.Message,
|
|
287
|
+
lastBlockId?: string,
|
|
288
|
+
toolCallIds?: Map<string, string>,
|
|
289
|
+
lastCommitByBranch?: Map<string, string>,
|
|
290
|
+
): Commit[] => {
|
|
291
|
+
let previousBlockId: string | undefined = lastBlockId;
|
|
292
|
+
|
|
293
|
+
return message.blocks
|
|
294
|
+
.map((block, idx) => {
|
|
295
|
+
const branch = getMessageBranch(message);
|
|
296
|
+
const parents = getBlockParents(block, previousBlockId, message, toolCallIds, lastCommitByBranch);
|
|
297
|
+
|
|
298
|
+
const commit = createBlockCommit(block, message, branch, parents, idx);
|
|
299
|
+
if (commit) {
|
|
300
|
+
previousBlockId = commit.id;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return commit;
|
|
304
|
+
})
|
|
305
|
+
.filter(isNonNullable);
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Determines the parent commits for a block based on its type and context.
|
|
310
|
+
*/
|
|
311
|
+
const getBlockParents = (
|
|
312
|
+
block: ContentBlock.Any,
|
|
313
|
+
previousBlockId: string | undefined,
|
|
314
|
+
message: DataType.Message,
|
|
315
|
+
toolCallIds?: Map<string, string>,
|
|
316
|
+
lastCommitByBranch?: Map<string, string>,
|
|
317
|
+
): string[] => {
|
|
318
|
+
const parents: string[] = [];
|
|
319
|
+
|
|
320
|
+
if (block._tag === 'toolResult') {
|
|
321
|
+
// Tool results have two parents: previous block and last block from tool call branch
|
|
322
|
+
if (previousBlockId) {
|
|
323
|
+
parents.push(previousBlockId);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const toolCallCommitId = toolCallIds?.get(block.toolCallId);
|
|
327
|
+
if (toolCallCommitId) {
|
|
328
|
+
const toolCallBranch = getToolCallBranchFromCommitId(toolCallCommitId, block.toolCallId);
|
|
329
|
+
const lastBlockFromToolCallBranch = lastCommitByBranch?.get(toolCallBranch);
|
|
330
|
+
|
|
331
|
+
if (lastBlockFromToolCallBranch) {
|
|
332
|
+
parents.push(lastBlockFromToolCallBranch);
|
|
333
|
+
} else {
|
|
334
|
+
// Fallback to tool call commit if no AgentStatus exists yet.
|
|
335
|
+
parents.push(toolCallCommitId);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
// For other blocks, use previous block as parent (or tool call parent for first block).
|
|
340
|
+
if (previousBlockId) {
|
|
341
|
+
parents.push(previousBlockId);
|
|
342
|
+
} else {
|
|
343
|
+
const toolCallParent = getParentId(message);
|
|
344
|
+
if (toolCallParent) {
|
|
345
|
+
parents.push(toolCallParent);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return parents;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Creates a commit for a specific block.
|
|
355
|
+
*/
|
|
356
|
+
const createBlockCommit = (
|
|
357
|
+
block: ContentBlock.Any,
|
|
358
|
+
message: DataType.Message,
|
|
359
|
+
branch: string,
|
|
360
|
+
parents: string[],
|
|
361
|
+
idx: number,
|
|
362
|
+
): Commit | null => {
|
|
363
|
+
const timestamp = new Date(message.created);
|
|
364
|
+
switch (block._tag) {
|
|
365
|
+
case 'text':
|
|
366
|
+
if (!block.text.trim().length) return null;
|
|
367
|
+
return {
|
|
368
|
+
id: getGenericBlockId(message.id, idx),
|
|
369
|
+
branch,
|
|
370
|
+
parents,
|
|
371
|
+
timestamp,
|
|
372
|
+
...(message.sender.role === 'user'
|
|
373
|
+
? {
|
|
374
|
+
icon: IconType.USER,
|
|
375
|
+
tags: ['user'],
|
|
376
|
+
message: 'Processing request...',
|
|
377
|
+
}
|
|
378
|
+
: {
|
|
379
|
+
icon: IconType.AGENT,
|
|
380
|
+
message: `Response (${block.text.split(' ').length} words)`,
|
|
381
|
+
}),
|
|
382
|
+
} satisfies Commit;
|
|
383
|
+
|
|
384
|
+
case 'toolCall':
|
|
385
|
+
return {
|
|
386
|
+
id: getToolCallId(message.id, block.toolCallId),
|
|
387
|
+
branch,
|
|
388
|
+
parents,
|
|
389
|
+
timestamp,
|
|
390
|
+
icon: IconType.TOOL,
|
|
391
|
+
level: LogLevel.INFO,
|
|
392
|
+
message: `Calling tool (${block.name})`,
|
|
393
|
+
} satisfies Commit;
|
|
394
|
+
|
|
395
|
+
case 'toolResult':
|
|
396
|
+
return {
|
|
397
|
+
id: getToolResultId(message.id, block.toolCallId),
|
|
398
|
+
branch,
|
|
399
|
+
parents,
|
|
400
|
+
timestamp,
|
|
401
|
+
icon: block.error ? IconType.ERROR : IconType.SUCCESS,
|
|
402
|
+
level: block.error ? LogLevel.ERROR : LogLevel.INFO,
|
|
403
|
+
message: block.error ? 'Tool error: ' + block.error : 'Tool call succeeded',
|
|
404
|
+
} satisfies Commit;
|
|
405
|
+
|
|
406
|
+
case 'summary':
|
|
407
|
+
return {
|
|
408
|
+
id: getGenericBlockId(message.id, idx),
|
|
409
|
+
branch,
|
|
410
|
+
parents,
|
|
411
|
+
timestamp,
|
|
412
|
+
icon: IconType.ROCKET,
|
|
413
|
+
level: LogLevel.INFO,
|
|
414
|
+
message: ContentBlock.createSummaryMessage(block),
|
|
415
|
+
} satisfies Commit;
|
|
416
|
+
|
|
417
|
+
case 'status':
|
|
418
|
+
return {
|
|
419
|
+
id: getGenericBlockId(message.id, idx),
|
|
420
|
+
branch,
|
|
421
|
+
parents,
|
|
422
|
+
timestamp,
|
|
423
|
+
message: block.statusText,
|
|
424
|
+
level: LogLevel.INFO,
|
|
425
|
+
icon: IconType.FLAG,
|
|
426
|
+
} satisfies Commit;
|
|
427
|
+
|
|
428
|
+
case 'reasoning':
|
|
429
|
+
return {
|
|
430
|
+
id: getGenericBlockId(message.id, idx),
|
|
431
|
+
branch,
|
|
432
|
+
parents,
|
|
433
|
+
timestamp,
|
|
434
|
+
message: block.reasoningText ?? 'Thinking...',
|
|
435
|
+
icon: IconType.THINKING,
|
|
436
|
+
} satisfies Commit;
|
|
437
|
+
|
|
438
|
+
case 'reference':
|
|
439
|
+
return {
|
|
440
|
+
id: getGenericBlockId(message.id, idx),
|
|
441
|
+
branch,
|
|
442
|
+
parents,
|
|
443
|
+
timestamp,
|
|
444
|
+
icon: IconType.LINK,
|
|
445
|
+
message: stringifyRef(block.reference),
|
|
446
|
+
} satisfies Commit;
|
|
447
|
+
|
|
448
|
+
default:
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Gets the tool call branch name from a tool call commit ID.
|
|
455
|
+
*/
|
|
456
|
+
const getToolCallBranchFromCommitId = (toolCallCommitId: string, toolCallId: string): string => {
|
|
457
|
+
const messageId = toolCallCommitId.split('_toolCall_')[0];
|
|
458
|
+
return `${messageId}_${toolCallId}`;
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
const getToolCallId = (messageId: ObjectId, toolCallId: string) => `${messageId}_toolCall_${toolCallId}`;
|
|
462
|
+
const getToolResultId = (messageId: ObjectId, toolCallId: string) => `${messageId}_toolResult_${toolCallId}`;
|
|
463
|
+
const getGenericBlockId = (messageId: ObjectId, idx: number) => `${messageId}_block_${idx}`;
|
|
464
|
+
|
|
465
|
+
const getBranchName = (options: { parentMessage?: ObjectId; toolCallId?: string }) => {
|
|
466
|
+
if (options.toolCallId) {
|
|
467
|
+
return options.toolCallId;
|
|
468
|
+
} else if (options.parentMessage) {
|
|
469
|
+
return options.parentMessage;
|
|
470
|
+
} else {
|
|
471
|
+
return 'main';
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
const getMessageBranch = (message: DataType.Message) => {
|
|
476
|
+
return getBranchName({
|
|
477
|
+
parentMessage: message.parentMessage,
|
|
478
|
+
toolCallId: message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID],
|
|
479
|
+
});
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const getParentId = (message: DataType.Message) => {
|
|
483
|
+
if (message.parentMessage && message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID]) {
|
|
484
|
+
return getToolCallId(message.parentMessage, message.properties[MESSAGE_PROPERTY_TOOL_CALL_ID]);
|
|
485
|
+
} else {
|
|
486
|
+
return undefined;
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
const stringifyRef = (ref: Ref.Any) => {
|
|
491
|
+
if (ref.target) {
|
|
492
|
+
return stringifyObject(ref.target);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return ref.dxn.asEchoDXN()?.echoId ?? ref.dxn.asQueueDXN()?.objectId ?? '';
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const stringifyObject = (obj: Obj.Any) => {
|
|
499
|
+
return Obj.getLabel(obj) ?? Obj.getTypename(obj) ?? obj.id;
|
|
500
|
+
};
|
|
@@ -11,7 +11,8 @@ import { createTestData } from '@dxos/schema/testing';
|
|
|
11
11
|
import { range } from '@dxos/util';
|
|
12
12
|
|
|
13
13
|
import { processTranscriptMessage } from '../extraction';
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
import { extractionAnthropicFunction } from './extraction-llm-function';
|
|
15
16
|
|
|
16
17
|
// TODO(burdon): Rewrite test.
|
|
17
18
|
describe.skip('LLM EntityExtraction', () => {
|
|
@@ -32,7 +33,7 @@ describe.skip('LLM EntityExtraction', () => {
|
|
|
32
33
|
message,
|
|
33
34
|
objects: [...documents, ...Object.values(contacts)],
|
|
34
35
|
},
|
|
35
|
-
function:
|
|
36
|
+
function: extractionAnthropicFunction,
|
|
36
37
|
executor,
|
|
37
38
|
});
|
|
38
39
|
log.info('output', enhancedMessage);
|
|
@@ -41,8 +42,8 @@ describe.skip('LLM EntityExtraction', () => {
|
|
|
41
42
|
|
|
42
43
|
test('computational irreducibility', async () => {
|
|
43
44
|
const { transcriptWoflram, documents, contacts } = createTestData();
|
|
44
|
-
|
|
45
45
|
log.info('context', { documents, contacts });
|
|
46
|
+
|
|
46
47
|
const message = transcriptWoflram[0];
|
|
47
48
|
log.info('input', message);
|
|
48
49
|
|
|
@@ -53,9 +54,10 @@ describe.skip('LLM EntityExtraction', () => {
|
|
|
53
54
|
message,
|
|
54
55
|
objects: [...documents, ...Object.values(contacts)],
|
|
55
56
|
},
|
|
56
|
-
function:
|
|
57
|
+
function: extractionAnthropicFunction,
|
|
57
58
|
executor,
|
|
58
59
|
});
|
|
60
|
+
|
|
59
61
|
log.info('output', { message: enhancedMessage.blocks[0], timeElapsed });
|
|
60
62
|
}),
|
|
61
63
|
);
|
|
@@ -63,20 +65,19 @@ describe.skip('LLM EntityExtraction', () => {
|
|
|
63
65
|
|
|
64
66
|
test('org and document linking', async () => {
|
|
65
67
|
const { transcriptJosiah, documents, contacts, organizations } = createTestData();
|
|
66
|
-
|
|
67
68
|
log.info('context', { contacts, organizations, documents });
|
|
68
69
|
|
|
69
70
|
for (const message of transcriptJosiah) {
|
|
70
71
|
log.info('input', message);
|
|
71
|
-
|
|
72
72
|
const { message: enhancedMessage } = await processTranscriptMessage({
|
|
73
73
|
input: {
|
|
74
74
|
message,
|
|
75
75
|
objects: [...documents, ...Object.values(contacts), ...Object.values(organizations)],
|
|
76
76
|
},
|
|
77
|
-
function:
|
|
77
|
+
function: extractionAnthropicFunction,
|
|
78
78
|
executor,
|
|
79
79
|
});
|
|
80
|
+
|
|
80
81
|
log.info('output', enhancedMessage);
|
|
81
82
|
}
|
|
82
83
|
});
|
|
@@ -2,27 +2,30 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// ISSUE(burdon): defineFunction
|
|
6
6
|
// @ts-nocheck
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { AiService } from '@dxos/ai';
|
|
9
9
|
import { Obj } from '@dxos/echo';
|
|
10
10
|
import { create } from '@dxos/echo-schema';
|
|
11
|
-
import {
|
|
11
|
+
import { type FunctionDefinition, defineFunction } from '@dxos/functions';
|
|
12
12
|
import { type ContentBlock, DataType } from '@dxos/schema';
|
|
13
13
|
|
|
14
|
-
import PROMPT from './instructions.tpl?raw';
|
|
15
14
|
import { AiSession } from '../../session';
|
|
16
15
|
import { ExtractionInput, ExtractionOutput } from '../extraction';
|
|
17
|
-
import {
|
|
16
|
+
import { ReferencedQuotes, insertReferences } from '../quotes';
|
|
17
|
+
|
|
18
|
+
import PROMPT from './instructions.tpl?raw';
|
|
18
19
|
|
|
19
|
-
export const
|
|
20
|
+
export const extractionAnthropicFunction: FunctionDefinition<ExtractionInput, ExtractionOutput> = defineFunction({
|
|
21
|
+
key: 'dxos.org/function/extraction/extract-entities',
|
|
22
|
+
name: 'Extract Entities',
|
|
20
23
|
description: 'Extract entities from the transcript message and add them to the message.',
|
|
21
24
|
inputSchema: ExtractionInput,
|
|
22
25
|
outputSchema: ExtractionOutput,
|
|
23
26
|
handler: async ({ data: { message, objects }, context }) => {
|
|
24
27
|
const startTime = performance.now();
|
|
25
|
-
const ai = context.getService(AiService);
|
|
28
|
+
const ai = context.getService(AiService.AiService);
|
|
26
29
|
const session = new AiSession({ operationModel: 'configured' });
|
|
27
30
|
const result = await session.runStructured(ReferencedQuotes, {
|
|
28
31
|
generationOptions: {
|
|
@@ -49,13 +52,13 @@ export const extractionAnthropicFn: FunctionDefinition<ExtractionInput, Extracti
|
|
|
49
52
|
artifacts: [],
|
|
50
53
|
prompt: '',
|
|
51
54
|
tools: [],
|
|
52
|
-
toolResolver: new ToolRegistry([]),
|
|
53
55
|
} as any); // TODO(burdon): Rewrite test.
|
|
54
56
|
|
|
55
57
|
return {
|
|
58
|
+
timeElapsed: performance.now() - startTime,
|
|
56
59
|
message: create(DataType.Message, {
|
|
57
60
|
...message,
|
|
58
|
-
blocks: message.blocks.map((block
|
|
61
|
+
blocks: message.blocks.map((block) =>
|
|
59
62
|
block._tag !== 'transcript'
|
|
60
63
|
? block
|
|
61
64
|
: {
|
|
@@ -64,7 +67,6 @@ export const extractionAnthropicFn: FunctionDefinition<ExtractionInput, Extracti
|
|
|
64
67
|
},
|
|
65
68
|
),
|
|
66
69
|
}),
|
|
67
|
-
timeElapsed: performance.now() - startTime,
|
|
68
70
|
};
|
|
69
71
|
},
|
|
70
72
|
});
|
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { describe, test
|
|
5
|
+
import { beforeAll, describe, test } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { type EchoDatabase } from '@dxos/echo-db';
|
|
8
8
|
import { EchoTestBuilder } from '@dxos/echo-db/testing';
|
|
9
9
|
import { FunctionExecutor, ServiceContainer } from '@dxos/functions';
|
|
10
10
|
import { log } from '@dxos/log';
|
|
11
11
|
import { DataType } from '@dxos/schema';
|
|
12
|
-
import {
|
|
12
|
+
import { Testing, createTestData } from '@dxos/schema/testing';
|
|
13
13
|
import { range } from '@dxos/util';
|
|
14
14
|
|
|
15
|
-
import { extractionNerFn } from './extraction-ner-function';
|
|
16
15
|
import { processTranscriptMessage } from '../extraction';
|
|
17
16
|
|
|
17
|
+
import { extractionNerFunction } from './extraction-ner-function';
|
|
18
|
+
|
|
18
19
|
describe.skip('NER EntityExtraction', () => {
|
|
19
20
|
let builder: EchoTestBuilder;
|
|
20
21
|
let db: EchoDatabase;
|
|
@@ -27,6 +28,7 @@ describe.skip('NER EntityExtraction', () => {
|
|
|
27
28
|
contacts: Record<string, DataType.Person>;
|
|
28
29
|
organizations: Record<string, DataType.Organization>;
|
|
29
30
|
};
|
|
31
|
+
|
|
30
32
|
const TYPES = [DataType.Organization, DataType.Person, Testing.Contact, Testing.DocumentType];
|
|
31
33
|
|
|
32
34
|
beforeAll(async () => {
|
|
@@ -44,8 +46,8 @@ describe.skip('NER EntityExtraction', () => {
|
|
|
44
46
|
contacts: Object.fromEntries(Object.entries(data.contacts).map(([key, value]) => [key, db.add(value)])),
|
|
45
47
|
organizations: Object.fromEntries(Object.entries(data.organizations).map(([key, value]) => [key, db.add(value)])),
|
|
46
48
|
};
|
|
47
|
-
await db.flush();
|
|
48
49
|
|
|
50
|
+
await db.flush();
|
|
49
51
|
executor = new FunctionExecutor(
|
|
50
52
|
new ServiceContainer().setServices({
|
|
51
53
|
database: { db },
|
|
@@ -64,7 +66,7 @@ describe.skip('NER EntityExtraction', () => {
|
|
|
64
66
|
message,
|
|
65
67
|
objects: [...documents, ...Object.values(contacts)],
|
|
66
68
|
},
|
|
67
|
-
function:
|
|
69
|
+
function: extractionNerFunction,
|
|
68
70
|
executor,
|
|
69
71
|
});
|
|
70
72
|
log.info('output', enhancedMessage);
|
|
@@ -73,7 +75,6 @@ describe.skip('NER EntityExtraction', () => {
|
|
|
73
75
|
|
|
74
76
|
test('computational irreducibility', async () => {
|
|
75
77
|
const { transcriptWoflram, documents, contacts } = testData;
|
|
76
|
-
|
|
77
78
|
log.info('context', { documents, contacts });
|
|
78
79
|
const message = transcriptWoflram[0];
|
|
79
80
|
log.info('input', message);
|
|
@@ -85,7 +86,7 @@ describe.skip('NER EntityExtraction', () => {
|
|
|
85
86
|
message,
|
|
86
87
|
objects: [...documents, ...Object.values(contacts)],
|
|
87
88
|
},
|
|
88
|
-
function:
|
|
89
|
+
function: extractionNerFunction,
|
|
89
90
|
executor,
|
|
90
91
|
});
|
|
91
92
|
log.info('output', { message: enhancedMessage.blocks[0], timeElapsed });
|
|
@@ -95,7 +96,6 @@ describe.skip('NER EntityExtraction', () => {
|
|
|
95
96
|
|
|
96
97
|
test('org and document linking', async () => {
|
|
97
98
|
const { transcriptJosiah, documents, contacts, organizations } = testData;
|
|
98
|
-
|
|
99
99
|
log.info('context', { contacts, organizations, documents });
|
|
100
100
|
|
|
101
101
|
for (const message of transcriptJosiah) {
|
|
@@ -106,7 +106,7 @@ describe.skip('NER EntityExtraction', () => {
|
|
|
106
106
|
message,
|
|
107
107
|
objects: [...documents, ...Object.values(contacts), ...Object.values(organizations)],
|
|
108
108
|
},
|
|
109
|
-
function:
|
|
109
|
+
function: extractionNerFunction,
|
|
110
110
|
executor,
|
|
111
111
|
});
|
|
112
112
|
log.info('output', enhancedMessage);
|