conversationalist 0.0.2 → 0.0.3
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 +418 -287
- package/dist/context.d.ts +22 -19
- package/dist/context.d.ts.map +1 -1
- package/dist/conversation.d.ts.map +1 -1
- package/dist/environment.d.ts +13 -1
- package/dist/environment.d.ts.map +1 -1
- package/dist/history.d.ts +165 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +461 -141
- package/dist/index.js.map +9 -8
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/pii-redaction.d.ts +36 -0
- package/dist/plugins/pii-redaction.d.ts.map +1 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/with-conversation.d.ts +3 -3
- package/dist/with-conversation.d.ts.map +1 -1
- package/package.json +8 -1
package/dist/index.js
CHANGED
|
@@ -95,24 +95,103 @@ var conversationShape = {
|
|
|
95
95
|
updatedAt: z.string()
|
|
96
96
|
};
|
|
97
97
|
var conversationSchema = z.object(conversationShape);
|
|
98
|
+
// src/utilities.ts
|
|
99
|
+
function pairToolCallsWithResults(messages) {
|
|
100
|
+
const pairs = [];
|
|
101
|
+
const resultsMap = new Map;
|
|
102
|
+
for (const msg of messages) {
|
|
103
|
+
if (msg.toolResult) {
|
|
104
|
+
resultsMap.set(msg.toolResult.callId, msg.toolResult);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const msg of messages) {
|
|
108
|
+
if (msg.toolCall) {
|
|
109
|
+
pairs.push({
|
|
110
|
+
call: msg.toolCall,
|
|
111
|
+
result: resultsMap.get(msg.toolCall.id)
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return pairs;
|
|
116
|
+
}
|
|
117
|
+
function toReadonly(value) {
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
function createMessage(props) {
|
|
121
|
+
const content = Array.isArray(props.content) ? toReadonly([...props.content]) : props.content;
|
|
122
|
+
const message = {
|
|
123
|
+
id: props.id,
|
|
124
|
+
role: props.role,
|
|
125
|
+
content,
|
|
126
|
+
position: props.position,
|
|
127
|
+
createdAt: props.createdAt,
|
|
128
|
+
metadata: toReadonly({ ...props.metadata }),
|
|
129
|
+
hidden: props.hidden,
|
|
130
|
+
toolCall: props.toolCall ? toReadonly({ ...props.toolCall }) : undefined,
|
|
131
|
+
toolResult: props.toolResult ? toReadonly({ ...props.toolResult }) : undefined,
|
|
132
|
+
tokenUsage: props.tokenUsage ? toReadonly({ ...props.tokenUsage }) : undefined,
|
|
133
|
+
goalCompleted: props.goalCompleted
|
|
134
|
+
};
|
|
135
|
+
return toReadonly(message);
|
|
136
|
+
}
|
|
137
|
+
function toMultiModalArray(input) {
|
|
138
|
+
if (typeof input === "string")
|
|
139
|
+
return [{ type: "text", text: input }];
|
|
140
|
+
return Array.isArray(input) ? input : [input];
|
|
141
|
+
}
|
|
142
|
+
function normalizeContent(content) {
|
|
143
|
+
if (content === undefined)
|
|
144
|
+
return;
|
|
145
|
+
if (typeof content === "string")
|
|
146
|
+
return content;
|
|
147
|
+
return Array.isArray(content) ? content : [content];
|
|
148
|
+
}
|
|
149
|
+
function messageParts(message) {
|
|
150
|
+
if (typeof message.content === "string") {
|
|
151
|
+
return message.content ? [{ type: "text", text: message.content }] : [];
|
|
152
|
+
}
|
|
153
|
+
return message.content;
|
|
154
|
+
}
|
|
155
|
+
function messageText(message, joiner = `
|
|
156
|
+
|
|
157
|
+
`) {
|
|
158
|
+
if (typeof message.content === "string")
|
|
159
|
+
return message.content;
|
|
160
|
+
return messageParts(message).filter((p) => p.type === "text").map((p) => p.type === "text" ? p.text : "").join(joiner);
|
|
161
|
+
}
|
|
162
|
+
function messageHasImages(message) {
|
|
163
|
+
return messageParts(message).some((p) => p.type === "image");
|
|
164
|
+
}
|
|
165
|
+
|
|
98
166
|
// src/environment.ts
|
|
167
|
+
function simpleTokenEstimator(message) {
|
|
168
|
+
const text = messageText(message);
|
|
169
|
+
return Math.ceil(text.length / 4);
|
|
170
|
+
}
|
|
99
171
|
var defaultConversationEnvironment = {
|
|
100
172
|
now: () => new Date().toISOString(),
|
|
101
|
-
randomId: () => crypto.randomUUID()
|
|
173
|
+
randomId: () => crypto.randomUUID(),
|
|
174
|
+
estimateTokens: simpleTokenEstimator,
|
|
175
|
+
plugins: []
|
|
102
176
|
};
|
|
103
177
|
function resolveConversationEnvironment(environment) {
|
|
104
178
|
return {
|
|
105
179
|
now: environment?.now ?? defaultConversationEnvironment.now,
|
|
106
|
-
randomId: environment?.randomId ?? defaultConversationEnvironment.randomId
|
|
180
|
+
randomId: environment?.randomId ?? defaultConversationEnvironment.randomId,
|
|
181
|
+
estimateTokens: environment?.estimateTokens ?? defaultConversationEnvironment.estimateTokens,
|
|
182
|
+
plugins: [...environment?.plugins ?? defaultConversationEnvironment.plugins]
|
|
107
183
|
};
|
|
108
184
|
}
|
|
109
185
|
function isConversationEnvironmentParameter(value) {
|
|
110
|
-
if (!value || typeof value !== "object")
|
|
186
|
+
if (!value || typeof value !== "object" || value === null)
|
|
111
187
|
return false;
|
|
112
188
|
if ("role" in value)
|
|
113
189
|
return false;
|
|
114
190
|
const candidate = value;
|
|
115
|
-
return typeof candidate
|
|
191
|
+
return typeof candidate["now"] === "function" || typeof candidate["randomId"] === "function" || typeof candidate["estimateTokens"] === "function" || Array.isArray(candidate["plugins"]) && candidate["plugins"].length > 0;
|
|
192
|
+
}
|
|
193
|
+
function withEnvironment(environment, fn) {
|
|
194
|
+
return (...args) => fn(...args, environment);
|
|
116
195
|
}
|
|
117
196
|
|
|
118
197
|
// src/errors.ts
|
|
@@ -169,74 +248,6 @@ function createValidationError(message, context, cause) {
|
|
|
169
248
|
return new ConversationalistError("error:validation", message, { context, cause });
|
|
170
249
|
}
|
|
171
250
|
|
|
172
|
-
// src/utilities.ts
|
|
173
|
-
function pairToolCallsWithResults(messages) {
|
|
174
|
-
const pairs = [];
|
|
175
|
-
const resultsMap = new Map;
|
|
176
|
-
for (const msg of messages) {
|
|
177
|
-
if (msg.toolResult) {
|
|
178
|
-
resultsMap.set(msg.toolResult.callId, msg.toolResult);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
for (const msg of messages) {
|
|
182
|
-
if (msg.toolCall) {
|
|
183
|
-
pairs.push({
|
|
184
|
-
call: msg.toolCall,
|
|
185
|
-
result: resultsMap.get(msg.toolCall.id)
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return pairs;
|
|
190
|
-
}
|
|
191
|
-
function toReadonly(value) {
|
|
192
|
-
return value;
|
|
193
|
-
}
|
|
194
|
-
function createMessage(props) {
|
|
195
|
-
const content = Array.isArray(props.content) ? toReadonly([...props.content]) : props.content;
|
|
196
|
-
const message = {
|
|
197
|
-
id: props.id,
|
|
198
|
-
role: props.role,
|
|
199
|
-
content,
|
|
200
|
-
position: props.position,
|
|
201
|
-
createdAt: props.createdAt,
|
|
202
|
-
metadata: toReadonly({ ...props.metadata }),
|
|
203
|
-
hidden: props.hidden,
|
|
204
|
-
toolCall: props.toolCall ? toReadonly({ ...props.toolCall }) : undefined,
|
|
205
|
-
toolResult: props.toolResult ? toReadonly({ ...props.toolResult }) : undefined,
|
|
206
|
-
tokenUsage: props.tokenUsage ? toReadonly({ ...props.tokenUsage }) : undefined,
|
|
207
|
-
goalCompleted: props.goalCompleted
|
|
208
|
-
};
|
|
209
|
-
return toReadonly(message);
|
|
210
|
-
}
|
|
211
|
-
function toMultiModalArray(input) {
|
|
212
|
-
if (typeof input === "string")
|
|
213
|
-
return [{ type: "text", text: input }];
|
|
214
|
-
return Array.isArray(input) ? input : [input];
|
|
215
|
-
}
|
|
216
|
-
function normalizeContent(content) {
|
|
217
|
-
if (content === undefined)
|
|
218
|
-
return;
|
|
219
|
-
if (typeof content === "string")
|
|
220
|
-
return content;
|
|
221
|
-
return Array.isArray(content) ? content : [content];
|
|
222
|
-
}
|
|
223
|
-
function messageParts(message) {
|
|
224
|
-
if (typeof message.content === "string") {
|
|
225
|
-
return message.content ? [{ type: "text", text: message.content }] : [];
|
|
226
|
-
}
|
|
227
|
-
return message.content;
|
|
228
|
-
}
|
|
229
|
-
function messageText(message, joiner = `
|
|
230
|
-
|
|
231
|
-
`) {
|
|
232
|
-
if (typeof message.content === "string")
|
|
233
|
-
return message.content;
|
|
234
|
-
return messageParts(message).filter((p) => p.type === "text").map((p) => p.type === "text" ? p.text : "").join(joiner);
|
|
235
|
-
}
|
|
236
|
-
function messageHasImages(message) {
|
|
237
|
-
return messageParts(message).some((p) => p.type === "image");
|
|
238
|
-
}
|
|
239
|
-
|
|
240
251
|
// src/conversation.ts
|
|
241
252
|
var buildToolUseIndex = (messages) => messages.reduce((index, message) => {
|
|
242
253
|
if (message.role === "tool-use" && message.toolCall) {
|
|
@@ -289,24 +300,25 @@ function appendMessages(conversation, ...args) {
|
|
|
289
300
|
const startPosition = conversation.messages.length;
|
|
290
301
|
const initialToolUses = buildToolUseIndex(conversation.messages);
|
|
291
302
|
const { messages } = inputs.reduce((state, input, index) => {
|
|
292
|
-
|
|
293
|
-
|
|
303
|
+
const processedInput = resolvedEnvironment.plugins.reduce((acc, plugin) => plugin(acc), input);
|
|
304
|
+
if (processedInput.role === "tool-result" && processedInput.toolResult) {
|
|
305
|
+
assertToolReference(state.toolUses, processedInput.toolResult.callId);
|
|
294
306
|
}
|
|
295
|
-
const normalizedContent = normalizeContent(
|
|
307
|
+
const normalizedContent = normalizeContent(processedInput.content);
|
|
296
308
|
const message = createMessage({
|
|
297
309
|
id: resolvedEnvironment.randomId(),
|
|
298
|
-
role:
|
|
310
|
+
role: processedInput.role,
|
|
299
311
|
content: normalizedContent,
|
|
300
312
|
position: startPosition + index,
|
|
301
313
|
createdAt: now,
|
|
302
|
-
metadata: { ...
|
|
303
|
-
hidden:
|
|
304
|
-
toolCall:
|
|
305
|
-
toolResult:
|
|
306
|
-
tokenUsage:
|
|
307
|
-
goalCompleted:
|
|
314
|
+
metadata: { ...processedInput.metadata ?? {} },
|
|
315
|
+
hidden: processedInput.hidden ?? false,
|
|
316
|
+
toolCall: processedInput.toolCall,
|
|
317
|
+
toolResult: processedInput.toolResult,
|
|
318
|
+
tokenUsage: processedInput.tokenUsage,
|
|
319
|
+
goalCompleted: processedInput.goalCompleted
|
|
308
320
|
});
|
|
309
|
-
const toolUses =
|
|
321
|
+
const toolUses = processedInput.role === "tool-use" && processedInput.toolCall ? registerToolUse(state.toolUses, processedInput.toolCall) : state.toolUses;
|
|
310
322
|
return {
|
|
311
323
|
toolUses,
|
|
312
324
|
messages: [...state.messages, message]
|
|
@@ -583,73 +595,57 @@ function toChatMessages(conversation) {
|
|
|
583
595
|
return result;
|
|
584
596
|
}
|
|
585
597
|
// src/context.ts
|
|
586
|
-
function
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
return filtered.slice(-count);
|
|
597
|
-
}
|
|
598
|
-
function truncateFromPosition(conversation, position, options, environment) {
|
|
599
|
-
const preserveSystem = options?.preserveSystemMessages ?? true;
|
|
600
|
-
const resolvedEnvironment = resolveConversationEnvironment(environment);
|
|
601
|
-
const now = resolvedEnvironment.now();
|
|
602
|
-
const systemMessages = preserveSystem ? conversation.messages.filter((m) => m.role === "system" && m.position < position) : [];
|
|
603
|
-
const keptMessages = conversation.messages.filter((m) => m.position >= position);
|
|
604
|
-
const allMessages = [...systemMessages, ...keptMessages];
|
|
605
|
-
const renumbered = allMessages.map((message, index) => createMessage({
|
|
606
|
-
id: message.id,
|
|
607
|
-
role: message.role,
|
|
608
|
-
content: typeof message.content === "string" ? message.content : [...message.content],
|
|
609
|
-
position: index,
|
|
610
|
-
createdAt: message.createdAt,
|
|
611
|
-
metadata: { ...message.metadata },
|
|
612
|
-
hidden: message.hidden,
|
|
613
|
-
toolCall: message.toolCall ? { ...message.toolCall } : undefined,
|
|
614
|
-
toolResult: message.toolResult ? { ...message.toolResult } : undefined,
|
|
615
|
-
tokenUsage: message.tokenUsage ? { ...message.tokenUsage } : undefined,
|
|
616
|
-
goalCompleted: message.goalCompleted
|
|
617
|
-
}));
|
|
618
|
-
return toReadonly({
|
|
619
|
-
...conversation,
|
|
620
|
-
messages: renumbered,
|
|
621
|
-
updatedAt: now
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
function estimateConversationTokens(conversation, estimateTokens) {
|
|
625
|
-
return conversation.messages.reduce((total, message) => total + estimateTokens(message), 0);
|
|
626
|
-
}
|
|
627
|
-
function simpleTokenEstimator(message) {
|
|
628
|
-
const text = messageText(message);
|
|
629
|
-
return Math.ceil(text.length / 4);
|
|
598
|
+
function estimateConversationTokens(conversation, estimateTokens, environment) {
|
|
599
|
+
let estimator = estimateTokens;
|
|
600
|
+
let env = environment;
|
|
601
|
+
if (!environment && estimateTokens && isConversationEnvironmentParameter(estimateTokens)) {
|
|
602
|
+
env = estimateTokens;
|
|
603
|
+
estimator = undefined;
|
|
604
|
+
}
|
|
605
|
+
const resolvedEnvironment = resolveConversationEnvironment(env);
|
|
606
|
+
const finalEstimator = typeof estimator === "function" ? estimator : resolvedEnvironment.estimateTokens;
|
|
607
|
+
return conversation.messages.reduce((total, message) => total + finalEstimator(message), 0);
|
|
630
608
|
}
|
|
631
|
-
function truncateToTokenLimit(conversation, maxTokens,
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
609
|
+
function truncateToTokenLimit(conversation, maxTokens, optionsOrEstimator, environment) {
|
|
610
|
+
let options = {};
|
|
611
|
+
let env = environment;
|
|
612
|
+
if (typeof optionsOrEstimator === "function") {
|
|
613
|
+
options = { estimateTokens: optionsOrEstimator };
|
|
614
|
+
} else if (optionsOrEstimator) {
|
|
615
|
+
if (!environment && isConversationEnvironmentParameter(optionsOrEstimator)) {
|
|
616
|
+
const candidate = optionsOrEstimator;
|
|
617
|
+
const hasEnvFields = !!(candidate["now"] || candidate["randomId"] || Array.isArray(candidate["plugins"]) && candidate["plugins"].length > 0);
|
|
618
|
+
if (hasEnvFields) {
|
|
619
|
+
env = optionsOrEstimator;
|
|
620
|
+
} else {
|
|
621
|
+
options = optionsOrEstimator;
|
|
622
|
+
}
|
|
623
|
+
} else {
|
|
624
|
+
options = optionsOrEstimator;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
const resolvedEnvironment = resolveConversationEnvironment(env);
|
|
628
|
+
const estimator = options.estimateTokens ?? resolvedEnvironment.estimateTokens;
|
|
629
|
+
const preserveSystem = options.preserveSystemMessages ?? true;
|
|
630
|
+
const preserveLastN = options.preserveLastN ?? 0;
|
|
631
|
+
const currentTokens = estimateConversationTokens(conversation, estimator, resolvedEnvironment);
|
|
635
632
|
if (currentTokens <= maxTokens) {
|
|
636
633
|
return conversation;
|
|
637
634
|
}
|
|
638
|
-
const resolvedEnvironment = resolveConversationEnvironment(environment);
|
|
639
635
|
const now = resolvedEnvironment.now();
|
|
640
636
|
const systemMessages = preserveSystem ? conversation.messages.filter((m) => m.role === "system") : [];
|
|
641
637
|
const nonSystemMessages = conversation.messages.filter((m) => m.role !== "system");
|
|
642
638
|
const protectedMessages = preserveLastN > 0 ? nonSystemMessages.slice(-preserveLastN) : [];
|
|
643
639
|
const removableMessages = preserveLastN > 0 ? nonSystemMessages.slice(0, -preserveLastN) : nonSystemMessages;
|
|
644
|
-
const systemTokens = systemMessages.reduce((sum, m) => sum +
|
|
645
|
-
const protectedTokens = protectedMessages.reduce((sum, m) => sum +
|
|
640
|
+
const systemTokens = systemMessages.reduce((sum, m) => sum + estimator(m), 0);
|
|
641
|
+
const protectedTokens = protectedMessages.reduce((sum, m) => sum + estimator(m), 0);
|
|
646
642
|
const availableTokens = maxTokens - systemTokens - protectedTokens;
|
|
647
643
|
if (availableTokens <= 0) {
|
|
648
644
|
const allMessages2 = [...systemMessages, ...protectedMessages];
|
|
649
645
|
const renumbered2 = allMessages2.map((message, index) => createMessage({
|
|
650
646
|
id: message.id,
|
|
651
647
|
role: message.role,
|
|
652
|
-
content:
|
|
648
|
+
content: copyContent(message.content),
|
|
653
649
|
position: index,
|
|
654
650
|
createdAt: message.createdAt,
|
|
655
651
|
metadata: { ...message.metadata },
|
|
@@ -669,7 +665,7 @@ function truncateToTokenLimit(conversation, maxTokens, estimateTokens, options,
|
|
|
669
665
|
let usedTokens = 0;
|
|
670
666
|
for (let i = removableMessages.length - 1;i >= 0; i--) {
|
|
671
667
|
const message = removableMessages[i];
|
|
672
|
-
const messageTokens =
|
|
668
|
+
const messageTokens = estimator(message);
|
|
673
669
|
if (usedTokens + messageTokens <= availableTokens) {
|
|
674
670
|
keptRemovable.unshift(message);
|
|
675
671
|
usedTokens += messageTokens;
|
|
@@ -682,7 +678,45 @@ function truncateToTokenLimit(conversation, maxTokens, estimateTokens, options,
|
|
|
682
678
|
const renumbered = allMessages.map((message, index) => createMessage({
|
|
683
679
|
id: message.id,
|
|
684
680
|
role: message.role,
|
|
685
|
-
content:
|
|
681
|
+
content: copyContent(message.content),
|
|
682
|
+
position: index,
|
|
683
|
+
createdAt: message.createdAt,
|
|
684
|
+
metadata: { ...message.metadata },
|
|
685
|
+
hidden: message.hidden,
|
|
686
|
+
toolCall: message.toolCall ? { ...message.toolCall } : undefined,
|
|
687
|
+
toolResult: message.toolResult ? { ...message.toolResult } : undefined,
|
|
688
|
+
tokenUsage: message.tokenUsage ? { ...message.tokenUsage } : undefined,
|
|
689
|
+
goalCompleted: message.goalCompleted
|
|
690
|
+
}));
|
|
691
|
+
return toReadonly({
|
|
692
|
+
...conversation,
|
|
693
|
+
messages: renumbered,
|
|
694
|
+
updatedAt: now
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
function getRecentMessages(conversation, count, options) {
|
|
698
|
+
const includeHidden = options?.includeHidden ?? false;
|
|
699
|
+
const includeSystem = options?.includeSystem ?? false;
|
|
700
|
+
const filtered = conversation.messages.filter((m) => {
|
|
701
|
+
if (!includeHidden && m.hidden)
|
|
702
|
+
return false;
|
|
703
|
+
if (!includeSystem && m.role === "system")
|
|
704
|
+
return false;
|
|
705
|
+
return true;
|
|
706
|
+
});
|
|
707
|
+
return filtered.slice(-count);
|
|
708
|
+
}
|
|
709
|
+
function truncateFromPosition(conversation, position, options, environment) {
|
|
710
|
+
const preserveSystem = options?.preserveSystemMessages ?? true;
|
|
711
|
+
const resolvedEnvironment = resolveConversationEnvironment(environment);
|
|
712
|
+
const now = resolvedEnvironment.now();
|
|
713
|
+
const systemMessages = preserveSystem ? conversation.messages.filter((m) => m.role === "system" && m.position < position) : [];
|
|
714
|
+
const keptMessages = conversation.messages.filter((m) => m.position >= position);
|
|
715
|
+
const allMessages = [...systemMessages, ...keptMessages];
|
|
716
|
+
const renumbered = allMessages.map((message, index) => createMessage({
|
|
717
|
+
id: message.id,
|
|
718
|
+
role: message.role,
|
|
719
|
+
content: copyContent(message.content),
|
|
686
720
|
position: index,
|
|
687
721
|
createdAt: message.createdAt,
|
|
688
722
|
metadata: { ...message.metadata },
|
|
@@ -875,8 +909,8 @@ function createDraft(initial) {
|
|
|
875
909
|
current = truncateFromPosition(current, position, options);
|
|
876
910
|
return draft;
|
|
877
911
|
},
|
|
878
|
-
truncateToTokenLimit: (maxTokens,
|
|
879
|
-
current = truncateToTokenLimit(current, maxTokens,
|
|
912
|
+
truncateToTokenLimit: (maxTokens, options) => {
|
|
913
|
+
current = truncateToTokenLimit(current, maxTokens, options);
|
|
880
914
|
return draft;
|
|
881
915
|
}
|
|
882
916
|
};
|
|
@@ -893,7 +927,291 @@ function withConversation(conversation, fn) {
|
|
|
893
927
|
function pipeConversation(conversation, ...fns) {
|
|
894
928
|
return fns.reduce((current, fn) => fn(current), conversation);
|
|
895
929
|
}
|
|
930
|
+
// src/history.ts
|
|
931
|
+
class ConversationHistoryEvent extends CustomEvent {
|
|
932
|
+
constructor(type, detail) {
|
|
933
|
+
super(type, { detail });
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
class ConversationHistory extends EventTarget {
|
|
938
|
+
currentNode;
|
|
939
|
+
environment;
|
|
940
|
+
constructor(initial, environment) {
|
|
941
|
+
super();
|
|
942
|
+
this.environment = resolveConversationEnvironment(environment);
|
|
943
|
+
this.currentNode = {
|
|
944
|
+
conversation: initial,
|
|
945
|
+
parent: null,
|
|
946
|
+
children: []
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
notifyChange(type) {
|
|
950
|
+
const detail = {
|
|
951
|
+
type,
|
|
952
|
+
conversation: this.current
|
|
953
|
+
};
|
|
954
|
+
this.dispatchEvent(new ConversationHistoryEvent("change", detail));
|
|
955
|
+
this.dispatchEvent(new ConversationHistoryEvent(type, detail));
|
|
956
|
+
}
|
|
957
|
+
addEventListener(type, callback, options) {
|
|
958
|
+
if (!callback)
|
|
959
|
+
return;
|
|
960
|
+
super.addEventListener(type, callback, options);
|
|
961
|
+
const unsubscribe = () => this.removeEventListener(type, callback, options);
|
|
962
|
+
return unsubscribe;
|
|
963
|
+
}
|
|
964
|
+
subscribe(run) {
|
|
965
|
+
run(this.current);
|
|
966
|
+
const handler = (event) => {
|
|
967
|
+
if (event instanceof ConversationHistoryEvent) {
|
|
968
|
+
run(event.detail.conversation);
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
const unsubscribe = this.addEventListener("change", handler);
|
|
972
|
+
return unsubscribe || (() => {});
|
|
973
|
+
}
|
|
974
|
+
getSnapshot() {
|
|
975
|
+
return this.current;
|
|
976
|
+
}
|
|
977
|
+
get current() {
|
|
978
|
+
return this.currentNode.conversation;
|
|
979
|
+
}
|
|
980
|
+
get canUndo() {
|
|
981
|
+
return this.currentNode.parent !== null;
|
|
982
|
+
}
|
|
983
|
+
get canRedo() {
|
|
984
|
+
return this.currentNode.children.length > 0;
|
|
985
|
+
}
|
|
986
|
+
get env() {
|
|
987
|
+
return this.environment;
|
|
988
|
+
}
|
|
989
|
+
get branchCount() {
|
|
990
|
+
return this.currentNode.parent ? this.currentNode.parent.children.length : 1;
|
|
991
|
+
}
|
|
992
|
+
get branchIndex() {
|
|
993
|
+
if (!this.currentNode.parent)
|
|
994
|
+
return 0;
|
|
995
|
+
return this.currentNode.parent.children.indexOf(this.currentNode);
|
|
996
|
+
}
|
|
997
|
+
get redoCount() {
|
|
998
|
+
return this.currentNode.children.length;
|
|
999
|
+
}
|
|
1000
|
+
push(next) {
|
|
1001
|
+
const newNode = {
|
|
1002
|
+
conversation: next,
|
|
1003
|
+
parent: this.currentNode,
|
|
1004
|
+
children: []
|
|
1005
|
+
};
|
|
1006
|
+
this.currentNode.children.push(newNode);
|
|
1007
|
+
this.currentNode = newNode;
|
|
1008
|
+
this.notifyChange("push");
|
|
1009
|
+
}
|
|
1010
|
+
undo() {
|
|
1011
|
+
if (this.currentNode.parent) {
|
|
1012
|
+
this.currentNode = this.currentNode.parent;
|
|
1013
|
+
this.notifyChange("undo");
|
|
1014
|
+
return this.current;
|
|
1015
|
+
}
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
redo(childIndex = 0) {
|
|
1019
|
+
const next = this.currentNode.children[childIndex];
|
|
1020
|
+
if (next) {
|
|
1021
|
+
this.currentNode = next;
|
|
1022
|
+
this.notifyChange("redo");
|
|
1023
|
+
return this.current;
|
|
1024
|
+
}
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
switchToBranch(index) {
|
|
1028
|
+
if (this.currentNode.parent) {
|
|
1029
|
+
const target = this.currentNode.parent.children[index];
|
|
1030
|
+
if (target) {
|
|
1031
|
+
this.currentNode = target;
|
|
1032
|
+
this.notifyChange("switch");
|
|
1033
|
+
return this.current;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
getPath() {
|
|
1039
|
+
const path = [];
|
|
1040
|
+
let curr = this.currentNode;
|
|
1041
|
+
while (curr) {
|
|
1042
|
+
path.unshift(curr.conversation);
|
|
1043
|
+
curr = curr.parent;
|
|
1044
|
+
}
|
|
1045
|
+
return path;
|
|
1046
|
+
}
|
|
1047
|
+
getMessages(options) {
|
|
1048
|
+
return getConversationMessages(this.current, options);
|
|
1049
|
+
}
|
|
1050
|
+
getMessageAtPosition(position) {
|
|
1051
|
+
return getMessageAtPosition(this.current, position);
|
|
1052
|
+
}
|
|
1053
|
+
getMessageByIdentifier(id) {
|
|
1054
|
+
return getMessageByIdentifier(this.current, id);
|
|
1055
|
+
}
|
|
1056
|
+
searchMessages(predicate) {
|
|
1057
|
+
return searchConversationMessages(this.current, predicate);
|
|
1058
|
+
}
|
|
1059
|
+
getStatistics() {
|
|
1060
|
+
return computeConversationStatistics(this.current);
|
|
1061
|
+
}
|
|
1062
|
+
hasSystemMessage() {
|
|
1063
|
+
return hasSystemMessage(this.current);
|
|
1064
|
+
}
|
|
1065
|
+
getFirstSystemMessage() {
|
|
1066
|
+
return getFirstSystemMessage(this.current);
|
|
1067
|
+
}
|
|
1068
|
+
getSystemMessages() {
|
|
1069
|
+
return getSystemMessages(this.current);
|
|
1070
|
+
}
|
|
1071
|
+
serialize() {
|
|
1072
|
+
return serializeConversation(this.current);
|
|
1073
|
+
}
|
|
1074
|
+
toChatMessages() {
|
|
1075
|
+
return toChatMessages(this.current);
|
|
1076
|
+
}
|
|
1077
|
+
estimateTokens(estimator) {
|
|
1078
|
+
return estimateConversationTokens(this.current, estimator, this.env);
|
|
1079
|
+
}
|
|
1080
|
+
getRecentMessages(count, options) {
|
|
1081
|
+
return getRecentMessages(this.current, count, options);
|
|
1082
|
+
}
|
|
1083
|
+
getStreamingMessage() {
|
|
1084
|
+
return getStreamingMessage(this.current);
|
|
1085
|
+
}
|
|
1086
|
+
appendMessages(...inputs) {
|
|
1087
|
+
this.push(appendMessages(this.current, ...inputs, this.env));
|
|
1088
|
+
}
|
|
1089
|
+
appendUserMessage(content, metadata) {
|
|
1090
|
+
this.push(appendUserMessage(this.current, content, metadata, this.env));
|
|
1091
|
+
}
|
|
1092
|
+
appendAssistantMessage(content, metadata) {
|
|
1093
|
+
this.push(appendAssistantMessage(this.current, content, metadata, this.env));
|
|
1094
|
+
}
|
|
1095
|
+
appendSystemMessage(content, metadata) {
|
|
1096
|
+
this.push(appendSystemMessage(this.current, content, metadata, this.env));
|
|
1097
|
+
}
|
|
1098
|
+
prependSystemMessage(content, metadata) {
|
|
1099
|
+
this.push(prependSystemMessage(this.current, content, metadata, this.env));
|
|
1100
|
+
}
|
|
1101
|
+
replaceSystemMessage(content, metadata) {
|
|
1102
|
+
this.push(replaceSystemMessage(this.current, content, metadata, this.env));
|
|
1103
|
+
}
|
|
1104
|
+
collapseSystemMessages() {
|
|
1105
|
+
this.push(collapseSystemMessages(this.current, this.env));
|
|
1106
|
+
}
|
|
1107
|
+
redactMessageAtPosition(position, placeholder) {
|
|
1108
|
+
this.push(redactMessageAtPosition(this.current, position, placeholder, this.env));
|
|
1109
|
+
}
|
|
1110
|
+
truncateFromPosition(position, options) {
|
|
1111
|
+
this.push(truncateFromPosition(this.current, position, options, this.env));
|
|
1112
|
+
}
|
|
1113
|
+
truncateToTokenLimit(maxTokens, options) {
|
|
1114
|
+
this.push(truncateToTokenLimit(this.current, maxTokens, options, this.env));
|
|
1115
|
+
}
|
|
1116
|
+
appendStreamingMessage(role, metadata) {
|
|
1117
|
+
const { conversation, messageId } = appendStreamingMessage(this.current, role, metadata, this.env);
|
|
1118
|
+
this.push(conversation);
|
|
1119
|
+
return messageId;
|
|
1120
|
+
}
|
|
1121
|
+
updateStreamingMessage(messageId, content) {
|
|
1122
|
+
this.push(updateStreamingMessage(this.current, messageId, content, this.env));
|
|
1123
|
+
}
|
|
1124
|
+
finalizeStreamingMessage(messageId, options) {
|
|
1125
|
+
this.push(finalizeStreamingMessage(this.current, messageId, options, this.env));
|
|
1126
|
+
}
|
|
1127
|
+
cancelStreamingMessage(messageId) {
|
|
1128
|
+
this.push(cancelStreamingMessage(this.current, messageId, this.env));
|
|
1129
|
+
}
|
|
1130
|
+
toJSON() {
|
|
1131
|
+
const getPath = (node) => {
|
|
1132
|
+
const path = [];
|
|
1133
|
+
let curr = node;
|
|
1134
|
+
while (curr.parent) {
|
|
1135
|
+
path.unshift(curr.parent.children.indexOf(curr));
|
|
1136
|
+
curr = curr.parent;
|
|
1137
|
+
}
|
|
1138
|
+
return path;
|
|
1139
|
+
};
|
|
1140
|
+
const serializeNode = (node) => ({
|
|
1141
|
+
conversation: serializeConversation(node.conversation),
|
|
1142
|
+
children: node.children.map(serializeNode)
|
|
1143
|
+
});
|
|
1144
|
+
let root = this.currentNode;
|
|
1145
|
+
while (root.parent) {
|
|
1146
|
+
root = root.parent;
|
|
1147
|
+
}
|
|
1148
|
+
return {
|
|
1149
|
+
root: serializeNode(root),
|
|
1150
|
+
currentPath: getPath(this.currentNode)
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
static from(json, environment) {
|
|
1154
|
+
const rootConv = deserializeConversation(json.root.conversation);
|
|
1155
|
+
const history = new ConversationHistory(rootConv, environment);
|
|
1156
|
+
const buildTree = (nodeJSON, parentNode) => {
|
|
1157
|
+
const nodeConv = deserializeConversation(nodeJSON.conversation);
|
|
1158
|
+
const node = {
|
|
1159
|
+
conversation: nodeConv,
|
|
1160
|
+
parent: parentNode,
|
|
1161
|
+
children: []
|
|
1162
|
+
};
|
|
1163
|
+
node.children = nodeJSON.children.map((child) => buildTree(child, node));
|
|
1164
|
+
return node;
|
|
1165
|
+
};
|
|
1166
|
+
const h = history;
|
|
1167
|
+
const rootNode = h.currentNode;
|
|
1168
|
+
rootNode.children = json.root.children.map((child) => buildTree(child, rootNode));
|
|
1169
|
+
let current = rootNode;
|
|
1170
|
+
for (const index of json.currentPath) {
|
|
1171
|
+
const target = current.children[index];
|
|
1172
|
+
if (target) {
|
|
1173
|
+
current = target;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
h.currentNode = current;
|
|
1177
|
+
return history;
|
|
1178
|
+
}
|
|
1179
|
+
bind(fn) {
|
|
1180
|
+
return bindToConversationHistory(this, fn);
|
|
1181
|
+
}
|
|
1182
|
+
[Symbol.dispose]() {
|
|
1183
|
+
let root = this.currentNode;
|
|
1184
|
+
while (root?.parent) {
|
|
1185
|
+
root = root.parent;
|
|
1186
|
+
}
|
|
1187
|
+
const clearNode = (node) => {
|
|
1188
|
+
for (const child of node.children) {
|
|
1189
|
+
clearNode(child);
|
|
1190
|
+
}
|
|
1191
|
+
node.children = [];
|
|
1192
|
+
const n = node;
|
|
1193
|
+
n.parent = null;
|
|
1194
|
+
n.conversation = null;
|
|
1195
|
+
};
|
|
1196
|
+
if (root)
|
|
1197
|
+
clearNode(root);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
function bindToConversationHistory(history, fn) {
|
|
1201
|
+
return (...args) => {
|
|
1202
|
+
const boundFn = fn;
|
|
1203
|
+
const result = boundFn(history.current, ...args, history.env);
|
|
1204
|
+
if (isConversation(result)) {
|
|
1205
|
+
history.push(result);
|
|
1206
|
+
}
|
|
1207
|
+
return result;
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
function isConversation(value) {
|
|
1211
|
+
return value !== null && typeof value === "object" && typeof value.id === "string" && typeof value.status === "string" && value.metadata !== null && typeof value.metadata === "object" && Array.isArray(value.tags) && Array.isArray(value.messages) && typeof value.createdAt === "string" && typeof value.updatedAt === "string";
|
|
1212
|
+
}
|
|
896
1213
|
export {
|
|
1214
|
+
withEnvironment,
|
|
897
1215
|
withConversation,
|
|
898
1216
|
updateStreamingMessage,
|
|
899
1217
|
truncateToTokenLimit,
|
|
@@ -945,12 +1263,14 @@ export {
|
|
|
945
1263
|
computeConversationStatistics,
|
|
946
1264
|
collapseSystemMessages,
|
|
947
1265
|
cancelStreamingMessage,
|
|
1266
|
+
bindToConversationHistory,
|
|
948
1267
|
appendUserMessage,
|
|
949
1268
|
appendSystemMessage,
|
|
950
1269
|
appendStreamingMessage,
|
|
951
1270
|
appendMessages,
|
|
952
1271
|
appendAssistantMessage,
|
|
953
|
-
ConversationalistError
|
|
1272
|
+
ConversationalistError,
|
|
1273
|
+
ConversationHistory
|
|
954
1274
|
};
|
|
955
1275
|
|
|
956
|
-
//# debugId=
|
|
1276
|
+
//# debugId=A4607F063A1411CC64756E2164756E21
|