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